ng-qubee 2.1.0 → 3.1.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 +378 -54
- package/fesm2022/ng-qubee.mjs +1791 -419
- package/fesm2022/ng-qubee.mjs.map +1 -1
- package/package.json +4 -4
- package/types/ng-qubee.d.ts +1544 -0
- package/index.d.ts +0 -5
- package/lib/enums/sort.enum.d.ts +0 -4
- package/lib/errors/invalid-limit.error.d.ts +0 -3
- package/lib/errors/invalid-model-name.error.d.ts +0 -3
- package/lib/errors/invalid-page-number.error.d.ts +0 -3
- package/lib/errors/key-not-found.error.d.ts +0 -3
- package/lib/errors/unselectable-model.error.d.ts +0 -3
- package/lib/interfaces/config.interface.d.ts +0 -6
- package/lib/interfaces/fields.interface.d.ts +0 -3
- package/lib/interfaces/filters.interface.d.ts +0 -3
- package/lib/interfaces/nest-state.interface.d.ts +0 -4
- package/lib/interfaces/normalized.interface.d.ts +0 -3
- package/lib/interfaces/page.interface.d.ts +0 -2
- package/lib/interfaces/paginated-object.interface.d.ts +0 -3
- package/lib/interfaces/pagination-config.interface.d.ts +0 -14
- package/lib/interfaces/query-builder-config.interface.d.ts +0 -9
- package/lib/interfaces/query-builder-state.interface.d.ts +0 -13
- package/lib/interfaces/sort.interface.d.ts +0 -5
- package/lib/models/paginated-collection.d.ts +0 -30
- package/lib/models/query-builder-options.d.ts +0 -11
- package/lib/models/response-options.d.ts +0 -16
- package/lib/ng-qubee.module.d.ts +0 -9
- package/lib/provide-ngqubee.d.ts +0 -21
- package/lib/services/nest.service.d.ts +0 -182
- package/lib/services/ng-qubee.service.d.ts +0 -147
- package/lib/services/pagination.service.d.ts +0 -13
- package/public-api.d.ts +0 -20
package/README.md
CHANGED
|
@@ -15,6 +15,7 @@ NgQubee is a Query Builder for Angular. Easily compose your API requests without
|
|
|
15
15
|
- Pagination ready
|
|
16
16
|
- Reactive, as the results are emitted with a RxJS Observable
|
|
17
17
|
- Developed with a test-driven approach
|
|
18
|
+
- **Multi-driver support**: JSON:API, Laravel (pagination-only), Spatie Query Builder, and NestJS (nestjs-paginate)
|
|
18
19
|
|
|
19
20
|
## We love it, we use it ❤️
|
|
20
21
|
NgQubee uses some open source projects to work properly:
|
|
@@ -38,25 +39,88 @@ Install NgQubee via NPM
|
|
|
38
39
|
npm i ng-qubee
|
|
39
40
|
```
|
|
40
41
|
|
|
42
|
+
## Drivers
|
|
43
|
+
|
|
44
|
+
NgQubee supports four drivers out of the box. A driver **must** be specified in the configuration:
|
|
45
|
+
|
|
46
|
+
| Driver | Backend | Request Format | Response Format |
|
|
47
|
+
|---|---|---|---|
|
|
48
|
+
| **JSON:API** | Any JSON:API-compliant backend | `filter[field]=value`, `sort=-field`, `page[number]=N&page[size]=N` | Nested: `{ data, meta: {...}, links: {...} }` |
|
|
49
|
+
| **Laravel** | Plain Laravel pagination | `limit=N&page=N` (pagination only) | Flat: `{ data, current_page, total, ... }` |
|
|
50
|
+
| **Spatie** | Spatie Query Builder | `filter[field]=value`, `sort=-field` | Flat: `{ data, current_page, total, ... }` |
|
|
51
|
+
| **NestJS** | nestjs-paginate | `filter.field=$operator:value`, `sortBy=field:DESC` | Nested: `{ data, meta: {...}, links: {...} }` |
|
|
52
|
+
|
|
41
53
|
## Usage
|
|
42
|
-
|
|
54
|
+
|
|
55
|
+
### JSON:API Driver
|
|
56
|
+
|
|
57
|
+
The JSON:API driver generates URIs compatible with any [JSON:API](https://jsonapi.org/format/)-compliant backend (Rails, Django, .NET, Java, Elixir, etc.):
|
|
43
58
|
|
|
44
59
|
```typescript
|
|
60
|
+
import { DriverEnum } from 'ng-qubee';
|
|
61
|
+
|
|
62
|
+
// Standalone approach
|
|
63
|
+
bootstrapApplication(AppComponent, {
|
|
64
|
+
providers: [provideNgQubee({ driver: DriverEnum.JSON_API })]
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// Module approach
|
|
45
68
|
@NgModule({
|
|
46
69
|
imports: [
|
|
47
|
-
NgQubeeModule.forRoot({
|
|
70
|
+
NgQubeeModule.forRoot({ driver: DriverEnum.JSON_API })
|
|
48
71
|
]
|
|
49
72
|
})
|
|
50
|
-
export class AppModule
|
|
73
|
+
export class AppModule {}
|
|
51
74
|
```
|
|
52
75
|
|
|
53
|
-
|
|
76
|
+
The JSON:API driver supports:
|
|
77
|
+
|
|
78
|
+
- **Filters** are composed as `filter[field]=value`
|
|
79
|
+
- **Fields** are composed as `fields[type]=col1,col2`
|
|
80
|
+
- **Includes** are composed as `include=author,comments.author`
|
|
81
|
+
- **Sort** is composed as `sort=-created_at,name` (`-` prefix = DESC)
|
|
82
|
+
- **Pagination** uses bracket notation: `page[number]=1&page[size]=15`
|
|
83
|
+
|
|
84
|
+
### Laravel Driver (pagination-only)
|
|
85
|
+
|
|
86
|
+
The Laravel driver provides basic pagination — limit and page parameters only. No filters, sorts, fields, or includes are supported.
|
|
87
|
+
|
|
54
88
|
```typescript
|
|
55
|
-
|
|
89
|
+
import { DriverEnum } from 'ng-qubee';
|
|
56
90
|
|
|
91
|
+
// Standalone approach
|
|
57
92
|
bootstrapApplication(AppComponent, {
|
|
58
|
-
providers: [provideNgQubee(
|
|
93
|
+
providers: [provideNgQubee({ driver: DriverEnum.LARAVEL })]
|
|
59
94
|
});
|
|
95
|
+
|
|
96
|
+
// Module approach
|
|
97
|
+
@NgModule({
|
|
98
|
+
imports: [
|
|
99
|
+
NgQubeeModule.forRoot({ driver: DriverEnum.LARAVEL })
|
|
100
|
+
]
|
|
101
|
+
})
|
|
102
|
+
export class AppModule {}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Spatie Driver
|
|
106
|
+
|
|
107
|
+
The Spatie driver generates URIs compatible with [Spatie Laravel Query Builder](https://spatie.be/docs/laravel-query-builder):
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
import { DriverEnum } from 'ng-qubee';
|
|
111
|
+
|
|
112
|
+
// Standalone approach
|
|
113
|
+
bootstrapApplication(AppComponent, {
|
|
114
|
+
providers: [provideNgQubee({ driver: DriverEnum.SPATIE })]
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// Module approach
|
|
118
|
+
@NgModule({
|
|
119
|
+
imports: [
|
|
120
|
+
NgQubeeModule.forRoot({ driver: DriverEnum.SPATIE })
|
|
121
|
+
]
|
|
122
|
+
})
|
|
123
|
+
export class AppModule {}
|
|
60
124
|
```
|
|
61
125
|
|
|
62
126
|
The object given to the _forRoot_ method allows to customize the query param keys. Following, the default behaviour:
|
|
@@ -72,6 +136,7 @@ As you can easily imagine, everything that regards the URI composition is placed
|
|
|
72
136
|
|
|
73
137
|
```typescript
|
|
74
138
|
NgQubeeModule.forRoot({
|
|
139
|
+
driver: DriverEnum.SPATIE,
|
|
75
140
|
request: {
|
|
76
141
|
filters: 'custom-filter-key',
|
|
77
142
|
fields: 'custom-fields-key',
|
|
@@ -80,6 +145,39 @@ NgQubeeModule.forRoot({
|
|
|
80
145
|
})
|
|
81
146
|
```
|
|
82
147
|
|
|
148
|
+
### NestJS Driver
|
|
149
|
+
|
|
150
|
+
To use the NestJS driver, specify the driver in your configuration:
|
|
151
|
+
|
|
152
|
+
```typescript
|
|
153
|
+
import { DriverEnum } from 'ng-qubee';
|
|
154
|
+
|
|
155
|
+
// Standalone approach
|
|
156
|
+
bootstrapApplication(AppComponent, {
|
|
157
|
+
providers: [provideNgQubee({ driver: DriverEnum.NESTJS })]
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// Module approach
|
|
161
|
+
@NgModule({
|
|
162
|
+
imports: [
|
|
163
|
+
NgQubeeModule.forRoot({ driver: DriverEnum.NESTJS })
|
|
164
|
+
]
|
|
165
|
+
})
|
|
166
|
+
export class AppModule {}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
The NestJS driver generates URIs compatible with [nestjs-paginate](https://github.com/ppetzold/nestjs-paginate):
|
|
170
|
+
|
|
171
|
+
- **Filters** are composed as `filter.field=value`
|
|
172
|
+
- **Filter operators** are composed as `filter.field=$operator:value`
|
|
173
|
+
- **Sorts** are composed as `sortBy=field:ASC,field2:DESC`
|
|
174
|
+
- **Select** is composed as `select=col1,col2`
|
|
175
|
+
- **Search** is composed as `search=term`
|
|
176
|
+
- **Limit** is composed as `limit=15`
|
|
177
|
+
- **Page** is composed as `page=1`
|
|
178
|
+
|
|
179
|
+
## Query Builder API
|
|
180
|
+
|
|
83
181
|
For composing queries, the first step is to inject the proper NgQubeeService:
|
|
84
182
|
|
|
85
183
|
```typescript
|
|
@@ -89,16 +187,16 @@ export class YourService {
|
|
|
89
187
|
}
|
|
90
188
|
```
|
|
91
189
|
|
|
92
|
-
Set the **
|
|
190
|
+
Set the **resource** to run the query against:
|
|
93
191
|
|
|
94
192
|
```typescript
|
|
95
|
-
this._ngQubeeService.
|
|
193
|
+
this._ngQubeeService.setResource('users');
|
|
96
194
|
```
|
|
97
195
|
|
|
98
196
|
This is necessary to build the prefix of the URI (/users)
|
|
99
197
|
|
|
100
198
|
|
|
101
|
-
### Fields
|
|
199
|
+
### Fields (JSON:API + Spatie)
|
|
102
200
|
Fields can be selected as following:
|
|
103
201
|
|
|
104
202
|
```typescript
|
|
@@ -107,14 +205,25 @@ this._ngQubeeService.addFields('users', ['id', 'email']);
|
|
|
107
205
|
|
|
108
206
|
Will output _/users?fields[users]=id,email_
|
|
109
207
|
|
|
110
|
-
###
|
|
208
|
+
### Select (NestJS only)
|
|
209
|
+
Flat field selection for the NestJS driver:
|
|
210
|
+
|
|
211
|
+
```typescript
|
|
212
|
+
this._ngQubeeService.addSelect('id', 'name', 'email');
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
Will output _/users?select=id,name,email_
|
|
216
|
+
|
|
217
|
+
### Filters (JSON:API + Spatie + NestJS)
|
|
111
218
|
Filters are applied as following:
|
|
112
219
|
|
|
113
220
|
```typescript
|
|
114
221
|
this._ngQubeeService.addFilter('id', 5);
|
|
115
222
|
```
|
|
116
223
|
|
|
117
|
-
Will output
|
|
224
|
+
Will output:
|
|
225
|
+
- Spatie: _/users?filter[id]=5_
|
|
226
|
+
- NestJS: _/users?filter.id=5_
|
|
118
227
|
|
|
119
228
|
Multiple values are allowed too:
|
|
120
229
|
|
|
@@ -122,11 +231,40 @@ Multiple values are allowed too:
|
|
|
122
231
|
this._ngQubeeService.addFilter('id', 5, 7, 10);
|
|
123
232
|
```
|
|
124
233
|
|
|
125
|
-
Will output
|
|
234
|
+
Will output:
|
|
235
|
+
- Spatie: _/users?filter[id]=5,7,10_
|
|
236
|
+
- NestJS: _/users?filter.id=5,7,10_
|
|
126
237
|
|
|
127
|
-
|
|
238
|
+
### Filter Operators (NestJS only)
|
|
239
|
+
The NestJS driver supports explicit filter operators:
|
|
128
240
|
|
|
129
|
-
|
|
241
|
+
```typescript
|
|
242
|
+
import { FilterOperatorEnum } from 'ng-qubee';
|
|
243
|
+
|
|
244
|
+
// Equality
|
|
245
|
+
this._ngQubeeService.addFilterOperator('status', FilterOperatorEnum.EQ, 'active');
|
|
246
|
+
// Output: filter.status=$eq:active
|
|
247
|
+
|
|
248
|
+
// Greater than or equal
|
|
249
|
+
this._ngQubeeService.addFilterOperator('age', FilterOperatorEnum.GTE, 18);
|
|
250
|
+
// Output: filter.age=$gte:18
|
|
251
|
+
|
|
252
|
+
// In (multiple values)
|
|
253
|
+
this._ngQubeeService.addFilterOperator('id', FilterOperatorEnum.IN, 1, 2, 3);
|
|
254
|
+
// Output: filter.id=$in:1,2,3
|
|
255
|
+
|
|
256
|
+
// Between
|
|
257
|
+
this._ngQubeeService.addFilterOperator('price', FilterOperatorEnum.BTW, 10, 100);
|
|
258
|
+
// Output: filter.price=$btw:10,100
|
|
259
|
+
|
|
260
|
+
// Case-insensitive like
|
|
261
|
+
this._ngQubeeService.addFilterOperator('name', FilterOperatorEnum.ILIKE, 'john');
|
|
262
|
+
// Output: filter.name=$ilike:john
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
**Available operators:** `$eq`, `$not`, `$null`, `$in`, `$gt`, `$gte`, `$lt`, `$lte`, `$btw`, `$ilike`, `$sw`, `$contains`
|
|
266
|
+
|
|
267
|
+
### Includes (JSON:API + Spatie)
|
|
130
268
|
Ask to include related models with:
|
|
131
269
|
|
|
132
270
|
```typescript
|
|
@@ -135,7 +273,16 @@ this._ngQubeeService.addIncludes('profile', 'settings');
|
|
|
135
273
|
|
|
136
274
|
Will output _/users?include=profile,settings_
|
|
137
275
|
|
|
138
|
-
###
|
|
276
|
+
### Search (NestJS only)
|
|
277
|
+
Full-text search for the NestJS driver:
|
|
278
|
+
|
|
279
|
+
```typescript
|
|
280
|
+
this._ngQubeeService.setSearch('john doe');
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
Will output _/users?search=john doe_
|
|
284
|
+
|
|
285
|
+
### Sort (JSON:API + Spatie + NestJS)
|
|
139
286
|
Sort elements as following:
|
|
140
287
|
|
|
141
288
|
```typescript
|
|
@@ -144,7 +291,9 @@ import { SortEnum } from 'ng-qubee';
|
|
|
144
291
|
this._ngQubeeService.addSort('fieldName', SortEnum.ASC);
|
|
145
292
|
```
|
|
146
293
|
|
|
147
|
-
Will output
|
|
294
|
+
Will output:
|
|
295
|
+
- Spatie: _/users?sort=fieldName_ (or _/users?sort=-fieldName_ if DESC)
|
|
296
|
+
- NestJS: _/users?sortBy=fieldName:ASC_ (or _/users?sortBy=fieldName:DESC_ if DESC)
|
|
148
297
|
|
|
149
298
|
The `SortEnum` provides two ordering options:
|
|
150
299
|
- `SortEnum.ASC` - Ascending order
|
|
@@ -166,6 +315,28 @@ Default values are automatically added to the query:
|
|
|
166
315
|
|
|
167
316
|
Always expect your query to include _limit=15&page=1_
|
|
168
317
|
|
|
318
|
+
#### Fetch all (NestJS only)
|
|
319
|
+
|
|
320
|
+
When the active driver is NestJS, `setLimit(-1)` is accepted as a "fetch all items" sentinel, following the [nestjs-paginate](https://github.com/ppetzold/nestjs-paginate) convention (the server must opt in via `maxLimit: -1`):
|
|
321
|
+
|
|
322
|
+
```typescript
|
|
323
|
+
// NestJS driver only
|
|
324
|
+
this._ngQubeeService.setLimit(-1);
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
JSON:API, Laravel, and Spatie drivers reject `-1` and throw `InvalidLimitError`.
|
|
328
|
+
|
|
329
|
+
#### Limit validation
|
|
330
|
+
|
|
331
|
+
Limit validation is driver-scoped — each request strategy enforces its own accepted range and invalid values throw `InvalidLimitError` immediately when passed to `setLimit()`:
|
|
332
|
+
|
|
333
|
+
| Driver | Accepted limit values |
|
|
334
|
+
|---|---|
|
|
335
|
+
| NestJS | integer `-1` (fetch all) or `>= 1` |
|
|
336
|
+
| JSON:API / Laravel / Spatie | integer `>= 1` |
|
|
337
|
+
|
|
338
|
+
Non-integer values, zero, negative numbers (other than `-1` for NestJS), `NaN`, and `Infinity` are all rejected.
|
|
339
|
+
|
|
169
340
|
### Retrieving data
|
|
170
341
|
URI is generated invoking the _generateUri_ method of the NgQubeeService. An observable is returned and the URI will be emitted:
|
|
171
342
|
|
|
@@ -173,13 +344,47 @@ URI is generated invoking the _generateUri_ method of the NgQubeeService. An obs
|
|
|
173
344
|
this._ngQubeeService.generateUri().subscribe(uri => console.log(uri));
|
|
174
345
|
```
|
|
175
346
|
|
|
347
|
+
### Deleting State
|
|
348
|
+
|
|
349
|
+
All query features have corresponding delete methods:
|
|
350
|
+
|
|
351
|
+
```typescript
|
|
352
|
+
// JSON:API + Spatie + NestJS
|
|
353
|
+
this._ngQubeeService.deleteFilters('status', 'role');
|
|
354
|
+
this._ngQubeeService.deleteSorts('created_at');
|
|
355
|
+
|
|
356
|
+
// JSON:API + Spatie only
|
|
357
|
+
this._ngQubeeService.deleteFields({ users: ['email'] });
|
|
358
|
+
this._ngQubeeService.deleteFieldsByModel('users', 'email');
|
|
359
|
+
this._ngQubeeService.deleteIncludes('profile');
|
|
360
|
+
|
|
361
|
+
// NestJS only
|
|
362
|
+
this._ngQubeeService.deleteOperatorFilters('age');
|
|
363
|
+
this._ngQubeeService.deleteSelect('email');
|
|
364
|
+
this._ngQubeeService.deleteSearch();
|
|
365
|
+
```
|
|
366
|
+
|
|
176
367
|
### Reset state
|
|
177
|
-
Query Builder state can be cleaned with the reset method. This will clean up everything set up previously, including the current
|
|
368
|
+
Query Builder state can be cleaned with the reset method. This will clean up everything set up previously, including the current resource, filters, includes and so on...
|
|
178
369
|
|
|
179
370
|
```typescript
|
|
180
371
|
this._ngQubeeService.reset();
|
|
181
372
|
```
|
|
182
373
|
|
|
374
|
+
### Driver Validation
|
|
375
|
+
|
|
376
|
+
Calling a method that is not supported by the active driver throws a descriptive error immediately:
|
|
377
|
+
|
|
378
|
+
| Method | JSON:API | Laravel | Spatie | NestJS |
|
|
379
|
+
|---|---|---|---|---|
|
|
380
|
+
| `addFilter()` / `deleteFilters()` | supported | throws `UnsupportedFilterError` | supported | supported |
|
|
381
|
+
| `addSort()` / `deleteSorts()` | supported | throws `UnsupportedSortError` | supported | supported |
|
|
382
|
+
| `addFields()` / `deleteFields()` / `deleteFieldsByModel()` | supported | throws `UnsupportedFieldSelectionError` | supported | throws `UnsupportedFieldSelectionError` |
|
|
383
|
+
| `addIncludes()` / `deleteIncludes()` | supported | throws `UnsupportedIncludesError` | supported | throws `UnsupportedIncludesError` |
|
|
384
|
+
| `addFilterOperator()` / `deleteOperatorFilters()` | throws `UnsupportedFilterOperatorError` | throws `UnsupportedFilterOperatorError` | throws `UnsupportedFilterOperatorError` | supported |
|
|
385
|
+
| `addSelect()` / `deleteSelect()` | throws `UnsupportedSelectError` | throws `UnsupportedSelectError` | throws `UnsupportedSelectError` | supported |
|
|
386
|
+
| `setSearch()` / `deleteSearch()` | throws `UnsupportedSearchError` | throws `UnsupportedSearchError` | throws `UnsupportedSearchError` | supported |
|
|
387
|
+
|
|
183
388
|
## Pagination
|
|
184
389
|
If you are working with an API that supports pagination, we have got you covered 😉 NgQubee provides:
|
|
185
390
|
- A PaginatedCollection class that holds paginated data
|
|
@@ -199,31 +404,93 @@ this._pg.paginate<Model>({ ...response, data: response.data.map(e => new Mo
|
|
|
199
404
|
|
|
200
405
|
The "paginate" method returns a PaginatedCollection that helps handling paginated data. Additionally, if you are dealing with a state library in your application, you can use the "normalize" method of the collection to normalize the data.
|
|
201
406
|
|
|
202
|
-
|
|
407
|
+
### Laravel / Spatie Response Format
|
|
408
|
+
|
|
409
|
+
When using the Laravel or Spatie driver, the paginated collection will check for the following keys in the response:
|
|
203
410
|
|
|
204
411
|
- data - the key that holds the response data
|
|
205
|
-
-
|
|
206
|
-
- from - Showing items from n
|
|
207
|
-
- to - Showing items
|
|
208
|
-
- total - Count of the items available in
|
|
209
|
-
-
|
|
210
|
-
-
|
|
211
|
-
-
|
|
212
|
-
-
|
|
213
|
-
-
|
|
214
|
-
-
|
|
215
|
-
|
|
216
|
-
|
|
412
|
+
- current_page - requested page for the pagination
|
|
413
|
+
- from - Showing items from n
|
|
414
|
+
- to - Showing items to n
|
|
415
|
+
- total - Count of the items available in the whole pagination
|
|
416
|
+
- per_page - Items per page
|
|
417
|
+
- prev_page_url - URL to the previous page
|
|
418
|
+
- next_page_url - URL to the next page
|
|
419
|
+
- last_page - Last page number
|
|
420
|
+
- first_page_url - URL to the first page
|
|
421
|
+
- last_page_url - URL to the last page
|
|
422
|
+
|
|
423
|
+
### JSON:API Response Format
|
|
424
|
+
|
|
425
|
+
When using the JSON:API driver, the PaginationService automatically parses nested responses:
|
|
426
|
+
|
|
427
|
+
```json
|
|
428
|
+
{
|
|
429
|
+
"data": [...],
|
|
430
|
+
"meta": {
|
|
431
|
+
"current-page": 1,
|
|
432
|
+
"per-page": 10,
|
|
433
|
+
"total": 100,
|
|
434
|
+
"page-count": 10
|
|
435
|
+
},
|
|
436
|
+
"links": {
|
|
437
|
+
"first": "http://api.com/articles?page[number]=1&page[size]=10",
|
|
438
|
+
"prev": null,
|
|
439
|
+
"next": "http://api.com/articles?page[number]=2&page[size]=10",
|
|
440
|
+
"last": "http://api.com/articles?page[number]=10&page[size]=10"
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
The `from` and `to` values are computed automatically from `current-page` and `per-page` when not present in the response. JSON:API meta key names vary by implementation; defaults can be fully customised via response configuration.
|
|
446
|
+
|
|
447
|
+
### NestJS Response Format
|
|
448
|
+
|
|
449
|
+
When using the NestJS driver, the PaginationService automatically parses nested responses:
|
|
450
|
+
|
|
451
|
+
```json
|
|
452
|
+
{
|
|
453
|
+
"data": [...],
|
|
454
|
+
"meta": {
|
|
455
|
+
"currentPage": 1,
|
|
456
|
+
"totalItems": 100,
|
|
457
|
+
"itemsPerPage": 10,
|
|
458
|
+
"totalPages": 10
|
|
459
|
+
},
|
|
460
|
+
"links": {
|
|
461
|
+
"first": "http://api.com/users?page=1",
|
|
462
|
+
"previous": null,
|
|
463
|
+
"next": "http://api.com/users?page=2",
|
|
464
|
+
"last": "http://api.com/users?page=10",
|
|
465
|
+
"current": "http://api.com/users?page=1"
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
The `from` and `to` values are computed automatically from `currentPage` and `itemsPerPage` when not present in the response.
|
|
471
|
+
|
|
472
|
+
### Customizing Response Keys
|
|
473
|
+
|
|
474
|
+
Just like the query builder, the pagination service supports customizable keys. While invoking the forRoot method of the module, use the response key to look for different keys in the API response:
|
|
217
475
|
|
|
218
476
|
```typescript
|
|
477
|
+
// Spatie
|
|
219
478
|
NgQubeeModule.forRoot({
|
|
479
|
+
driver: DriverEnum.SPATIE,
|
|
220
480
|
response: {
|
|
221
481
|
currentPage: 'pg'
|
|
222
482
|
}
|
|
223
483
|
})
|
|
224
|
-
```
|
|
225
484
|
|
|
226
|
-
|
|
485
|
+
// NestJS (use dot-notation for nested paths)
|
|
486
|
+
NgQubeeModule.forRoot({
|
|
487
|
+
driver: DriverEnum.NESTJS,
|
|
488
|
+
response: {
|
|
489
|
+
currentPage: 'pagination.page',
|
|
490
|
+
total: 'pagination.total'
|
|
491
|
+
}
|
|
492
|
+
})
|
|
493
|
+
```
|
|
227
494
|
|
|
228
495
|
## TypeScript Support
|
|
229
496
|
|
|
@@ -232,11 +499,31 @@ NgQubee is fully typed and exports all public interfaces, enums, and types for T
|
|
|
232
499
|
### Available Enums
|
|
233
500
|
|
|
234
501
|
```typescript
|
|
235
|
-
import { SortEnum } from 'ng-qubee';
|
|
502
|
+
import { DriverEnum, FilterOperatorEnum, SortEnum } from 'ng-qubee';
|
|
503
|
+
|
|
504
|
+
// Driver options
|
|
505
|
+
DriverEnum.JSON_API // 'json-api'
|
|
506
|
+
DriverEnum.LARAVEL // 'laravel' (pagination only)
|
|
507
|
+
DriverEnum.SPATIE // 'spatie'
|
|
508
|
+
DriverEnum.NESTJS // 'nestjs'
|
|
236
509
|
|
|
237
510
|
// Sorting options
|
|
238
511
|
SortEnum.ASC // 'asc'
|
|
239
512
|
SortEnum.DESC // 'desc'
|
|
513
|
+
|
|
514
|
+
// Filter operators (NestJS only)
|
|
515
|
+
FilterOperatorEnum.EQ // '$eq'
|
|
516
|
+
FilterOperatorEnum.NOT // '$not'
|
|
517
|
+
FilterOperatorEnum.NULL // '$null'
|
|
518
|
+
FilterOperatorEnum.IN // '$in'
|
|
519
|
+
FilterOperatorEnum.GT // '$gt'
|
|
520
|
+
FilterOperatorEnum.GTE // '$gte'
|
|
521
|
+
FilterOperatorEnum.LT // '$lt'
|
|
522
|
+
FilterOperatorEnum.LTE // '$lte'
|
|
523
|
+
FilterOperatorEnum.BTW // '$btw'
|
|
524
|
+
FilterOperatorEnum.ILIKE // '$ilike'
|
|
525
|
+
FilterOperatorEnum.SW // '$sw'
|
|
526
|
+
FilterOperatorEnum.CONTAINS // '$contains'
|
|
240
527
|
```
|
|
241
528
|
|
|
242
529
|
### Available Interfaces
|
|
@@ -252,8 +539,9 @@ import {
|
|
|
252
539
|
IPaginationConfig
|
|
253
540
|
} from 'ng-qubee';
|
|
254
541
|
|
|
255
|
-
// Main configuration interface
|
|
542
|
+
// Main configuration interface (driver is required)
|
|
256
543
|
const config: IConfig = {
|
|
544
|
+
driver: DriverEnum.NESTJS,
|
|
257
545
|
request: {
|
|
258
546
|
filters: 'custom-filter-key',
|
|
259
547
|
fields: 'custom-fields-key',
|
|
@@ -277,7 +565,8 @@ const config: IConfig = {
|
|
|
277
565
|
import {
|
|
278
566
|
IFilters,
|
|
279
567
|
IFields,
|
|
280
|
-
ISort
|
|
568
|
+
ISort,
|
|
569
|
+
IOperatorFilter
|
|
281
570
|
} from 'ng-qubee';
|
|
282
571
|
|
|
283
572
|
// Filters interface - key-value pairs with array values
|
|
@@ -297,30 +586,25 @@ const sort: ISort = {
|
|
|
297
586
|
field: 'created_at',
|
|
298
587
|
order: SortEnum.DESC
|
|
299
588
|
};
|
|
589
|
+
|
|
590
|
+
// Operator filter interface (NestJS only)
|
|
591
|
+
const operatorFilter: IOperatorFilter = {
|
|
592
|
+
field: 'age',
|
|
593
|
+
operator: FilterOperatorEnum.GTE,
|
|
594
|
+
values: [18]
|
|
595
|
+
};
|
|
300
596
|
```
|
|
301
597
|
|
|
302
|
-
####
|
|
598
|
+
#### Strategy Interfaces
|
|
303
599
|
|
|
304
600
|
```typescript
|
|
305
|
-
import {
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
id: number;
|
|
310
|
-
email: string;
|
|
311
|
-
username: string;
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
// The paginated object can contain any additional keys
|
|
315
|
-
const paginatedData: IPaginatedObject = {
|
|
316
|
-
data: [/* users */],
|
|
317
|
-
currentPage: 1,
|
|
318
|
-
total: 100,
|
|
319
|
-
perPage: 15
|
|
320
|
-
};
|
|
601
|
+
import {
|
|
602
|
+
IRequestStrategy,
|
|
603
|
+
IResponseStrategy
|
|
604
|
+
} from 'ng-qubee';
|
|
321
605
|
```
|
|
322
606
|
|
|
323
|
-
### Usage Example
|
|
607
|
+
### Spatie Usage Example
|
|
324
608
|
|
|
325
609
|
```typescript
|
|
326
610
|
import { Component, OnInit } from '@angular/core';
|
|
@@ -340,7 +624,7 @@ export class UsersComponent implements OnInit {
|
|
|
340
624
|
|
|
341
625
|
ngOnInit(): void {
|
|
342
626
|
// Set up the query with type safety
|
|
343
|
-
this.ngQubee.
|
|
627
|
+
this.ngQubee.setResource('users');
|
|
344
628
|
|
|
345
629
|
// Define fields with type checking
|
|
346
630
|
const userFields: IFields = {
|
|
@@ -368,6 +652,46 @@ export class UsersComponent implements OnInit {
|
|
|
368
652
|
}
|
|
369
653
|
```
|
|
370
654
|
|
|
655
|
+
### NestJS Usage Example
|
|
656
|
+
|
|
657
|
+
```typescript
|
|
658
|
+
import { Component, OnInit } from '@angular/core';
|
|
659
|
+
import {
|
|
660
|
+
NgQubeeService,
|
|
661
|
+
PaginationService,
|
|
662
|
+
FilterOperatorEnum,
|
|
663
|
+
SortEnum
|
|
664
|
+
} from 'ng-qubee';
|
|
665
|
+
|
|
666
|
+
@Component({
|
|
667
|
+
selector: 'app-users',
|
|
668
|
+
template: '...'
|
|
669
|
+
})
|
|
670
|
+
export class UsersComponent implements OnInit {
|
|
671
|
+
constructor(
|
|
672
|
+
private ngQubee: NgQubeeService,
|
|
673
|
+
private pagination: PaginationService
|
|
674
|
+
) {}
|
|
675
|
+
|
|
676
|
+
ngOnInit(): void {
|
|
677
|
+
this.ngQubee
|
|
678
|
+
.setResource('users')
|
|
679
|
+
.addFilterOperator('age', FilterOperatorEnum.GTE, 18)
|
|
680
|
+
.addFilter('status', 'active')
|
|
681
|
+
.addSelect('id', 'name', 'email')
|
|
682
|
+
.addSort('name', SortEnum.ASC)
|
|
683
|
+
.setSearch('john')
|
|
684
|
+
.setLimit(10)
|
|
685
|
+
.setPage(1);
|
|
686
|
+
|
|
687
|
+
this.ngQubee.generateUri().subscribe(uri => {
|
|
688
|
+
console.log(uri);
|
|
689
|
+
// Output: /users?filter.status=active&filter.age=$gte:18&sortBy=name:ASC&select=id,name,email&search=john&limit=10&page=1
|
|
690
|
+
});
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
```
|
|
694
|
+
|
|
371
695
|
[ng-qubee]: <https://github.com/AndreaAlhena/ng-qubee>
|
|
372
696
|
[rxjs]: <https://reactivex.io>
|
|
373
697
|
[qs]: <https://github.com/ljharb/qs>
|