@verisoft/store 19.0.0-rc001 → 20.0.0

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/README.md CHANGED
@@ -1,4 +1,421 @@
1
- # store
1
+ # @verisoft/store
2
+
3
+ NgRx-based state management library for Verisoft Angular applications, providing standardized patterns for handling application state, including table/list views, detail forms, and entity relationships.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @verisoft/store
9
+ ```
10
+
11
+ ## Features
12
+
13
+ - **Detail State Management**: Handle entity detail forms with validation and persistence
14
+ - **Table State Management**: Manage paginated lists with filtering, sorting, and selection
15
+ - **Binding State Management**: Handle entity relationships and associations
16
+ - **Type-Safe**: Full TypeScript support with generic types
17
+ - **NgRx Integration**: Built on @ngrx/store and @ngrx/effects
18
+
19
+ ## State Modules
20
+
21
+ ### Detail State
22
+
23
+ Manages individual entity details, forms, and CRUD operations.
24
+
25
+ ```typescript
26
+ import {
27
+ createDetailReducers,
28
+ createInitDetailAction,
29
+ createLoadDetailSuccessAction,
30
+ createSaveDetailAction,
31
+ DetailState
32
+ } from '@verisoft/store';
33
+
34
+ // State interface
35
+ interface UserDetailState extends DetailState<User> {
36
+ // Additional state properties if needed
37
+ }
38
+
39
+ // Create reducer
40
+ const userDetailReducer = createDetailReducers<User>(
41
+ 'userDetail', // Repository name
42
+ INITIAL_USER_DETAIL_STATE
43
+ );
44
+
45
+ // Actions usage
46
+ export class UserDetailComponent {
47
+ constructor(private store: Store) {}
48
+
49
+ loadUser(id: string) {
50
+ this.store.dispatch(
51
+ createInitDetailAction('userDetail')({ obj: id })
52
+ );
53
+ }
54
+
55
+ saveUser() {
56
+ this.store.dispatch(
57
+ createSaveDetailAction('userDetail')()
58
+ );
59
+ }
60
+ }
61
+ ```
62
+
63
+ ### Table State
64
+
65
+ Manages paginated lists with filtering, sorting, and item selection.
66
+
67
+ ```typescript
68
+ import {
69
+ createTablePageReducers,
70
+ createGetPageTableAction,
71
+ createFilterPageTableAction,
72
+ TableState
73
+ } from '@verisoft/store';
74
+ import { RequestParams } from '@verisoft/core';
75
+
76
+ // State interface
77
+ interface UserTableState extends TableState<User> {
78
+ // Additional state properties if needed
79
+ }
80
+
81
+ // Create reducer
82
+ const userTableReducer = createTablePageReducers<User>(
83
+ 'userTable', // Repository name
84
+ INITIAL_USER_TABLE_STATE
85
+ );
86
+
87
+ // Actions usage
88
+ export class UserListComponent {
89
+ constructor(private store: Store) {}
90
+
91
+ loadPage(page: number) {
92
+ this.store.dispatch(
93
+ createGetPageTableAction('userTable')({
94
+ page,
95
+ filter: this.currentFilter,
96
+ sort: this.currentSort
97
+ })
98
+ );
99
+ }
100
+
101
+ applyFilter(filter: UserFilter) {
102
+ this.store.dispatch(
103
+ createFilterPageTableAction('userTable')({ filter })
104
+ );
105
+ }
106
+
107
+ selectItems(selectedItems: User[]) {
108
+ this.store.dispatch(
109
+ createSelectItemsTableAction('userTable')({ selectedItems })
110
+ );
111
+ }
112
+ }
113
+ ```
114
+
115
+ ### Binding State
116
+
117
+ Manages relationships between entities (e.g., user roles, project members).
118
+
119
+ ```typescript
120
+ import {
121
+ createBindingsEffects,
122
+ createAddBindingAction,
123
+ createEditBindingAction,
124
+ createDeleteBindingAction
125
+ } from '@verisoft/store';
126
+
127
+ // Usage in effects
128
+ @Injectable()
129
+ export class UserRoleEffects {
130
+ constructor(
131
+ private actions$: Actions,
132
+ private userRoleService: UserRoleService,
133
+ private store: Store
134
+ ) {}
135
+
136
+ // Create binding effects
137
+ userRoleBindings$ = createBindingsEffects(
138
+ 'userRoles', // Repository name
139
+ this.actions$, // Actions observable
140
+ this.userDetail$, // Parent entity observable
141
+ 'role', // Singular name
142
+ this.userRoleService.addRoles.bind(this.userRoleService),
143
+ this.userRoleService.deleteRoles.bind(this.userRoleService),
144
+ this.snackBar,
145
+ this.translateService,
146
+ this.tableService
147
+ );
148
+ }
149
+ ```
150
+
151
+ ## Complete Example
152
+
153
+ ### Setting up Feature State
154
+
155
+ ```typescript
156
+ import { createFeatureSelector, createSelector } from '@ngrx/store';
157
+ import {
158
+ DetailState,
159
+ TableState,
160
+ createDetailReducers,
161
+ createTablePageReducers
162
+ } from '@verisoft/store';
163
+
164
+ // Feature state interface
165
+ interface UserFeatureState {
166
+ userDetail: DetailState<User>;
167
+ userTable: TableState<User>;
168
+ }
169
+
170
+ // Reducers
171
+ const userDetailReducer = createDetailReducers<User>('userDetail');
172
+ const userTableReducer = createTablePageReducers<User>('userTable');
173
+
174
+ // Feature selector
175
+ const selectUserFeature = createFeatureSelector<UserFeatureState>('users');
176
+
177
+ // Selectors
178
+ export const selectUserDetail = createSelector(
179
+ selectUserFeature,
180
+ state => state.userDetail
181
+ );
182
+
183
+ export const selectUserTable = createSelector(
184
+ selectUserFeature,
185
+ state => state.userTable
186
+ );
187
+
188
+ export const selectCurrentUser = createSelector(
189
+ selectUserDetail,
190
+ state => state.item
191
+ );
192
+
193
+ export const selectUserList = createSelector(
194
+ selectUserTable,
195
+ state => state.gPage?.data || []
196
+ );
197
+ ```
198
+
199
+ ### Component Integration
200
+
201
+ ```typescript
202
+ import { Component, OnInit } from '@angular/core';
203
+ import { Store } from '@ngrx/store';
204
+ import {
205
+ createInitDetailAction,
206
+ createSaveDetailAction,
207
+ createGetPageTableAction
208
+ } from '@verisoft/store';
209
+
210
+ @Component({
211
+ template: `
212
+ <div *ngIf="user$ | async as user">
213
+ <form [formGroup]="userForm" (ngSubmit)="saveUser()">
214
+ <!-- Form controls -->
215
+ </form>
216
+ </div>
217
+
218
+ <div *ngIf="users$ | async as users">
219
+ <table>
220
+ <tr *ngFor="let user of users">
221
+ <td>{{ user.name }}</td>
222
+ <td>{{ user.email }}</td>
223
+ </tr>
224
+ </table>
225
+ </div>
226
+ `
227
+ })
228
+ export class UserManagementComponent implements OnInit {
229
+ user$ = this.store.select(selectCurrentUser);
230
+ users$ = this.store.select(selectUserList);
231
+ loading$ = this.store.select(selectUserTable).pipe(
232
+ map(state => state.dataLoading)
233
+ );
234
+
235
+ constructor(private store: Store) {}
236
+
237
+ ngOnInit() {
238
+ this.loadUsers();
239
+ }
240
+
241
+ loadUsers() {
242
+ this.store.dispatch(
243
+ createGetPageTableAction('userTable')({
244
+ page: 1,
245
+ size: 20
246
+ })
247
+ );
248
+ }
249
+
250
+ loadUser(id: string) {
251
+ this.store.dispatch(
252
+ createInitDetailAction('userDetail')({ obj: id })
253
+ );
254
+ }
255
+
256
+ saveUser() {
257
+ this.store.dispatch(
258
+ createSaveDetailAction('userDetail')()
259
+ );
260
+ }
261
+ }
262
+ ```
263
+
264
+ ### Form Integration with DetailStore Directive
265
+
266
+ ```typescript
267
+ // Component using the directive
268
+ @Component({
269
+ template: `
270
+ <form
271
+ v-baseForm
272
+ #userForm="baseForm"
273
+ v-useDetailStore
274
+ [form]="userForm"
275
+ detailsRepository="userDetail"
276
+ ngrxFeatureKey="users"
277
+ [detailId]="userId">
278
+
279
+ <input
280
+ type="text"
281
+ formControlName="name"
282
+ placeholder="User Name">
283
+
284
+ <input
285
+ type="email"
286
+ formControlName="email"
287
+ placeholder="Email">
288
+
289
+ <button type="submit" [disabled]="!userForm.valid">
290
+ Save User
291
+ </button>
292
+ </form>
293
+ `
294
+ })
295
+ export class UserFormComponent {
296
+ @Input() userId?: string;
297
+ }
298
+ ```
299
+
300
+ ## State Models
301
+
302
+ ### DetailState Interface
303
+
304
+ ```typescript
305
+ interface DetailState<T> {
306
+ loaded: boolean; // Data load status
307
+ item?: T; // Current entity
308
+ formState?: FormState; // Form validation state
309
+ error?: string | null; // Error message
310
+ saveItemState: SaveItemState; // Save operation state
311
+ backendValidationErrors: BackendValidationError[]; // Server validation
312
+ }
313
+
314
+ interface FormState {
315
+ dirty: boolean; // Form has changes
316
+ valid: boolean; // Form is valid
317
+ }
318
+
319
+ interface SaveItemState {
320
+ saveInProgress: boolean; // Save operation in progress
321
+ error?: string | null; // Save error
322
+ }
323
+ ```
324
+
325
+ ### TableState Interface
326
+
327
+ ```typescript
328
+ interface TableState<T> {
329
+ dataLoading: boolean; // Data loading status
330
+ requestParams: RequestParams<T>; // Current request parameters
331
+ gPage?: Page<T>; // Paginated data
332
+ error?: string | null; // Error message
333
+ selectedItems?: T[]; // Selected table items
334
+ }
335
+ ```
336
+
337
+ ## Available Actions
338
+
339
+ ### Detail Actions
340
+
341
+ ```typescript
342
+ // Initialize detail (load or create new)
343
+ createInitDetailAction(repository)({ obj: id })
344
+ createInitNewDetailAction(repository)()
345
+
346
+ // Data operations
347
+ createLoadDetailSuccessAction(repository)({ item })
348
+ createLoadDetailFailureAction(repository)({ error })
349
+
350
+ // Form operations
351
+ createUpdateDetailAction(repository)({ item })
352
+ createUpdateFormStateAction(repository)({ formState })
353
+
354
+ // Save operations
355
+ createSaveDetailAction(repository)()
356
+ createSaveDetailSuccessAction(repository)({ item })
357
+ createSaveDetailFailureAction(repository)({ error })
358
+
359
+ // State management
360
+ createResetStateAction(repository)()
361
+ ```
362
+
363
+ ### Table Actions
364
+
365
+ ```typescript
366
+ // Data loading
367
+ createGetPageTableAction(repository)({ page, filter, sort })
368
+ createDataLoadSuccessTableAction(repository)({ gPage })
369
+ createDataLoadErrorTableAction(repository)({ error })
370
+
371
+ // Filtering and sorting
372
+ createFilterPageTableAction(repository)({ filter })
373
+ createStaticFilterTableAction(repository)({ filter })
374
+ createResetTableFilterAction(repository)()
375
+
376
+ // Selection and pagination
377
+ createSelectItemsTableAction(repository)({ selectedItems })
378
+ createChangePageSizeTableAction(repository)({ size })
379
+
380
+ // State management
381
+ createDestroyTableAction(repository)()
382
+ ```
383
+
384
+ ### Binding Actions
385
+
386
+ ```typescript
387
+ // Binding operations
388
+ createAddBindingAction(repository)({ bindings })
389
+ createEditBindingAction(repository)({ binding })
390
+ createDeleteBindingAction(repository)({ bindingIds })
391
+
392
+ // Success/failure actions
393
+ createBindingModifiedSuccessAction(repository)()
394
+ createBindingModifiedFailureAction(repository)({ error })
395
+ ```
396
+
397
+ ## Best Practices
398
+
399
+ 1. **Repository Naming**: Use consistent repository names across your application
400
+ 2. **State Structure**: Keep state normalized and avoid deep nesting
401
+ 3. **Selectors**: Create memoized selectors for computed state
402
+ 4. **Effects**: Handle side effects (API calls) in NgRx effects
403
+ 5. **Error Handling**: Implement proper error handling for all operations
404
+ 6. **Type Safety**: Use TypeScript generics for type-safe state management
405
+
406
+ ## Dependencies
407
+
408
+ - `@ngrx/store`
409
+ - `@ngrx/effects`
410
+ - `@verisoft/core` (for HTTP models)
411
+
412
+ ## Contributing
413
+
414
+ This library is part of the Verisoft framework ecosystem. Follow the established patterns and ensure compatibility with other @verisoft packages.
415
+
416
+ ## Running unit tests
417
+
418
+ Run `nx test store` to execute the unit tests.
2
419
 
3
420
  This library was generated with [Nx](https://nx.dev).
4
421