http-request-manager 18.7.21 → 18.7.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/http-request-manager.mjs +7633 -0
- package/fesm2022/http-request-manager.mjs.map +1 -0
- package/http-request-manager-18.7.24.tgz +0 -0
- package/package.json +16 -5
- package/types/http-request-manager.d.ts +2277 -0
- package/ARCHITECTURE.md +0 -483
- package/DATABASE_README.md +0 -1176
- package/HTTP_MANAGER_README.md +0 -579
- package/HTTP_SINGNALS_MANAGER_README.md +0 -654
- package/HTTP_STATE_MANAGER_README.md +0 -948
- package/INTERCEPTOR_README.md +0 -549
- package/LOCAL_STORAGE_README.md +0 -1056
- package/STORE_STATE_MANAGER_README.md +0 -1322
- package/UTILS_README.md +0 -1186
- package/WS_MANAGER_README.md +0 -613
- package/ng-package.json +0 -8
- package/src/lib/http-request-manager.module.ts +0 -132
- package/src/lib/http-request-services-demo/database-data-demo/database-data-demo.component.html +0 -65
- package/src/lib/http-request-services-demo/database-data-demo/database-data-demo.component.scss +0 -0
- package/src/lib/http-request-services-demo/database-data-demo/database-data-demo.component.ts +0 -224
- package/src/lib/http-request-services-demo/http-request-services-demo.component.html +0 -114
- package/src/lib/http-request-services-demo/http-request-services-demo.component.scss +0 -6
- package/src/lib/http-request-services-demo/http-request-services-demo.component.ts +0 -52
- package/src/lib/http-request-services-demo/local-storage-demo/local-storage-demo.component.html +0 -195
- package/src/lib/http-request-services-demo/local-storage-demo/local-storage-demo.component.scss +0 -17
- package/src/lib/http-request-services-demo/local-storage-demo/local-storage-demo.component.ts +0 -206
- package/src/lib/http-request-services-demo/local-storage-signals-demo/local-storage-signals-demo.component.html +0 -200
- package/src/lib/http-request-services-demo/local-storage-signals-demo/local-storage-signals-demo.component.scss +0 -17
- package/src/lib/http-request-services-demo/local-storage-signals-demo/local-storage-signals-demo.component.ts +0 -212
- package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/download-file/download-file.component.html +0 -53
- package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/download-file/download-file.component.scss +0 -60
- package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/download-file/download-file.component.ts +0 -72
- package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/file-download.module.ts +0 -28
- package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/file-downloader.component.html +0 -10
- package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/file-downloader.component.scss +0 -29
- package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/file-downloader.component.ts +0 -100
- package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/models/download-labels-model.ts +0 -22
- package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/spinner/spinner.component.html +0 -8
- package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/spinner/spinner.component.scss +0 -19
- package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/spinner/spinner.component.ts +0 -26
- package/src/lib/http-request-services-demo/request-manager-demo/models/app-session.model.ts +0 -30
- package/src/lib/http-request-services-demo/request-manager-demo/models/app.model.ts +0 -19
- package/src/lib/http-request-services-demo/request-manager-demo/models/get-sample.model.ts +0 -25
- package/src/lib/http-request-services-demo/request-manager-demo/models/sample-ai-prompt.ts +0 -19
- package/src/lib/http-request-services-demo/request-manager-demo/models/sample-client-details.ts +0 -24
- package/src/lib/http-request-services-demo/request-manager-demo/models/sample-client-info.ts +0 -30
- package/src/lib/http-request-services-demo/request-manager-demo/models/sample-client.model.ts +0 -49
- package/src/lib/http-request-services-demo/request-manager-demo/models/sample-mapper-client-info.ts +0 -33
- package/src/lib/http-request-services-demo/request-manager-demo/request-manager-demo.component.html +0 -392
- package/src/lib/http-request-services-demo/request-manager-demo/request-manager-demo.component.scss +0 -24
- package/src/lib/http-request-services-demo/request-manager-demo/request-manager-demo.component.ts +0 -461
- package/src/lib/http-request-services-demo/request-manager-state-demo/request-manager-state-demo.component.html +0 -393
- package/src/lib/http-request-services-demo/request-manager-state-demo/request-manager-state-demo.component.scss +0 -24
- package/src/lib/http-request-services-demo/request-manager-state-demo/request-manager-state-demo.component.ts +0 -421
- package/src/lib/http-request-services-demo/request-manager-state-demo/services/state-manager-demo.service.ts +0 -87
- package/src/lib/http-request-services-demo/request-manager-ws-demo/components/services/state-data-request.service.ts +0 -120
- package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-ai-messaging/ws-ai-messaging.component.css +0 -0
- package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-ai-messaging/ws-ai-messaging.component.html +0 -3
- package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-ai-messaging/ws-ai-messaging.component.ts +0 -16
- package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-chats/ws-chats.component.css +0 -0
- package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-chats/ws-chats.component.html +0 -3
- package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-chats/ws-chats.component.ts +0 -16
- package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-data-control/ws-data-control.component.css +0 -31
- package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-data-control/ws-data-control.component.html +0 -72
- package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-data-control/ws-data-control.component.scss +0 -41
- package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-data-control/ws-data-control.component.spec.ts +0 -205
- package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-data-control/ws-data-control.component.ts +0 -77
- package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-messaging/ws-messaging.component.css +0 -11
- package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-messaging/ws-messaging.component.html +0 -96
- package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-messaging/ws-messaging.component.spec.ts +0 -31
- package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-messaging/ws-messaging.component.ts +0 -229
- package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-notifications/ws-notifications.component.css +0 -30
- package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-notifications/ws-notifications.component.html +0 -172
- package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-notifications/ws-notifications.component.spec.ts +0 -31
- package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-notifications/ws-notifications.component.ts +0 -239
- package/src/lib/http-request-services-demo/request-manager-ws-demo/models/oidc-client.model.ts +0 -31
- package/src/lib/http-request-services-demo/request-manager-ws-demo/models/user-data.model.ts +0 -32
- package/src/lib/http-request-services-demo/request-manager-ws-demo/request-manager-ws-demo.component.css +0 -0
- package/src/lib/http-request-services-demo/request-manager-ws-demo/request-manager-ws-demo.component.html +0 -84
- package/src/lib/http-request-services-demo/request-manager-ws-demo/request-manager-ws-demo.component.ts +0 -41
- package/src/lib/http-request-services-demo/request-manager-ws-demo/services/index.ts +0 -3
- package/src/lib/http-request-services-demo/request-manager-ws-demo/services/message-service-demo.service.ts +0 -83
- package/src/lib/http-request-services-demo/request-manager-ws-demo/services/notification-service-demo.service.ts +0 -147
- package/src/lib/http-request-services-demo/request-manager-ws-demo/services/state-service-demo.service.ts +0 -158
- package/src/lib/http-request-services-demo/request-signals-manager-demo/file-downloader/download-file/download-file.component.html +0 -53
- package/src/lib/http-request-services-demo/request-signals-manager-demo/file-downloader/download-file/download-file.component.scss +0 -60
- package/src/lib/http-request-services-demo/request-signals-manager-demo/file-downloader/download-file/download-file.component.ts +0 -72
- package/src/lib/http-request-services-demo/request-signals-manager-demo/file-downloader/file-download.module.ts +0 -28
- package/src/lib/http-request-services-demo/request-signals-manager-demo/file-downloader/file-downloader.component.html +0 -10
- package/src/lib/http-request-services-demo/request-signals-manager-demo/file-downloader/file-downloader.component.scss +0 -29
- package/src/lib/http-request-services-demo/request-signals-manager-demo/file-downloader/file-downloader.component.ts +0 -100
- package/src/lib/http-request-services-demo/request-signals-manager-demo/file-downloader/models/download-labels-model.ts +0 -22
- package/src/lib/http-request-services-demo/request-signals-manager-demo/file-downloader/spinner/spinner.component.html +0 -8
- package/src/lib/http-request-services-demo/request-signals-manager-demo/file-downloader/spinner/spinner.component.scss +0 -19
- package/src/lib/http-request-services-demo/request-signals-manager-demo/file-downloader/spinner/spinner.component.ts +0 -26
- package/src/lib/http-request-services-demo/request-signals-manager-demo/models/app-session.model.ts +0 -30
- package/src/lib/http-request-services-demo/request-signals-manager-demo/models/app.model.ts +0 -19
- package/src/lib/http-request-services-demo/request-signals-manager-demo/models/get-sample.model.ts +0 -25
- package/src/lib/http-request-services-demo/request-signals-manager-demo/models/sample-ai-prompt.ts +0 -19
- package/src/lib/http-request-services-demo/request-signals-manager-demo/models/sample-client-details.ts +0 -24
- package/src/lib/http-request-services-demo/request-signals-manager-demo/models/sample-client-info.ts +0 -30
- package/src/lib/http-request-services-demo/request-signals-manager-demo/models/sample-client.model.ts +0 -49
- package/src/lib/http-request-services-demo/request-signals-manager-demo/models/sample-mapper-client-info.ts +0 -33
- package/src/lib/http-request-services-demo/request-signals-manager-demo/request-signals-manager-demo.component.html +0 -380
- package/src/lib/http-request-services-demo/request-signals-manager-demo/request-signals-manager-demo.component.scss +0 -24
- package/src/lib/http-request-services-demo/request-signals-manager-demo/request-signals-manager-demo.component.ts +0 -410
- package/src/lib/http-request-services-demo/store-state-manager-demo/models/settings.model.ts +0 -28
- package/src/lib/http-request-services-demo/store-state-manager-demo/services/settings-state.service.ts +0 -48
- package/src/lib/http-request-services-demo/store-state-manager-demo/store-state-manager-demo.component.css +0 -0
- package/src/lib/http-request-services-demo/store-state-manager-demo/store-state-manager-demo.component.html +0 -23
- package/src/lib/http-request-services-demo/store-state-manager-demo/store-state-manager-demo.component.ts +0 -36
- package/src/lib/index.ts +0 -3
- package/src/lib/interceptors/credentials.interceptor.ts +0 -16
- package/src/lib/interceptors/index.ts +0 -6
- package/src/lib/interceptors/models/error-settings.model.ts +0 -22
- package/src/lib/interceptors/models/index.ts +0 -2
- package/src/lib/interceptors/proxy-debugger.interceptor.ts +0 -46
- package/src/lib/interceptors/request-error.interceptor.ts +0 -65
- package/src/lib/interceptors/request-header.interceptor.ts +0 -53
- package/src/lib/models/config-http-options.model.ts +0 -42
- package/src/lib/models/config-local-storage-options.model.ts +0 -27
- package/src/lib/models/config-options.model.ts +0 -27
- package/src/lib/models/config-token.model.ts +0 -9
- package/src/lib/models/data-type.enum.ts +0 -5
- package/src/lib/models/database-storage.model.ts +0 -24
- package/src/lib/models/index.ts +0 -12
- package/src/lib/models/retry-options.model.ts +0 -22
- package/src/lib/services/database-manager-service/database.manager.service.ts +0 -262
- package/src/lib/services/database-manager-service/db.storage.service.ts +0 -207
- package/src/lib/services/database-manager-service/index.ts +0 -4
- package/src/lib/services/database-manager-service/models/index.ts +0 -2
- package/src/lib/services/database-manager-service/models/table-schema.ts +0 -33
- package/src/lib/services/index.ts +0 -12
- package/src/lib/services/local-storage-manager-service/index.ts +0 -4
- package/src/lib/services/local-storage-manager-service/local-storage-manager.service.spec.ts +0 -71
- package/src/lib/services/local-storage-manager-service/local-storage-manager.service.ts +0 -426
- package/src/lib/services/local-storage-manager-service/local-storage-signals-manager.service.spec.ts +0 -67
- package/src/lib/services/local-storage-manager-service/local-storage-signals-manager.service.ts +0 -345
- package/src/lib/services/local-storage-manager-service/models/global-store-options.model.ts +0 -30
- package/src/lib/services/local-storage-manager-service/models/index.ts +0 -6
- package/src/lib/services/local-storage-manager-service/models/setting-options.model.ts +0 -35
- package/src/lib/services/local-storage-manager-service/models/storage-data.model.ts +0 -24
- package/src/lib/services/local-storage-manager-service/models/storage-option.model.ts +0 -32
- package/src/lib/services/local-storage-manager-service/models/storage-type.enum.ts +0 -5
- package/src/lib/services/request-manager-services/README.md +0 -268
- package/src/lib/services/request-manager-services/http-manager-signals.service.ts +0 -246
- package/src/lib/services/request-manager-services/http-manager.service.spec.ts +0 -232
- package/src/lib/services/request-manager-services/http-manager.service.ts +0 -274
- package/src/lib/services/request-manager-services/index.ts +0 -8
- package/src/lib/services/request-manager-services/request-signals.service.ts +0 -214
- package/src/lib/services/request-manager-services/request.service.ts +0 -309
- package/src/lib/services/request-manager-services/rxjs-operators/countdown.ts +0 -17
- package/src/lib/services/request-manager-services/rxjs-operators/delay-retry.ts +0 -16
- package/src/lib/services/request-manager-services/rxjs-operators/index.ts +0 -4
- package/src/lib/services/request-manager-services/rxjs-operators/request-polling.ts +0 -35
- package/src/lib/services/request-manager-services/rxjs-operators/request-streaming.ts +0 -436
- package/src/lib/services/request-manager-state-service/http-manager-state.store.ts +0 -1321
- package/src/lib/services/request-manager-state-service/index.ts +0 -3
- package/src/lib/services/request-manager-state-service/models/api-request.model.ts +0 -61
- package/src/lib/services/request-manager-state-service/models/index.ts +0 -6
- package/src/lib/services/request-manager-state-service/models/request-options.model.ts +0 -22
- package/src/lib/services/request-manager-state-service/models/stream-type.enum.ts +0 -13
- package/src/lib/services/request-manager-state-service/models/ws-options.model.ts +0 -39
- package/src/lib/services/store-state-manager-service/index.ts +0 -3
- package/src/lib/services/store-state-manager-service/models/index.ts +0 -2
- package/src/lib/services/store-state-manager-service/models/state-storage-options.model.ts +0 -24
- package/src/lib/services/store-state-manager-service/store-state-manager.service.ts +0 -88
- package/src/lib/services/utils/app.service.spec.ts +0 -25
- package/src/lib/services/utils/app.service.ts +0 -21
- package/src/lib/services/utils/encryption/README.md +0 -79
- package/src/lib/services/utils/encryption/asymmetrical-encryption.service.ts +0 -282
- package/src/lib/services/utils/encryption/encryption-test.service.ts +0 -39
- package/src/lib/services/utils/encryption/index.ts +0 -5
- package/src/lib/services/utils/encryption/random.ts +0 -81
- package/src/lib/services/utils/encryption/symmetrical-encryption.service.ts +0 -93
- package/src/lib/services/utils/headers.service.spec.ts +0 -80
- package/src/lib/services/utils/headers.service.ts +0 -18
- package/src/lib/services/utils/index.ts +0 -7
- package/src/lib/services/utils/object-merger.service.spec.ts +0 -18
- package/src/lib/services/utils/object-merger.service.ts +0 -78
- package/src/lib/services/utils/path-query.service.spec.ts +0 -117
- package/src/lib/services/utils/path-query.service.ts +0 -69
- package/src/lib/services/utils/random-color.utils.ts +0 -83
- package/src/lib/services/utils/utils.service.spec.ts +0 -165
- package/src/lib/services/utils/utils.service.ts +0 -192
- package/src/lib/services/ws-manager-service/index.ts +0 -4
- package/src/lib/services/ws-manager-service/models/channel-info.model.ts +0 -24
- package/src/lib/services/ws-manager-service/models/channel-message-data.model.ts +0 -24
- package/src/lib/services/ws-manager-service/models/channel-message.model.ts +0 -24
- package/src/lib/services/ws-manager-service/models/communication-type.enum.ts +0 -5
- package/src/lib/services/ws-manager-service/models/index.ts +0 -5
- package/src/lib/services/ws-manager-service/models/ws-user.model.ts +0 -38
- package/src/lib/services/ws-manager-service/services/index.ts +0 -3
- package/src/lib/services/ws-manager-service/services/websocket.service.ts +0 -392
- package/src/public-api.ts +0 -14
- package/tsconfig.lib.json +0 -32
- package/tsconfig.lib.prod.json +0 -10
- package/tsconfig.spec.json +0 -14
|
@@ -1,948 +0,0 @@
|
|
|
1
|
-
# HTTP Manager State Service
|
|
2
|
-
|
|
3
|
-
The `HTTPManagerStateService` extends `ComponentStore` to provide robust state management with built-in HTTP request handling and WebSocket integration for CRUD operations.
|
|
4
|
-
|
|
5
|
-
## Overview
|
|
6
|
-
|
|
7
|
-
This service combines HTTP requests with local state management, automatically updating the local store when CRUD operations succeed. It integrates with WebSockets to keep the state synchronized with server-side changes.
|
|
8
|
-
|
|
9
|
-
### Key Features
|
|
10
|
-
|
|
11
|
-
- **ComponentStore Integration** - Extends NGRX ComponentStore for robust state management
|
|
12
|
-
- **Automatic CRUD Updates** - `createRecord`, `updateRecord`, and `deleteRecord` automatically update local state
|
|
13
|
-
- **WebSocket Sync** - Real-time synchronization with server via WebSocket channels
|
|
14
|
-
- **Database Support** - IndexedDB caching integration for offline-first applications
|
|
15
|
-
- **Selectors** - Built-in selectors like `data$` and `selectRecord$(id)` for data access
|
|
16
|
-
- **Pagination** - State tracking for paginated data
|
|
17
|
-
- **Streaming Support** - Real-time data streaming integration
|
|
18
|
-
|
|
19
|
-
## Installation
|
|
20
|
-
|
|
21
|
-
```typescript
|
|
22
|
-
import { HttpRequestManagerModule } from 'http-request-manager';
|
|
23
|
-
|
|
24
|
-
@NgModule({
|
|
25
|
-
imports: [HttpRequestManagerModule.forRoot({})]
|
|
26
|
-
})
|
|
27
|
-
export class AppModule { }
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
## Basic Usage
|
|
31
|
-
|
|
32
|
-
### Creating a State Service
|
|
33
|
-
|
|
34
|
-
```typescript
|
|
35
|
-
import { Injectable } from '@angular/core';
|
|
36
|
-
import { HTTPManagerStateService, ApiRequest, DataType } from 'http-request-manager';
|
|
37
|
-
|
|
38
|
-
interface User {
|
|
39
|
-
id: number;
|
|
40
|
-
name: string;
|
|
41
|
-
email: string;
|
|
42
|
-
active: boolean;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
@Injectable({
|
|
46
|
-
providedIn: 'root'
|
|
47
|
-
})
|
|
48
|
-
export class UserStateService extends HTTPManagerStateService<User> {
|
|
49
|
-
|
|
50
|
-
constructor() {
|
|
51
|
-
super(
|
|
52
|
-
ApiRequest.adapt({
|
|
53
|
-
server: 'http://localhost:8080',
|
|
54
|
-
path: ['users'],
|
|
55
|
-
adapter: User.adapt, // Required for Database Storage
|
|
56
|
-
ws: {
|
|
57
|
-
id: 'USERS123', // Base name - automatically becomes SYS-USERS123
|
|
58
|
-
wsServer: 'ws://localhost:8080',
|
|
59
|
-
retry: { times: 3, delay: 5 }
|
|
60
|
-
}
|
|
61
|
-
}),
|
|
62
|
-
DataType.ARRAY,
|
|
63
|
-
DatabaseStorage.adapt({
|
|
64
|
-
table: 'users-cache',
|
|
65
|
-
expiresIn: '1d' // Cache for 1 day
|
|
66
|
-
})
|
|
67
|
-
);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// Public API methods
|
|
71
|
-
loadUsers() {
|
|
72
|
-
this.fetchRecords();
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
addUser(user: Omit<User, 'id'>) {
|
|
76
|
-
this.createRecord(user);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
updateUser(user: User) {
|
|
80
|
-
this.updateRecord(user);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
deleteUser(id: number) {
|
|
84
|
-
this.deleteRecord(id);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
getUser(id: number) {
|
|
88
|
-
return this.selectRecord$(id);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
### Using in Components
|
|
94
|
-
|
|
95
|
-
```typescript
|
|
96
|
-
import { Component, inject } from '@angular/core';
|
|
97
|
-
import { UserStateService } from './user-state.service';
|
|
98
|
-
|
|
99
|
-
@Component({
|
|
100
|
-
selector: 'app-users',
|
|
101
|
-
template: `
|
|
102
|
-
<div class="user-management">
|
|
103
|
-
<h2>User Management</h2>
|
|
104
|
-
|
|
105
|
-
<!-- Loading State -->
|
|
106
|
-
<div *ngIf="store.isPending$ | async">
|
|
107
|
-
<div class="loading-spinner">Loading users...</div>
|
|
108
|
-
</div>
|
|
109
|
-
|
|
110
|
-
<!-- Error State -->
|
|
111
|
-
<div *ngIf="store.error$ | async as error" class="error">
|
|
112
|
-
Error: {{ error.message }}
|
|
113
|
-
</div>
|
|
114
|
-
|
|
115
|
-
<!-- User List -->
|
|
116
|
-
<div class="user-grid">
|
|
117
|
-
<div *ngFor="let user of store.data$ | async" class="user-card">
|
|
118
|
-
<div class="user-info">
|
|
119
|
-
<h3>{{ user.name }}</h3>
|
|
120
|
-
<p>{{ user.email }}</p>
|
|
121
|
-
<span class="user-status"
|
|
122
|
-
[class.active]="user.active"
|
|
123
|
-
[class.inactive]="!user.active">
|
|
124
|
-
{{ user.active ? 'Active' : 'Inactive' }}
|
|
125
|
-
</span>
|
|
126
|
-
</div>
|
|
127
|
-
|
|
128
|
-
<div class="user-actions">
|
|
129
|
-
<button (click)="store.updateUser({...user, active: !user.active})">
|
|
130
|
-
{{ user.active ? 'Deactivate' : 'Activate' }}
|
|
131
|
-
</button>
|
|
132
|
-
<button (click)="store.deleteUser(user.id)" class="delete">
|
|
133
|
-
Delete
|
|
134
|
-
</button>
|
|
135
|
-
</div>
|
|
136
|
-
</div>
|
|
137
|
-
</div>
|
|
138
|
-
|
|
139
|
-
<!-- Add User Form -->
|
|
140
|
-
<div class="add-user-form">
|
|
141
|
-
<h3>Add New User</h3>
|
|
142
|
-
<form (ngSubmit)="addUser()">
|
|
143
|
-
<input [(ngModel)]="newUser.name" name="name" placeholder="Name" required>
|
|
144
|
-
<input [(ngModel)]="newUser.email" name="email" placeholder="Email" required>
|
|
145
|
-
<label>
|
|
146
|
-
<input type="checkbox" [(ngModel)]="newUser.active" name="active">
|
|
147
|
-
Active
|
|
148
|
-
</label>
|
|
149
|
-
<button type="submit">Add User</button>
|
|
150
|
-
</form>
|
|
151
|
-
</div>
|
|
152
|
-
</div>
|
|
153
|
-
`
|
|
154
|
-
})
|
|
155
|
-
export class UsersComponent {
|
|
156
|
-
store = inject(UserStateService);
|
|
157
|
-
|
|
158
|
-
newUser = {
|
|
159
|
-
name: '',
|
|
160
|
-
email: '',
|
|
161
|
-
active: true
|
|
162
|
-
};
|
|
163
|
-
|
|
164
|
-
ngOnInit() {
|
|
165
|
-
this.store.loadUsers();
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
addUser() {
|
|
169
|
-
if (this.newUser.name && this.newUser.email) {
|
|
170
|
-
this.store.addUser(this.newUser);
|
|
171
|
-
this.newUser = { name: '', email: '', active: true };
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
## API Reference
|
|
178
|
-
|
|
179
|
-
### State Interface
|
|
180
|
-
|
|
181
|
-
The service manages state with the following interface:
|
|
182
|
-
|
|
183
|
-
```typescript
|
|
184
|
-
interface State<T> {
|
|
185
|
-
records: T[]; // Array of entities
|
|
186
|
-
record: T | null; // Single selected entity
|
|
187
|
-
page: number; // Current page
|
|
188
|
-
totalPages: number; // Total pages
|
|
189
|
-
isPending: boolean; // Loading state
|
|
190
|
-
error: any; // Error state
|
|
191
|
-
}
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
### Selectors
|
|
195
|
-
|
|
196
|
-
#### data$: Observable<T[]>
|
|
197
|
-
|
|
198
|
-
Get all records from the state:
|
|
199
|
-
|
|
200
|
-
```typescript
|
|
201
|
-
users$ = this.store.data$;
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
#### record$: Observable<T | null>
|
|
205
|
-
|
|
206
|
-
Get the currently selected record:
|
|
207
|
-
|
|
208
|
-
```typescript
|
|
209
|
-
selectedUser$ = this.store.record$;
|
|
210
|
-
```
|
|
211
|
-
|
|
212
|
-
#### selectRecord$(id: any): Observable<T | undefined>
|
|
213
|
-
|
|
214
|
-
Get a specific record by ID:
|
|
215
|
-
|
|
216
|
-
```typescript
|
|
217
|
-
user$ = this.store.selectRecord$(123);
|
|
218
|
-
```
|
|
219
|
-
|
|
220
|
-
#### isPending$: Observable<boolean>
|
|
221
|
-
|
|
222
|
-
Check if any operation is in progress:
|
|
223
|
-
|
|
224
|
-
```typescript
|
|
225
|
-
loading$ = this.store.isPending$;
|
|
226
|
-
```
|
|
227
|
-
|
|
228
|
-
#### error$: Observable<any>
|
|
229
|
-
|
|
230
|
-
Get the current error state:
|
|
231
|
-
|
|
232
|
-
```typescript
|
|
233
|
-
error$ = this.store.error$;
|
|
234
|
-
```
|
|
235
|
-
|
|
236
|
-
#### page$: Observable<number>
|
|
237
|
-
|
|
238
|
-
Get the current page number:
|
|
239
|
-
|
|
240
|
-
```typescript
|
|
241
|
-
currentPage$ = this.store.page$;
|
|
242
|
-
```
|
|
243
|
-
|
|
244
|
-
#### totalPages$: Observable<number>
|
|
245
|
-
|
|
246
|
-
Get the total number of pages:
|
|
247
|
-
|
|
248
|
-
```typescript
|
|
249
|
-
totalPages$ = this.store.totalPages$;
|
|
250
|
-
```
|
|
251
|
-
|
|
252
|
-
### CRUD Methods
|
|
253
|
-
|
|
254
|
-
#### fetchRecords(params?: any)
|
|
255
|
-
|
|
256
|
-
Fetch records from the API:
|
|
257
|
-
|
|
258
|
-
```typescript
|
|
259
|
-
// Basic fetch
|
|
260
|
-
this.store.fetchRecords();
|
|
261
|
-
|
|
262
|
-
// Fetch with parameters
|
|
263
|
-
this.store.fetchRecords({ page: 1, limit: 10, search: 'john' });
|
|
264
|
-
```
|
|
265
|
-
|
|
266
|
-
#### createRecord(record: T)
|
|
267
|
-
|
|
268
|
-
Create a new record and update state:
|
|
269
|
-
|
|
270
|
-
```typescript
|
|
271
|
-
this.store.createRecord({
|
|
272
|
-
name: 'John Doe',
|
|
273
|
-
email: 'john@example.com',
|
|
274
|
-
active: true
|
|
275
|
-
});
|
|
276
|
-
```
|
|
277
|
-
|
|
278
|
-
#### updateRecord(record: T)
|
|
279
|
-
|
|
280
|
-
Update an existing record and update state:
|
|
281
|
-
|
|
282
|
-
```typescript
|
|
283
|
-
this.store.updateRecord({
|
|
284
|
-
id: 123,
|
|
285
|
-
name: 'John Smith',
|
|
286
|
-
email: 'john.smith@example.com',
|
|
287
|
-
active: true
|
|
288
|
-
});
|
|
289
|
-
```
|
|
290
|
-
|
|
291
|
-
#### deleteRecord(id: any)
|
|
292
|
-
|
|
293
|
-
Delete a record and update state:
|
|
294
|
-
|
|
295
|
-
```typescript
|
|
296
|
-
this.store.deleteRecord(123);
|
|
297
|
-
```
|
|
298
|
-
|
|
299
|
-
#### setPage(page: number)
|
|
300
|
-
|
|
301
|
-
Set the current page:
|
|
302
|
-
|
|
303
|
-
```typescript
|
|
304
|
-
this.store.setPage(2);
|
|
305
|
-
```
|
|
306
|
-
|
|
307
|
-
#### setTotalPages(total: number)
|
|
308
|
-
|
|
309
|
-
Set the total number of pages:
|
|
310
|
-
|
|
311
|
-
```typescript
|
|
312
|
-
this.store.setTotalPages(10);
|
|
313
|
-
```
|
|
314
|
-
|
|
315
|
-
## WebSocket Integration
|
|
316
|
-
|
|
317
|
-
The HTTPManagerStateService automatically handles WebSocket connections for real-time state synchronization.
|
|
318
|
-
|
|
319
|
-
### Channel Types
|
|
320
|
-
|
|
321
|
-
The service uses three types of WebSocket channels:
|
|
322
|
-
|
|
323
|
-
| Channel Type | Prefix | Description | Purpose |
|
|
324
|
-
|--------------|--------|-------------|---------|
|
|
325
|
-
| **STATE** | `SYS-` | State synchronization | CRUD operations sync |
|
|
326
|
-
| **MESSAGE** | `MES-` | Messaging channels | Real-time messaging |
|
|
327
|
-
| **NOTIFICATION** | `PUB-` | Notification channels | System notifications |
|
|
328
|
-
|
|
329
|
-
### Configuration
|
|
330
|
-
|
|
331
|
-
```typescript
|
|
332
|
-
constructor() {
|
|
333
|
-
super(
|
|
334
|
-
ApiRequest.adapt({
|
|
335
|
-
server: 'http://localhost:8080',
|
|
336
|
-
path: ['users'],
|
|
337
|
-
ws: {
|
|
338
|
-
id: 'USERS123', // Base name becomes SYS-USERS123
|
|
339
|
-
wsServer: 'ws://localhost:8080',
|
|
340
|
-
jwtToken: 'your-jwt-token',
|
|
341
|
-
retry: { times: 3, delay: 5 }
|
|
342
|
-
}
|
|
343
|
-
}),
|
|
344
|
-
DataType.ARRAY
|
|
345
|
-
);
|
|
346
|
-
}
|
|
347
|
-
```
|
|
348
|
-
|
|
349
|
-
### WebSocket Observables
|
|
350
|
-
|
|
351
|
-
#### connectionStatus$: Observable<boolean>
|
|
352
|
-
|
|
353
|
-
Monitor WebSocket connection status:
|
|
354
|
-
|
|
355
|
-
```typescript
|
|
356
|
-
connectionStatus$ = this.store.connectionStatus$;
|
|
357
|
-
|
|
358
|
-
// Usage
|
|
359
|
-
<div *ngIf="store.connectionStatus$ | async; disconnected" class="connection-status">
|
|
360
|
-
Connected
|
|
361
|
-
</div>
|
|
362
|
-
|
|
363
|
-
<ng-template #disconnected>
|
|
364
|
-
<div class="connection-status offline">
|
|
365
|
-
Reconnecting...
|
|
366
|
-
</div>
|
|
367
|
-
</ng-template>
|
|
368
|
-
```
|
|
369
|
-
|
|
370
|
-
#### communicationMessages$: Observable<any[]>
|
|
371
|
-
|
|
372
|
-
Monitor incoming messages:
|
|
373
|
-
|
|
374
|
-
```typescript
|
|
375
|
-
messages$ = this.store.communicationMessages$;
|
|
376
|
-
```
|
|
377
|
-
|
|
378
|
-
#### notificationMessages$: Observable<any[]>
|
|
379
|
-
|
|
380
|
-
Monitor notifications:
|
|
381
|
-
|
|
382
|
-
```typescript
|
|
383
|
-
notifications$ = this.store.notificationMessages$;
|
|
384
|
-
```
|
|
385
|
-
|
|
386
|
-
### WebSocket Methods
|
|
387
|
-
|
|
388
|
-
#### initializeConnection(wsServer: string, jwtToken: string, user: any)
|
|
389
|
-
|
|
390
|
-
Initialize WebSocket connection:
|
|
391
|
-
|
|
392
|
-
```typescript
|
|
393
|
-
this.store.initializeConnection(
|
|
394
|
-
'ws://localhost:8080',
|
|
395
|
-
'jwt-token-here',
|
|
396
|
-
{ id: 1, name: 'John Doe', email: 'john@example.com' }
|
|
397
|
-
);
|
|
398
|
-
```
|
|
399
|
-
|
|
400
|
-
#### setApiRequestOptions(options: ApiRequest)
|
|
401
|
-
|
|
402
|
-
Update API and WebSocket configuration at runtime:
|
|
403
|
-
|
|
404
|
-
```typescript
|
|
405
|
-
this.store.setApiRequestOptions(
|
|
406
|
-
ApiRequest.adapt({
|
|
407
|
-
...this.store.apiRequestOptions,
|
|
408
|
-
ws: {
|
|
409
|
-
...this.store.apiRequestOptions.ws,
|
|
410
|
-
wsServer: 'wss://new-server.com/ws'
|
|
411
|
-
}
|
|
412
|
-
})
|
|
413
|
-
);
|
|
414
|
-
```
|
|
415
|
-
|
|
416
|
-
#### createChannel(channelName: string)
|
|
417
|
-
|
|
418
|
-
Create a new messaging channel:
|
|
419
|
-
|
|
420
|
-
```typescript
|
|
421
|
-
// Creates PUB-{channelName}
|
|
422
|
-
this.store.createChannel('general-chat');
|
|
423
|
-
```
|
|
424
|
-
|
|
425
|
-
#### subscribeToChannel(channelName: string)
|
|
426
|
-
|
|
427
|
-
Subscribe to a messaging channel:
|
|
428
|
-
|
|
429
|
-
```typescript
|
|
430
|
-
// Subscribes to PUB-{channelName}
|
|
431
|
-
this.store.subscribeToChannel('general-chat');
|
|
432
|
-
```
|
|
433
|
-
|
|
434
|
-
#### unsubscribeFromChannel(channelName: string)
|
|
435
|
-
|
|
436
|
-
Unsubscribe from a channel:
|
|
437
|
-
|
|
438
|
-
```typescript
|
|
439
|
-
this.store.unsubscribeFromChannel('general-chat');
|
|
440
|
-
```
|
|
441
|
-
|
|
442
|
-
#### sendMessage(content: any, channels: string[])
|
|
443
|
-
|
|
444
|
-
Send a message to specific channels:
|
|
445
|
-
|
|
446
|
-
```typescript
|
|
447
|
-
this.store.sendMessage(
|
|
448
|
-
{ message: 'Hello everyone!' },
|
|
449
|
-
['general-chat', 'team-updates']
|
|
450
|
-
);
|
|
451
|
-
```
|
|
452
|
-
|
|
453
|
-
### Real-time CRUD Operations
|
|
454
|
-
|
|
455
|
-
The service automatically syncs state changes via WebSocket:
|
|
456
|
-
|
|
457
|
-
```typescript
|
|
458
|
-
// When createRecord() is called:
|
|
459
|
-
// 1. Makes HTTP POST request
|
|
460
|
-
// 2. On success, updates local state
|
|
461
|
-
// 3. Sends WebSocket message to SYS-{id} channel
|
|
462
|
-
// 4. Other clients receive the message and update their state
|
|
463
|
-
|
|
464
|
-
// When other clients call createRecord:
|
|
465
|
-
// 1. They send WebSocket message to SYS-{id} channel
|
|
466
|
-
// 2. This client receives the message
|
|
467
|
-
// 3. Automatically calls fetchRecords() to get updated data
|
|
468
|
-
```
|
|
469
|
-
|
|
470
|
-
## Database Storage Integration
|
|
471
|
-
|
|
472
|
-
For offline-first applications, configure IndexedDB caching:
|
|
473
|
-
|
|
474
|
-
```typescript
|
|
475
|
-
@Injectable()
|
|
476
|
-
export class UserStateService extends HTTPManagerStateService<User> {
|
|
477
|
-
|
|
478
|
-
constructor() {
|
|
479
|
-
super(
|
|
480
|
-
ApiRequest.adapt({
|
|
481
|
-
server: 'http://localhost:8080',
|
|
482
|
-
path: ['users'],
|
|
483
|
-
adapter: User.adapt // Required for Database Storage
|
|
484
|
-
}),
|
|
485
|
-
DataType.ARRAY,
|
|
486
|
-
DatabaseStorage.adapt({
|
|
487
|
-
table: 'users-cache',
|
|
488
|
-
expiresIn: '1d' // Cache expires in 1 day
|
|
489
|
-
})
|
|
490
|
-
);
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
```
|
|
494
|
-
|
|
495
|
-
### Caching Behavior
|
|
496
|
-
|
|
497
|
-
1. **First Request**: Fetches from server and saves to IndexedDB
|
|
498
|
-
2. **Subsequent Requests**: Checks IndexedDB first, uses cached data if valid
|
|
499
|
-
3. **Cache Expiry**: Automatically fetches from server when cache expires
|
|
500
|
-
4. **Offline Mode**: Uses cached data when network is unavailable
|
|
501
|
-
|
|
502
|
-
## Advanced Examples
|
|
503
|
-
|
|
504
|
-
### Pagination State Service
|
|
505
|
-
|
|
506
|
-
```typescript
|
|
507
|
-
@Injectable()
|
|
508
|
-
export class PaginatedUserStateService extends HTTPManagerStateService<User> {
|
|
509
|
-
|
|
510
|
-
private currentPage = signal(1);
|
|
511
|
-
private pageSize = signal(10);
|
|
512
|
-
private searchQuery = signal('');
|
|
513
|
-
private sortBy = signal('name');
|
|
514
|
-
private sortOrder = signal<'asc' | 'desc'>('asc');
|
|
515
|
-
|
|
516
|
-
// Computed selectors
|
|
517
|
-
totalPages = computed(() => Math.ceil(this.store.data$().length / this.pageSize()));
|
|
518
|
-
|
|
519
|
-
paginatedData = computed(() => {
|
|
520
|
-
const data = this.store.data$();
|
|
521
|
-
const start = (this.currentPage() - 1) * this.pageSize();
|
|
522
|
-
const end = start + this.pageSize();
|
|
523
|
-
return data.slice(start, end);
|
|
524
|
-
});
|
|
525
|
-
|
|
526
|
-
constructor() {
|
|
527
|
-
super(
|
|
528
|
-
ApiRequest.adapt({
|
|
529
|
-
server: 'http://localhost:8080',
|
|
530
|
-
path: ['users'],
|
|
531
|
-
adapter: User.adapt
|
|
532
|
-
}),
|
|
533
|
-
DataType.ARRAY
|
|
534
|
-
);
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
loadUsers() {
|
|
538
|
-
const params = {
|
|
539
|
-
page: this.currentPage(),
|
|
540
|
-
limit: this.pageSize(),
|
|
541
|
-
search: this.searchQuery(),
|
|
542
|
-
sortBy: this.sortBy(),
|
|
543
|
-
sortOrder: this.sortOrder()
|
|
544
|
-
};
|
|
545
|
-
|
|
546
|
-
this.fetchRecords(params);
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
setPage(page: number) {
|
|
550
|
-
this.currentPage.set(page);
|
|
551
|
-
this.loadUsers();
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
setPageSize(size: number) {
|
|
555
|
-
this.pageSize.set(size);
|
|
556
|
-
this.currentPage.set(1); // Reset to first page
|
|
557
|
-
this.loadUsers();
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
setSearchQuery(query: string) {
|
|
561
|
-
this.searchQuery.set(query);
|
|
562
|
-
this.currentPage.set(1); // Reset to first page
|
|
563
|
-
this.loadUsers();
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
setSorting(sortBy: string, order: 'asc' | 'desc') {
|
|
567
|
-
this.sortBy.set(sortBy);
|
|
568
|
-
this.sortOrder.set(order);
|
|
569
|
-
this.currentPage.set(1); // Reset to first page
|
|
570
|
-
this.loadUsers();
|
|
571
|
-
}
|
|
572
|
-
}
|
|
573
|
-
```
|
|
574
|
-
|
|
575
|
-
### Real-time Chat with State Management
|
|
576
|
-
|
|
577
|
-
```typescript
|
|
578
|
-
@Injectable()
|
|
579
|
-
export class ChatStateService extends HTTPManagerStateService<ChatMessage> {
|
|
580
|
-
|
|
581
|
-
private currentUser = signal<any>(null);
|
|
582
|
-
private activeChannel = signal<string>('general');
|
|
583
|
-
|
|
584
|
-
// Observables
|
|
585
|
-
messages$ = this.store.data$;
|
|
586
|
-
channelUsers$ = this.store.communicationMessages$;
|
|
587
|
-
connectionStatus$ = this.store.connectionStatus$;
|
|
588
|
-
|
|
589
|
-
constructor() {
|
|
590
|
-
super(
|
|
591
|
-
ApiRequest.adapt({
|
|
592
|
-
server: 'http://localhost:8080',
|
|
593
|
-
path: ['messages'],
|
|
594
|
-
adapter: ChatMessage.adapt,
|
|
595
|
-
ws: {
|
|
596
|
-
id: 'CHAT_WS',
|
|
597
|
-
wsServer: 'ws://localhost:8080'
|
|
598
|
-
}
|
|
599
|
-
}),
|
|
600
|
-
DataType.ARRAY
|
|
601
|
-
);
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
initializeChat(user: any) {
|
|
605
|
-
this.currentUser.set(user);
|
|
606
|
-
this.store.initializeConnection(
|
|
607
|
-
'ws://localhost:8080',
|
|
608
|
-
user.jwtToken,
|
|
609
|
-
user
|
|
610
|
-
);
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
joinChannel(channelName: string) {
|
|
614
|
-
this.activeChannel.set(channelName);
|
|
615
|
-
this.store.createChannel(channelName);
|
|
616
|
-
this.store.subscribeToChannel(channelName);
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
leaveChannel(channelName: string) {
|
|
620
|
-
this.store.unsubscribeFromChannel(channelName);
|
|
621
|
-
if (this.activeChannel() === channelName) {
|
|
622
|
-
this.activeChannel.set('general');
|
|
623
|
-
}
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
sendMessage(message: string) {
|
|
627
|
-
const channel = this.activeChannel();
|
|
628
|
-
const user = this.currentUser();
|
|
629
|
-
|
|
630
|
-
if (!channel || !user) return;
|
|
631
|
-
|
|
632
|
-
// Create message record (HTTP)
|
|
633
|
-
this.createRecord({
|
|
634
|
-
id: Date.now(), // Temporary ID
|
|
635
|
-
content: message,
|
|
636
|
-
channel: channel,
|
|
637
|
-
userId: user.id,
|
|
638
|
-
userName: user.name,
|
|
639
|
-
timestamp: new Date()
|
|
640
|
-
});
|
|
641
|
-
|
|
642
|
-
// Send via WebSocket
|
|
643
|
-
this.store.sendMessage(
|
|
644
|
-
{
|
|
645
|
-
message: message,
|
|
646
|
-
userId: user.id,
|
|
647
|
-
userName: user.name,
|
|
648
|
-
channel: channel
|
|
649
|
-
},
|
|
650
|
-
[channel]
|
|
651
|
-
);
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
getMessagesForChannel(channelName: string) {
|
|
655
|
-
return this.store.data$.pipe(
|
|
656
|
-
map(messages => messages.filter(msg => msg.channel === channelName))
|
|
657
|
-
);
|
|
658
|
-
}
|
|
659
|
-
}
|
|
660
|
-
```
|
|
661
|
-
|
|
662
|
-
### Complex Filter and Search State
|
|
663
|
-
|
|
664
|
-
```typescript
|
|
665
|
-
@Injectable()
|
|
666
|
-
export class AdvancedUserStateService extends HTTPManagerStateService<User> {
|
|
667
|
-
|
|
668
|
-
// Filter state
|
|
669
|
-
activeFilter = signal<'all' | 'active' | 'inactive'>('all');
|
|
670
|
-
roleFilter = signal<string>('');
|
|
671
|
-
searchQuery = signal<string>('');
|
|
672
|
-
dateRange = signal<{start: Date, end: Date} | null>(null);
|
|
673
|
-
|
|
674
|
-
// Computed filters
|
|
675
|
-
filteredUsers = computed(() => {
|
|
676
|
-
let users = this.store.data$();
|
|
677
|
-
|
|
678
|
-
// Apply active filter
|
|
679
|
-
if (this.activeFilter() !== 'all') {
|
|
680
|
-
const isActive = this.activeFilter() === 'active';
|
|
681
|
-
users = users.filter(user => user.active === isActive);
|
|
682
|
-
}
|
|
683
|
-
|
|
684
|
-
// Apply role filter
|
|
685
|
-
if (this.roleFilter()) {
|
|
686
|
-
users = users.filter(user => user.role === this.roleFilter());
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
// Apply search query
|
|
690
|
-
if (this.searchQuery()) {
|
|
691
|
-
const query = this.searchQuery().toLowerCase();
|
|
692
|
-
users = users.filter(user =>
|
|
693
|
-
user.name.toLowerCase().includes(query) ||
|
|
694
|
-
user.email.toLowerCase().includes(query)
|
|
695
|
-
);
|
|
696
|
-
}
|
|
697
|
-
|
|
698
|
-
// Apply date range
|
|
699
|
-
if (this.dateRange()) {
|
|
700
|
-
const { start, end } = this.dateRange()!;
|
|
701
|
-
users = users.filter(user => {
|
|
702
|
-
const userDate = new Date(user.createdAt);
|
|
703
|
-
return userDate >= start && userDate <= end;
|
|
704
|
-
});
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
return users;
|
|
708
|
-
});
|
|
709
|
-
|
|
710
|
-
filterStats = computed(() => {
|
|
711
|
-
const allUsers = this.store.data$();
|
|
712
|
-
return {
|
|
713
|
-
total: allUsers.length,
|
|
714
|
-
active: allUsers.filter(u => u.active).length,
|
|
715
|
-
inactive: allUsers.filter(u => !u.active).length,
|
|
716
|
-
filtered: this.filteredUsers().length
|
|
717
|
-
};
|
|
718
|
-
});
|
|
719
|
-
|
|
720
|
-
constructor() {
|
|
721
|
-
super(
|
|
722
|
-
ApiRequest.adapt({
|
|
723
|
-
server: 'http://localhost:8080',
|
|
724
|
-
path: ['users'],
|
|
725
|
-
adapter: User.adapt
|
|
726
|
-
}),
|
|
727
|
-
DataType.ARRAY,
|
|
728
|
-
DatabaseStorage.adapt({
|
|
729
|
-
table: 'users-advanced-cache',
|
|
730
|
-
expiresIn: '1h'
|
|
731
|
-
})
|
|
732
|
-
);
|
|
733
|
-
}
|
|
734
|
-
|
|
735
|
-
// Filter methods
|
|
736
|
-
setActiveFilter(filter: 'all' | 'active' | 'inactive') {
|
|
737
|
-
this.activeFilter.set(filter);
|
|
738
|
-
this.fetchRecords();
|
|
739
|
-
}
|
|
740
|
-
|
|
741
|
-
setRoleFilter(role: string) {
|
|
742
|
-
this.roleFilter.set(role);
|
|
743
|
-
this.fetchRecords();
|
|
744
|
-
}
|
|
745
|
-
|
|
746
|
-
setSearchQuery(query: string) {
|
|
747
|
-
this.searchQuery.set(query);
|
|
748
|
-
// Debounced search
|
|
749
|
-
setTimeout(() => this.fetchRecords(), 300);
|
|
750
|
-
}
|
|
751
|
-
|
|
752
|
-
setDateRange(start: Date, end: Date) {
|
|
753
|
-
this.dateRange.set({ start, end });
|
|
754
|
-
this.fetchRecords();
|
|
755
|
-
}
|
|
756
|
-
|
|
757
|
-
clearFilters() {
|
|
758
|
-
this.activeFilter.set('all');
|
|
759
|
-
this.roleFilter.set('');
|
|
760
|
-
this.searchQuery.set('');
|
|
761
|
-
this.dateRange.set(null);
|
|
762
|
-
this.fetchRecords();
|
|
763
|
-
}
|
|
764
|
-
}
|
|
765
|
-
```
|
|
766
|
-
|
|
767
|
-
## Error Handling and Retry Logic
|
|
768
|
-
|
|
769
|
-
### Custom Error Handling
|
|
770
|
-
|
|
771
|
-
```typescript
|
|
772
|
-
@Injectable()
|
|
773
|
-
export class RobustUserStateService extends HTTPManagerStateService<User> {
|
|
774
|
-
|
|
775
|
-
private retryAttempts = signal(0);
|
|
776
|
-
private maxRetries = signal(3);
|
|
777
|
-
|
|
778
|
-
constructor() {
|
|
779
|
-
super(
|
|
780
|
-
ApiRequest.adapt({
|
|
781
|
-
server: 'http://localhost:8080',
|
|
782
|
-
path: ['users'],
|
|
783
|
-
retry: { times: 3, delay: 2 },
|
|
784
|
-
displayError: true
|
|
785
|
-
}),
|
|
786
|
-
DataType.ARRAY
|
|
787
|
-
);
|
|
788
|
-
}
|
|
789
|
-
|
|
790
|
-
// Override to add custom error handling
|
|
791
|
-
fetchRecords(params?: any) {
|
|
792
|
-
this.retryAttempts.set(0);
|
|
793
|
-
super.fetchRecords(params);
|
|
794
|
-
}
|
|
795
|
-
|
|
796
|
-
// Custom retry logic for critical operations
|
|
797
|
-
criticalUpdate(user: User) {
|
|
798
|
-
try {
|
|
799
|
-
this.updateRecord(user);
|
|
800
|
-
} catch (error) {
|
|
801
|
-
this.handleCriticalError(error, () => this.criticalUpdate(user));
|
|
802
|
-
}
|
|
803
|
-
}
|
|
804
|
-
|
|
805
|
-
private handleCriticalError(error: any, retryFn: () => void) {
|
|
806
|
-
const currentAttempts = this.retryAttempts();
|
|
807
|
-
const maxAttempts = this.maxRetries();
|
|
808
|
-
|
|
809
|
-
if (currentAttempts < maxAttempts) {
|
|
810
|
-
this.retryAttempts.set(currentAttempts + 1);
|
|
811
|
-
|
|
812
|
-
// Exponential backoff
|
|
813
|
-
const delay = Math.pow(2, currentAttempts) * 1000;
|
|
814
|
-
setTimeout(retryFn, delay);
|
|
815
|
-
} else {
|
|
816
|
-
// Max retries reached, show critical error
|
|
817
|
-
this.showCriticalErrorDialog(error);
|
|
818
|
-
}
|
|
819
|
-
}
|
|
820
|
-
|
|
821
|
-
private showCriticalErrorDialog(error: any) {
|
|
822
|
-
// Implement your critical error handling
|
|
823
|
-
console.error('Critical operation failed after retries:', error);
|
|
824
|
-
}
|
|
825
|
-
}
|
|
826
|
-
```
|
|
827
|
-
|
|
828
|
-
## Best Practices
|
|
829
|
-
|
|
830
|
-
### 1. Service Layer Pattern
|
|
831
|
-
|
|
832
|
-
```typescript
|
|
833
|
-
// ✅ Good
|
|
834
|
-
@Injectable()
|
|
835
|
-
export class UserStateService extends HTTPManagerStateService<User> {
|
|
836
|
-
// Business logic methods
|
|
837
|
-
activateUser(id: number) {
|
|
838
|
-
this.updateRecord({ id, active: true });
|
|
839
|
-
}
|
|
840
|
-
|
|
841
|
-
deactivateUser(id: number) {
|
|
842
|
-
this.updateRecord({ id, active: false });
|
|
843
|
-
}
|
|
844
|
-
}
|
|
845
|
-
|
|
846
|
-
// ❌ Avoid
|
|
847
|
-
@Component({
|
|
848
|
-
template: `<div (click)="store.updateRecord(...)">`
|
|
849
|
-
})
|
|
850
|
-
export class BadComponent {}
|
|
851
|
-
```
|
|
852
|
-
|
|
853
|
-
### 2. Proper State Management
|
|
854
|
-
|
|
855
|
-
```typescript
|
|
856
|
-
// ✅ Good
|
|
857
|
-
loadUsers() {
|
|
858
|
-
this.fetchRecords(); // Uses internal state management
|
|
859
|
-
}
|
|
860
|
-
|
|
861
|
-
// ❌ Avoid
|
|
862
|
-
loadUsers() {
|
|
863
|
-
this.http.get('users').subscribe(users => {
|
|
864
|
-
this.users = users; // Manual state management
|
|
865
|
-
});
|
|
866
|
-
}
|
|
867
|
-
```
|
|
868
|
-
|
|
869
|
-
### 3. WebSocket Error Handling
|
|
870
|
-
|
|
871
|
-
```typescript
|
|
872
|
-
// ✅ Good
|
|
873
|
-
initializeConnection(wsServer: string, jwtToken: string, user: any) {
|
|
874
|
-
try {
|
|
875
|
-
this.setApiRequestOptions(
|
|
876
|
-
ApiRequest.adapt({
|
|
877
|
-
...this.apiRequestOptions,
|
|
878
|
-
ws: { ...this.apiRequestOptions.ws, wsServer, jwtToken, user }
|
|
879
|
-
})
|
|
880
|
-
);
|
|
881
|
-
} catch (error) {
|
|
882
|
-
console.error('WebSocket initialization failed:', error);
|
|
883
|
-
}
|
|
884
|
-
}
|
|
885
|
-
```
|
|
886
|
-
|
|
887
|
-
### 4. Memory Management
|
|
888
|
-
|
|
889
|
-
```typescript
|
|
890
|
-
@Component({ template: '...' })
|
|
891
|
-
export class UserComponent implements OnDestroy {
|
|
892
|
-
private destroy$ = new Subject<void>();
|
|
893
|
-
|
|
894
|
-
ngOnInit() {
|
|
895
|
-
this.store.data$
|
|
896
|
-
.pipe(takeUntil(this.destroy$))
|
|
897
|
-
.subscribe();
|
|
898
|
-
}
|
|
899
|
-
|
|
900
|
-
ngOnDestroy() {
|
|
901
|
-
this.destroy$.next();
|
|
902
|
-
this.destroy$.complete();
|
|
903
|
-
}
|
|
904
|
-
}
|
|
905
|
-
```
|
|
906
|
-
|
|
907
|
-
## Troubleshooting
|
|
908
|
-
|
|
909
|
-
### Common Issues
|
|
910
|
-
|
|
911
|
-
#### 1. WebSocket Not Connecting
|
|
912
|
-
```typescript
|
|
913
|
-
// Check configuration
|
|
914
|
-
console.log('WS Config:', this.store.apiRequestOptions?.ws);
|
|
915
|
-
|
|
916
|
-
// Verify connection
|
|
917
|
-
this.store.connectionStatus$.subscribe(status => {
|
|
918
|
-
console.log('WebSocket status:', status);
|
|
919
|
-
});
|
|
920
|
-
```
|
|
921
|
-
|
|
922
|
-
#### 2. State Not Updating
|
|
923
|
-
```typescript
|
|
924
|
-
// Ensure adapter is provided for database storage
|
|
925
|
-
ApiRequest.adapt({
|
|
926
|
-
path: ['users'],
|
|
927
|
-
adapter: User.adapt // Required!
|
|
928
|
-
});
|
|
929
|
-
```
|
|
930
|
-
|
|
931
|
-
#### 3. Infinite Loops
|
|
932
|
-
```typescript
|
|
933
|
-
// Avoid calling fetchRecords() in response to WebSocket messages
|
|
934
|
-
// The service handles this automatically
|
|
935
|
-
|
|
936
|
-
// ❌ Don't do this
|
|
937
|
-
communicationMessages$.subscribe(msg => {
|
|
938
|
-
this.fetchRecords(); // This can cause loops
|
|
939
|
-
});
|
|
940
|
-
```
|
|
941
|
-
|
|
942
|
-
## Related Documentation
|
|
943
|
-
|
|
944
|
-
- [HTTP Manager Service](http-manager/README.md)
|
|
945
|
-
- [HTTP Signals Service](http-signals/README.md)
|
|
946
|
-
- [WebSocket Service](websocket/README.md)
|
|
947
|
-
- [Database Manager Service](database/README.md)
|
|
948
|
-
- [Architecture Overview](../architecture/README.md)
|