ng-qubee 3.1.0 → 3.2.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 +109 -0
- package/fesm2022/ng-qubee.mjs +1017 -759
- package/fesm2022/ng-qubee.mjs.map +1 -1
- package/package.json +1 -2
- package/types/ng-qubee.d.ts +221 -5
package/README.md
CHANGED
|
@@ -176,6 +176,27 @@ The NestJS driver generates URIs compatible with [nestjs-paginate](https://githu
|
|
|
176
176
|
- **Limit** is composed as `limit=15`
|
|
177
177
|
- **Page** is composed as `page=1`
|
|
178
178
|
|
|
179
|
+
### Per-component instances
|
|
180
|
+
|
|
181
|
+
By default, `provideNgQubee()` / `NgQubeeModule.forRoot()` register `NgQubeeService` at the environment injector, so every component that injects it shares the same query-builder state. If you need a dedicated instance — e.g. a feature component whose filters and pagination must not bleed into the app-wide one — spread `provideNgQubeeInstance()` into the component's `providers`:
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
import { Component } from '@angular/core';
|
|
185
|
+
import { NgQubeeService, provideNgQubeeInstance } from 'ng-qubee';
|
|
186
|
+
|
|
187
|
+
@Component({
|
|
188
|
+
selector: 'app-product-list',
|
|
189
|
+
standalone: true,
|
|
190
|
+
providers: [...provideNgQubeeInstance()],
|
|
191
|
+
template: '...'
|
|
192
|
+
})
|
|
193
|
+
export class ProductListComponent {
|
|
194
|
+
constructor(private _qb: NgQubeeService) {}
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
The component gets its own `NgQubeeService`, `NestService`, and `PaginationService`. The driver, strategies, and options are inherited from the environment injector configured by `provideNgQubee()` — you still configure the library once at bootstrap.
|
|
199
|
+
|
|
179
200
|
## Query Builder API
|
|
180
201
|
|
|
181
202
|
For composing queries, the first step is to inject the proper NgQubeeService:
|
|
@@ -337,6 +358,94 @@ Limit validation is driver-scoped — each request strategy enforces its own acc
|
|
|
337
358
|
|
|
338
359
|
Non-integer values, zero, negative numbers (other than `-1` for NestJS), `NaN`, and `Infinity` are all rejected.
|
|
339
360
|
|
|
361
|
+
#### Auto-reset of page on result-set-changing mutations
|
|
362
|
+
|
|
363
|
+
Any mutation that changes *which records* the server would return also resets `state.page` to `1` automatically. Staying on page 5 of an old result set after changing filters is almost always a bug, so the library makes the reset explicit:
|
|
364
|
+
|
|
365
|
+
| Resets page to 1 | Does NOT reset page |
|
|
366
|
+
|---|---|
|
|
367
|
+
| `setLimit()` | `setBaseUrl()` |
|
|
368
|
+
| `setResource()` | `setPage()` |
|
|
369
|
+
| `setSearch()` / `deleteSearch()` | `addFields()` / `deleteFields()` / `deleteFieldsByModel()` |
|
|
370
|
+
| `addFilter()` / `deleteFilters()` | `addIncludes()` / `deleteIncludes()` |
|
|
371
|
+
| `addFilterOperator()` / `deleteOperatorFilters()` | `addSelect()` / `deleteSelect()` |
|
|
372
|
+
| `addSort()` / `deleteSorts()` | |
|
|
373
|
+
|
|
374
|
+
Rule of thumb: if a mutation changes the record *set* (filters, sort, search, limit, resource), page resets. If it only changes the record *shape* (fields, includes, select), page stays. If you intentionally want to keep the previous page number, call `setPage(n)` again after the mutation.
|
|
375
|
+
|
|
376
|
+
### Pagination navigation
|
|
377
|
+
|
|
378
|
+
The service exposes a fluent navigation surface so you can wire a standard paginator UI (Prev / N of M / Next) with no manual bookkeeping. All navigation methods return `this` and can be chained.
|
|
379
|
+
|
|
380
|
+
```typescript
|
|
381
|
+
this._ngQubeeService.nextPage().generateUri().subscribe(uri => /* fire the request */);
|
|
382
|
+
this._ngQubeeService.previousPage();
|
|
383
|
+
this._ngQubeeService.firstPage();
|
|
384
|
+
this._ngQubeeService.lastPage();
|
|
385
|
+
this._ngQubeeService.goToPage(3);
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
#### Auto-sync from `PaginationService.paginate()`
|
|
389
|
+
|
|
390
|
+
When you hand a paginated response to `PaginationService.paginate()`, the library automatically copies the response's `page` and `lastPage` back into the query-builder state. That means `lastPage()`, `goToPage(n)` bounds checks, and the predicates below become accurate immediately — you don't thread the collection's `lastPage` back in yourself.
|
|
391
|
+
|
|
392
|
+
```typescript
|
|
393
|
+
this._paginationService.paginate(response); // auto-writes page + lastPage
|
|
394
|
+
this._ngQubeeService.lastPage(); // now safe; jumps to the last page
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
The auto-sync only flips `isLastPageKnown` to `true` when the response carries a **positive integer** `lastPage`. Server-emitted `0` (empty collection) and absent fields leave the flag `false` — the helpers fall back to their conservative defaults.
|
|
398
|
+
|
|
399
|
+
#### Predicates and accessors
|
|
400
|
+
|
|
401
|
+
Template-safe methods for driving button disable-states and labels:
|
|
402
|
+
|
|
403
|
+
```typescript
|
|
404
|
+
qb.isFirstPage(); // true on page 1
|
|
405
|
+
qb.isLastPage(); // true only when bounds known and page === lastPage
|
|
406
|
+
qb.hasNextPage(); // true when bounds unknown, or page < lastPage
|
|
407
|
+
qb.hasPreviousPage(); // true when page > 1
|
|
408
|
+
qb.currentPage(); // state.page (always safe)
|
|
409
|
+
qb.totalPages(); // state.lastPage (throws if never synced)
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
Angular template wiring:
|
|
413
|
+
|
|
414
|
+
```html
|
|
415
|
+
<button [disabled]="qb.isFirstPage()" (click)="qb.previousPage()">Prev</button>
|
|
416
|
+
<span>Page {{ qb.currentPage() }} of {{ qb.totalPages() }}</span>
|
|
417
|
+
<button [disabled]="qb.isLastPage()" (click)="qb.nextPage()">Next</button>
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
For the `qb.totalPages()` template usage above, either call `paginate()` at least once before the template renders, or guard the display with `*ngIf="qb.hasNextPage() || !qb.isFirstPage()"` / by reading `qb.nest().isLastPageKnown` directly.
|
|
421
|
+
|
|
422
|
+
#### Error behavior
|
|
423
|
+
|
|
424
|
+
| Helper | Throws | When |
|
|
425
|
+
|---|---|---|
|
|
426
|
+
| `nextPage()` | — | Never throws. No-op when already at `lastPage` (bounds known). |
|
|
427
|
+
| `previousPage()` | — | Never throws. No-op at page 1. |
|
|
428
|
+
| `firstPage()` | — | Never throws. |
|
|
429
|
+
| `lastPage()` | `PaginationNotSyncedError` | `paginate()` has never run (`state.isLastPageKnown` is `false`). |
|
|
430
|
+
| `goToPage(n)` | `InvalidPageNumberError` | `n` is not a positive integer, or exceeds `lastPage` when bounds are known. |
|
|
431
|
+
| `isFirstPage()` / `isLastPage()` / `hasNextPage()` / `hasPreviousPage()` | — | Template-safe. Conservative defaults when bounds unknown. |
|
|
432
|
+
| `currentPage()` | — | Always safe. |
|
|
433
|
+
| `totalPages()` | `PaginationNotSyncedError` | `paginate()` has never run. Guard with `nest().isLastPageKnown` for a non-throwing check. |
|
|
434
|
+
|
|
435
|
+
#### Guarding the imperative "jump to last" button
|
|
436
|
+
|
|
437
|
+
`lastPage()` and `totalPages()` need a synced response. A safe pattern:
|
|
438
|
+
|
|
439
|
+
```html
|
|
440
|
+
<button
|
|
441
|
+
[disabled]="!qb.nest().isLastPageKnown || qb.isLastPage()"
|
|
442
|
+
(click)="qb.lastPage()">
|
|
443
|
+
Last
|
|
444
|
+
</button>
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
The `isLastPageKnown` read short-circuits before `qb.lastPage()` could throw.
|
|
448
|
+
|
|
340
449
|
### Retrieving data
|
|
341
450
|
URI is generated invoking the _generateUri_ method of the NgQubeeService. An observable is returned and the URI will be emitted:
|
|
342
451
|
|