@ticatec/app-data-manager
v3.0.0
Published
A comprehensive TypeScript library providing hierarchical data manager classes for CRUD operations, pagination, and data management in frontend applications. Features include full list, paged, and stackable data managers with built-in caching and transfor
Downloads
520
Readme
App Data Manager
Current version: 2.0.0 | A comprehensive TypeScript library that provides a hierarchical set of data manager classes for handling CRUD operations, paginated data retrieval, and tree-structured data via HTTP requests. This library simplifies data management in frontend applications by abstracting the complexity of data operations and providing a structured approach to managing different types of datasets — flat lists, paginated lists, infinite-scroll feeds, and hierarchical trees.
Note: This is a pure ESM (ES Module) package. Only
importsyntax is supported. The compiled output uses explicit.jsextensions on every relative import, so it loads directly under Node's native ESM loader — no bundler required, even for SSR.
Features
- Type-safe: Full TypeScript support with comprehensive type definitions
- Hierarchical Architecture: Well-structured inheritance hierarchy for different data management needs
- Pagination Support: Built-in pagination handling with both standard and stackable pagination
- Tree Support: Dedicated
TreeDataManagerfor hierarchical data, with eager or lazy (on-demand) loading - CRUD Operations: Complete Create, Read, Update, Delete functionality
- Data Transformation: Built-in data conversion and validation support
- Extensible: Easy to extend and customize for specific use cases
- Dependency Injection: Service-based architecture for better testability
- Zero Global Side Effects: No
Array.prototypepolyfills or other global mutations — safe for long-running SSR processes
Installation
npm install @ticatec/app-data-managerTreeDataManager additionally depends on @ticatec/hierarchy-data (listed as a peer dependency). It's only required if you actually use TreeDataManager.
Quick Start
import { PagedDataManager } from '@ticatec/app-data-manager';
import { PagingDataService } from '@ticatec/app-data-service';
// 1. Create a data service
class UserService extends PagingDataService {
constructor() {
super('/api/users');
}
}
// 2. Create a data manager
const userManager = new PagedDataManager(new UserService(), 'id', {
tagData: { status: 'active' } // Default filter criteria
});
// 3. Load data
await userManager.search({ role: 'admin' });
console.log(`Found ${userManager.count} users`);
console.log(`Page ${userManager.pageNo} of ${userManager.pageCount}`);
console.log(userManager.list); // Current page users🤖 AI-Assisted Development
Accelerate your development with AI-powered code generation!
This library includes comprehensive AI coding assistant prompts optimized for Claude Code and other AI assistants. These prompts help you generate correct, consistent code faster.
📁 Available Prompts
prompts/AI_CODING_ASSISTANT_EN.md- Complete guide in Englishprompts/AI_CODING_ASSISTANT_CN.md- 完整指南(中文)prompts/CLAUDE_CODE_GUIDE.md- Quick start guideprompts/QUICK_REFERENCE.md- Decision trees and cheat sheets
🚀 Quick Start with AI
For Claude Code users:
# In your Claude Code conversation, simply reference the prompt:
Follow prompts/AI_CODING_ASSISTANT_EN.md
Create a user management feature with pagination, search, and CRUD operationsClaude Code will automatically:
- ✅ Analyze your existing codebase patterns
- ✅ Generate services and managers following best practices
- ✅ Match your coding style and conventions
- ✅ Place files in correct locations
- ✅ Handle imports and exports
💡 Example Usage
# 1. Create a new feature
Follow prompts/AI_CODING_ASSISTANT_EN.md
Create product management with infinite scroll
# 2. Refactor existing code
Review all managers and apply best practices from the prompt
# 3. Add features
Add bulk delete to OrderManager following existing patterns
# 4. Understand patterns
Explain when to use PagedDataManager vs StackDataManager vs TreeDataManager🎯 Benefits
- Faster Development: Generate complete features in seconds
- Consistency: Maintain patterns across your codebase
- Best Practices: Built-in knowledge of anti-patterns and optimization tips
- Context-Aware: AI reads your existing code and matches your style
- Error Prevention: Common mistakes are highlighted and avoided
📚 What's Included
The AI prompts cover:
- 🏗️ Architecture overview - Two-layer design (Service + Manager)
- 📋 Decision trees - Choose the right class for your needs
- 🏃 Code patterns - 6 common patterns with examples
- ✅ Best practices - Performance, error handling, type safety
- ⚠️ Common mistakes - Anti-patterns and how to avoid them
- 🎓 Advanced patterns - Custom endpoints, data transformation
- 🔍 Quick reference - Decision trees and cheat sheets
🌐 Language Support
# English projects
Follow prompts/AI_CODING_ASSISTANT_EN.md
# 中文项目
遵循 prompts/AI_CODING_ASSISTANT_CN.md 的指导📖 Learn More
Architecture Overview
The library is built on a hierarchical structure with the following core classes:
BaseDataManager (Abstract)
├── FullListDataManager
└── CommonPagedDataManager (Abstract)
├── PagedDataManager
└── StackDataManager
TreeDataManager (standalone)TreeDataManager deliberately does not extend BaseDataManager. BaseDataManager is built around a flat array (list) as its core storage; a tree's local cache is fundamentally a HierarchyTree (from @ticatec/hierarchy-data) — a completely different storage shape. Forcing it into the same inheritance chain would only muddy the list concept, so it's a sibling class that follows the same construction/error-handling conventions instead.
Core Classes
1. BaseDataManager
Abstract base class for managing data collections with common CRUD operations.
Key Features:
- Data collection management with local caching
- CRUD operations with automatic local synchronization
remove()matches items by primary key (viacheckEqual), not object reference — removal still works even if the caller holds a different object instance with the same key- Data transformation support via
convertfunction, applied uniformly on both save and load paths - Equality checking via
checkEqualfunction - Service-based architecture for data operations
Properties:
service: CommonDataService- Data service instancecheckEqual: CheckEqual- Function to compare data itemsconvert?: DataConvert- Optional data transformation functionlist: Array<any>- Current dataset (read-only copy)
Methods:
save(data: any, isNew: boolean): Promise<void>- Save/update dataremove(item: any): Promise<void>- Delete dataappend(item: any): void- Add item to beginning of listreplace(item: any): void- Replace existing item with matching keyremoveItem(item: any): void- Remove item from local collection only
2. FullListDataManager
Concrete class inheriting from BaseDataManager for managing complete datasets.
Use Cases:
- Dropdown lists
- Static reference data
- Small to medium-sized complete datasets
- Configuration lists
Constructor:
protected constructor(
service: T,
keyField: string | CheckEqual,
options: ManagerOptions = null
)Additional Methods:
loadData(): Promise<void>- Load complete dataset from service using tagData as filter conditions
Example:
import { FullListDataManager } from '@ticatec/app-data-manager';
import { MyFullListDataService } from './services';
class CategoryManager extends FullListDataManager<MyFullListDataService> {
constructor() {
super(
new MyFullListDataService(),
'id',
{
tagData: { status: 'active', type: 'public' } // filter conditions for getList
}
);
}
}
const manager = new CategoryManager();
console.log(manager.list); // [] — safe to read before loadData() resolves
await manager.loadData(); // Uses tagData as filter conditions
console.log(manager.list); // Filtered category list3. CommonPagedDataManager
Abstract base class for paginated data management with comprehensive pagination support.
Constructor:
protected constructor(
service: T,
keyField: string | CheckEqual,
options: PagedManagerOptions = null
)Key Features:
- Pagination state management (pageNo, pageCount, totalCount)
- Query criteria handling with tagData-based initialization;
search()/refresh()never mutate the criteria object you pass in, and thecriteriagetter always returns a clone - Configurable pagination parameters, either globally (static setters) or per instance (constructor options)
- A request-sequence guard: if two searches are in flight and the older one resolves after the newer one, its response is discarded instead of overwriting fresher data
refresh()reloads the current page, it doesn't jump back to page 1- Abstract
processDataResultmethod for custom data processing
Static Configuration (global defaults):
CommonPagedDataManager.setPageSize(25)/setRowsPerPage(25)- Set default rows per pageCommonPagedDataManager.setPageSizeKey('pageSize')/setRowsKey('pageSize')- Set page size parameter name (default is'pageSize')CommonPagedDataManager.setPageNoKey('pageNo')- Set page number parameter name (default is'pageNo')
Instance-level Configuration (PagedManagerOptions, overrides the global defaults above for a single manager):
interface PagedManagerOptions extends ManagerOptions {
pageSizeKey?: string; // property name for page size, falls back to the global default ('pageSize')
rowsKey?: string; // alias for pageSizeKey
pageNoKey?: string; // property name for page number, falls back to the global default ('pageNo')
pageSize?: number; // page size for this instance, falls back to the global default (25)
rowsPerPage?: number; // alias for pageSize
}This lets different managers in the same app talk to APIs with different pagination protocols, without needing to flip the global default back and forth.
Properties:
criteria: any- Current query criteria (a clone; mutating the returned object does not affect internal state)count: number- Total record count
Methods:
search(criteria: any): Promise<void>- Search with criteriasetPageNo(pageNo: number): Promise<void>- Navigate to specific pagesetPageSize(size: number): Promise<void>- Change page sizesetRowsPage(rows: number): Promise<void>- Change page size (alias forsetPageSize)refresh(): Promise<void>- Refresh current pageresetSearch(): Promise<void>- Reset to default criteria
4. PagedDataManager
Concrete implementation of CommonPagedDataManager for standard pagination.
Use Cases:
- Data tables with pagination
- Search results with page navigation
- Standard paginated lists
Additional Properties:
pageCount: number- Total number of pagespageNo: number- Current page number
Data Processing:
- Replaces entire dataset with new page data
- Simple and straightforward pagination
Constructor:
constructor(
service: T,
keyField: string | CheckEqual,
options: PagedManagerOptions = null
)Example:
import { PagedDataManager } from '@ticatec/app-data-manager';
import { MyPagingDataService } from './services';
class UserManager extends PagedDataManager<MyPagingDataService> {
constructor() {
super(
new MyPagingDataService(),
'userId',
{
tagData: { status: 'active' }, // default criteria via tagData
rowsPerPage: 20 // instance-level override, doesn't touch the global default
}
);
}
}
const manager = new UserManager();
// Will search with default criteria from tagData { status: 'active' }
await manager.resetSearch();
console.log(`Page ${manager.pageNo} of ${manager.pageCount}`);
console.log(`Total users: ${manager.count}`);
console.log(manager.list); // Current page users
// Navigate to next page
await manager.setPageNo(2);
// Search with additional criteria
await manager.search({ status: 'active', role: 'admin' });5. StackDataManager
Concrete implementation of CommonPagedDataManager for stackable pagination ("infinite scroll").
Use Cases:
- Social media feeds
- Infinite scroll implementations
- "Load More" functionality
- Progressive data loading
Key Features:
- Accumulates data across pages, but only when advancing via
loadMore() search(),resetSearch(),refresh(), andsetRowsPage()all replace the list instead of merging into it — so switching search criteria (or refreshing) never mixes results from two different queries- Duplicate prevention by primary key when merging (
checkEqual)
Additional Methods:
loadMore(): Promise<void>- Load next page and append to existing datahasMore(): boolean- Check if more pages are available
Constructor:
constructor(
service: T,
keyField: string | CheckEqual,
options: PagedManagerOptions = null
)Example:
import { StackDataManager } from '@ticatec/app-data-manager';
import { MyPagingDataService } from './services';
class FeedManager extends StackDataManager<MyPagingDataService> {
constructor() {
super(
new MyPagingDataService(),
'postId',
{
tagData: { category: 'technology' } // default criteria via tagData
}
);
}
}
const manager = new FeedManager();
// Will search with default criteria from tagData { category: 'technology' }
await manager.resetSearch();
// Load more content
while (manager.hasMore()) {
await manager.loadMore();
console.log(`Loaded ${manager.list.length} posts`);
}
// Search with different criteria — replaces the list, does not merge with the old results
await manager.search({ category: 'science', featured: true });6. TreeDataManager
Concrete class for managing hierarchical/tree-structured data. Wraps a HierarchyTree (a pure, synchronous, in-memory tree data structure) together with a FullListDataService — reusing that service's getList(params) to express both "fetch everything" (eager mode) and "fetch the children of node X" (lazy mode), so no dedicated tree service interface is needed.
Use Cases:
- Tree views (org charts, category trees, file explorers) with add/edit/delete/move
- Tree data grids with expandable rows, column-header sorting
- Any parent/child dataset where you need lookup-by-key, ancestor/descendant traversal, or lazy loading of large subtrees
Constructor:
constructor(service: T, options: TreeManagerOptions<TEntity>)
// T extends FullListDataServiceTreeManagerOptions:
interface TreeManagerOptions<T = any> {
keyField: string | ((item: T) => string | number);
parentKeyField: string | ((item: T) => string | number | null | undefined);
setParentKey?: (item: T, newParentKey: string | number | null | undefined) => T; // required for moveNode() when parentKeyField is a function; auto-derived when it's a string
convert?: DataConvert;
tagData?: any;
onError?: ErrorHandler;
compare?: (a: T, b: T) => number; // sibling sort comparator
maxDepth?: number; // 1-1000, default 1000
duplicateKeyStrategy?: 'throw' | 'replace' | 'warn'; // default 'throw'
cycleStrategy?: 'as-root' | 'throw'; // default 'as-root'
onDiagnostic?: (event: HierarchyDiagnosticEvent) => void;
loadMode?: 'eager' | 'lazy'; // default 'eager'
childrenParam?: string; // query param name for "children of X" in lazy mode, default 'parentId'
isBranch?: (item: T) => boolean; // hint for unloaded nodes in lazy mode; defaults to "assume it might be a branch"
}Eager mode (load the whole tree up front):
import { TreeDataManager } from '@ticatec/app-data-manager';
import { MyFullListDataService } from './services';
const treeManager = new TreeDataManager(new MyFullListDataService(), {
keyField: 'id',
parentKeyField: 'parentId',
});
await treeManager.loadData();
console.log(treeManager.roots); // top-level nodes
console.log(treeManager.roots[0].children); // nested children, already resolvedLazy mode (fetch children on demand as the user expands nodes):
const treeManager = new TreeDataManager(new MyFullListDataService(), {
keyField: 'id',
parentKeyField: 'parentId',
loadMode: 'lazy',
childrenParam: 'parentId',
isBranch: (item) => item.hasChildren, // optional hint from the backend, avoids a wasted request for known leaves
});
await treeManager.loadData(); // fetches only root-level nodes
// when the user expands a node in the UI:
await treeManager.loadChildren(nodeOrKey); // idempotent — a second call is a no-op unless force=true
treeManager.isBranch(nodeOrKey); // true/false, usable before or after loadingCRUD and move:
await treeManager.addNode({ name: 'New Category' }, parentKey);
await treeManager.updateNode({ id: 5, name: 'Renamed' });
await treeManager.removeNode(5); // removes the node and every descendant, on the server and locally
await treeManager.moveNode(5, newParentKey); // move a node (or its whole branch) elsewhere
treeManager.sort((a, b) => a.name.localeCompare(b.name)); // re-sort every level, e.g. for a grid column-header click
moveNode()consistency note: it persists first (service.save()), then commits the move locally (tree.move()) only on success. Ifservice.save()fails, the local tree is 100% untouched. Ifservice.save()succeeds buttree.move()itself then fails (maxDepthexceeded, a cycle withcycleStrategy: 'throw') —HierarchyTree.move()is transactional, so the local tree still stays exactly as it was — but the backend has already been updated by that point. That's a short-lived backend/local divergence, and the only thing that actually corrects it is callingloadData()again (a full rebuild). Don't callloadChildren(node, true)expecting it to fix this:loadChildren()is purely additive — it only appends keys the tree doesn't already have, and the moved node is already present locally (just at its old position), so it gets silently skipped under both the old and new parent. Treat amoveNode()failure as a signal to reload vialoadData(), not as proof the local tree matches the backend. (Note: in lazy mode,loadData()only re-fetches root-level items and clears previously-loaded subtrees, so it's a hard reset — the UI typically needs to reset its expanded state too, so affected branches get re-fetched vialoadChildren()as the user re-expands them.)
Methods:
loadData(): Promise<void>- Build/reload the tree (full dataset in eager mode, roots only in lazy mode)loadChildren(nodeOrKey, force?= false): Promise<void>- Fetch a node's children on demand (lazy mode only; no-op in eager mode)isBranch(nodeOrKey): boolean- Whether a node should show as expandable, using real structure once loaded and theisBranchpredicate before thatisChildrenLoaded(nodeOrKey): boolean- Whether a node's children have actually been fetched (alwaystruein eager mode)addNode(data, parentKey?): Promise<IHierarchyData | undefined>updateNode(data): Promise<IHierarchyData | undefined>- Updates data only, does not change parent/child relationshipsremoveNode(itemOrKey): Promise<boolean>- Removes the node and all descendantsmoveNode(targetKey, newParentKey?, index?): Promise<boolean>- See the consistency note abovesort(comparator?): void/sortSiblings(parentOrKey?): voidroots,find(key),walk(cb),map(cb),filter(predicate),getVisibleList(expandedKeys),clear()
Nodes returned by TreeDataManager are IHierarchyData<T> instances from @ticatec/hierarchy-data — a read-only view with .data, .parent, .children, .level, .isLeaf, plus traversal helpers (.walk, .map, .filter, .descendants(), .ancestors(), .path(), .siblings()). See the @ticatec/hierarchy-data docs for the full node API.
Interface Definitions
IPagedDataManager
Interface defining the contract for paginated data management operations.
Properties:
list: Array<any>- Current dataset (read-only)count: number- Total record countpageNo: number- Current page numberpageSize: number- Current page sizepageCount: number- Total number of pagescriteria: any- Current query criteria
Methods:
refresh(): Promise<void>- Refresh current dataresetCriteria(): any- Reset search criteria to defaultresetSearch(): Promise<void>- Reset to default criteria and reloadsearch(params: any): Promise<void>- Search with new criteriasetPageSize(size: number): Promise<void>- Change page sizesetRowsPage(rows: number): Promise<void>- Change page size (alias forsetPageSize)setPageNo(value: number): Promise<void>- Navigate to specific page
Implementation:
PagedDataManagerimplements this interface for standard pagination
Example:
import { IPagedDataManager, PagedDataManager } from '@ticatec/app-data-manager';
class UserService extends PagingDataService {
constructor() {
super('/api/users');
}
}
// PagedDataManager implements IPagedDataManager
const userManager: IPagedDataManager = new PagedDataManager(
new UserService(),
'id'
);
await userManager.search({ status: 'active' });
console.log(`Page ${userManager.pageNo} of ${userManager.pageCount}`);
console.log(`Total: ${userManager.count} records`);IFullListDataManager
Interface defining the contract for non-paginated, load-everything-at-once data management.
Properties:
list: Array<any>- Current dataset (read-only)
Methods:
loadData(): Promise<void>- Load the complete dataset from the service
Implementation:
FullListDataManagerimplements this interface
ITreeDataManager
Interface defining the contract for hierarchical data management — tree construction, on-demand child loading, add/update/remove, and move.
Properties:
roots: readonly IHierarchyData<any>[]- Root node list (read-only copy)
Methods:
loadData(): Promise<void>,loadChildren(nodeOrKey, force?): Promise<void>isBranch(nodeOrKey): boolean,isChildrenLoaded(nodeOrKey): booleanaddNode(data, parentKey?),updateNode(data),removeNode(itemOrKey),moveNode(targetKey, newParentKey?, index?)sort(comparator?),sortSiblings(parentOrKey?)find(key),walk(cb),map(cb),filter(predicate),getVisibleList(expandedKeys),clear()
Implementation:
TreeDataManagerimplements this interface
Type Definitions
CheckEqual
type CheckEqual = (e1: any, e2: any) => boolean;Function to determine if two data items are equal (typically by comparing primary keys).
DataConvert
type DataConvert = (item: any, isNew: boolean) => any;Optional function to transform data items, applied uniformly on both save and load paths (query results, full-list loads, and tree loads all get converted, not just save()).
ManagerOptions
interface ManagerOptions {
convert?: DataConvert; // Data transformation function
fromTop?: boolean; // Add new items to top of list (default: true)
tagData?: any; // Default query criteria/filter conditions
onError?: ErrorHandler; // Called on save/remove failure instead of throwing, if provided
}tagData Usage:
- For paginated managers: Used as default query criteria for searches
- For FullListDataManager: Used as filter conditions for the getList method
- For TreeDataManager: Used as extra fixed query criteria sent with every
loadData()/loadChildren()request
ErrorHandler
type ErrorHandler = (error: any, operation: string) => void;Optional callback passed via options.onError. If provided, it's invoked instead of throwing when save()/remove() (and, for TreeDataManager, loadData()/loadChildren()/addNode()/updateNode()/removeNode()/moveNode()) fail. If omitted, the error is rethrown as usual.
Advanced Usage
Custom Data Manager
import { BaseDataManager } from '@ticatec/app-data-manager';
class CustomDataManager extends BaseDataManager<MyDataService> {
constructor(service: MyDataService) {
super(service, 'id', {
convert: (item, isNew) => ({
...item,
timestamp: isNew ? Date.now() : item.timestamp
}),
fromTop: true
});
}
// Custom business logic
async archiveItem(item: any): Promise<void> {
const archived = { ...item, archived: true };
await this.save(archived, false);
}
}Service Implementation Example
import { PagingDataService } from '@ticatec/app-data-service';
class MyPagingService extends PagingDataService {
constructor() {
super('/api/users');
}
}Best Practices
Choose the Right Manager:
- Use
FullListDataManagerfor small, static datasets - Use
PagedDataManagerfor traditional paginated tables - Use
StackDataManagerfor infinite scroll or feed-like interfaces - Use
TreeDataManagerfor hierarchical data — trees, org charts, nested categories
- Use
Implement Proper Equality Checking:
// Good: Use unique identifiers const manager = new PagedDataManager(service, 'id'); // Better: Custom comparison for complex keys const manager = new PagedDataManager(service, (a, b) => a.companyId === b.companyId && a.userId === b.userId );Handle Errors Gracefully:
try { await manager.search({ query: 'user input' }); } catch (error) { console.error('Search failed:', error); // Handle error appropriately }Use Data Conversion for Consistency:
const options = { convert: (item, isNew) => ({ ...item, createdAt: isNew ? new Date().toISOString() : item.createdAt, updatedAt: new Date().toISOString() }) };Treat
moveNode()failures as a reload signal, not silent data loss: since it persists to the backend before committing locally, a failure partway through means the two can briefly disagree — see the consistency note underTreeDataManagerabove.
Dependencies
- @ticatec/app-data-service ^3.0.0 - Data service interfaces and base implementations (peer dependency)
- @ticatec/rest-api >=1.0.0 - RESTful API service (peer dependency)
- @ticatec/hierarchy-data ^0.2.0 - In-memory tree data structure (peer dependency, only needed if you use
TreeDataManager)
This package has no runtime dependencies of its own — it doesn't touch Array.prototype or any other global state, which matters if you're rendering it in a long-running SSR process (no risk of polluting other requests or modules sharing the same Node process).
Performance Optimization Tips
Use appropriate manager for your data size:
FullListDataManager: Best for datasets under 1,000 recordsPagedDataManager: Ideal for large datasets with paginationStackDataManager: Perfect for infinite scroll with frequent updatesTreeDataManager(lazy mode): Best for large trees where loading everything up front would be wasteful
Optimize page size:
// Configure optimal page size based on your data complexity CommonPagedDataManager.setRowsPerPage(50); // Default is 25 // Or scope it to a single manager instead of changing the global default new PagedDataManager(service, 'id', { rowsPerPage: 50 });Use efficient equality checks:
// Fast: Single field comparison const fastManager = new PagedDataManager(service, 'id'); // Slower but necessary: Multi-field comparison const complexManager = new PagedDataManager(service, (a, b) => a.companyId === b.companyId && a.userId === b.userId );Implement proper error boundaries:
try { await manager.search({ query: userKeyword }); } catch (error) { if (error.response?.status === 401) { // Handle authentication error } else if (error.response?.status === 429) { // Handle rate limiting } else { // Handle other errors } }
FAQ
Q: What's the difference between tagData and search criteria?
A: tagData in options sets default/fixed filter criteria that persist across searches, while criteria passed to search() are combined with tagData for that specific search.
Q: How do I clear all data?
A: Call resetSearch() to reset to default criteria and reload (TreeDataManager has clear() instead), or create a new manager instance.
Q: Can I use multiple managers for the same data type?
A: Yes, you can create multiple manager instances with different tagData configurations for the same service.
Q: How do I handle concurrent searches?
A: CommonPagedDataManager (and therefore PagedDataManager/StackDataManager) already guards against this: if an older search resolves after a newer one, its response is discarded instead of overwriting fresher data. You don't need to implement cancellation yourself for typical use.
Q: Is the data cached?
A: Yes, each manager maintains an in-memory cache. Use refresh() (or loadData()/loadChildren(..., true) for TreeDataManager) to reload data from the server.
Q: Should I use TreeDataManager in eager or lazy mode?
A: Eager mode (the default) is simplest and fine for trees with up to a few thousand nodes. Switch to loadMode: 'lazy' once the full tree is too large to fetch and render up front — children are then fetched only as the user expands nodes.
Browser Support
- Chrome/Edge 88+
- Firefox 85+
- Safari 14+
- Node.js 14+ (native ESM, no bundler required)
Contributing
Issues and pull requests are welcome. Please ensure:
npm run buildcompiles without errors- Code follows TypeScript best practices
- Documentation is updated for new features
- Examples are provided for new functionality
License
Copyright © 2023 Ticatec. All rights reserved.
This library is released under the MIT License. See LICENSE file for details.
Contact
- Email: [email protected]
- GitHub: https://github.com/ticatec/web-commons/tree/main/packages/app-data-manager
- Issues: https://github.com/ticatec/web-commons/issues
