App Data Manager
A comprehensive TypeScript library that provides a hierarchical set of data manager classes for handling CRUD operations and paginated data retrieval 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.
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
- 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
Installation
npm install @ticatec/app-data-manager
Architecture Overview
The library is built on a hierarchical structure with the following core classes:
BaseDataManager (Abstract)
├── FullListDataManager
└── CommonPagedDataManager (Abstract)
├── PagedDataManager
└── StackDataManager
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
- Data transformation support via
convert
function - Equality checking via
checkEqual
function - 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();
await manager.loadData(); // Uses tagData as filter conditions
console.log(manager.list); // Filtered category list
3. CommonPagedDataManager
Abstract base class for paginated data management with comprehensive pagination support.
Constructor:
protected constructor(
service: T,
keyField: string | CheckEqual,
options: any = null
)
Key Features:
- Pagination state management (pageNo, pageCount, totalCount)
- Query criteria handling with tagData-based initialization
- Configurable pagination parameters
- Abstract
processDataResult
method for custom data processing
Static Configuration:
CommonPagedDataManager.setRowsPerPage(25)
- Set default rows per pageCommonPagedDataManager.setRowsKey('rows')
- Set rows parameter nameCommonPagedDataManager.setPageNoKey('page')
- Set page parameter name
Properties:
criteria: any
- Current query criteria (initialized from options.tagData or empty object)count: number
- Total record count
Methods:
search(criteria: any): Promise<void>
- Search with criteriasetPageNo(pageNo: number): Promise<void>
- Navigate to specific pagesetRowsPage(rows: number): Promise<void>
- Change page sizerefresh(): Promise<void>
- Refresh current dataresetSearch(): 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: any = 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
}
);
}
}
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
- Automatic duplicate prevention using
union
method loadMore()
andhasMore()
methods for progressive loading
Additional Methods:
loadMore(): Promise<void>
- Load next page and append to existing datahasMore(): boolean
- Check if more pages are available
Data Processing:
- Merges new data with existing dataset
- Prevents duplicates using
checkEqual
function
Constructor:
constructor(
service: T,
keyField: string | CheckEqual,
options: any = 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
await manager.search({ category: 'science', featured: true });
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 numberpageCount: 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 criteriasetRowsPage(rows: number): Promise<void>
- Change page sizesetPageNo(value: number): Promise<void>
- Navigate to specific page
Implementation:
PagedDataManager
implements 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`);
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 during save/load operations.
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
}
tagData Usage:
- For paginated managers: Used as default query criteria for searches
- For FullListDataManager: Used as filter conditions for the getList method
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
FullListDataManager
for small, static datasets - Use
PagedDataManager
for traditional paginated tables - Use
StackDataManager
for infinite scroll or feed-like interfaces
- 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() }) };
Dependencies
- @ticatec/app-data-service - Data service interfaces and base implementations
- @ticatec/enhanced-utils - Utility functions including array extensions
Browser Support
- Chrome/Edge 88+
- Firefox 85+
- Safari 14+
- Node.js 14+
Contributing
Issues and pull requests are welcome. Please ensure:
- All tests pass
- 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.