ng-firebase-table-kxp 1.1.8 → 2.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 +212 -218
- package/fesm2022/ng-firebase-table-kxp.mjs +2790 -0
- package/fesm2022/ng-firebase-table-kxp.mjs.map +1 -0
- package/index.d.ts +634 -3
- package/index.d.ts.map +1 -0
- package/package.json +13 -21
- package/esm2020/lib/components/table/table.component.mjs +0 -1250
- package/esm2020/lib/ng-firebase-table-kxp.component.mjs +0 -11
- package/esm2020/lib/ng-firebase-table-kxp.module.mjs +0 -77
- package/esm2020/lib/ng-firebase-table-kxp.service.mjs +0 -14
- package/esm2020/lib/services/table.service.mjs +0 -1086
- package/esm2020/lib/types/Table.mjs +0 -9
- package/esm2020/ng-firebase-table-kxp.mjs +0 -5
- package/esm2020/public-api.mjs +0 -15
- package/fesm2015/ng-firebase-table-kxp.mjs +0 -2473
- package/fesm2015/ng-firebase-table-kxp.mjs.map +0 -1
- package/fesm2020/ng-firebase-table-kxp.mjs +0 -2437
- package/fesm2020/ng-firebase-table-kxp.mjs.map +0 -1
- package/lib/components/table/table.component.d.ts +0 -146
- package/lib/ng-firebase-table-kxp.component.d.ts +0 -5
- package/lib/ng-firebase-table-kxp.module.d.ts +0 -21
- package/lib/ng-firebase-table-kxp.service.d.ts +0 -6
- package/lib/services/table.service.d.ts +0 -74
- package/lib/types/Table.d.ts +0 -152
- package/public-api.d.ts +0 -6
package/README.md
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
NG Firebase Table KXP is a production-ready Angular data table created by **KXP Tech** to power enterprise dashboards backed by Firebase Firestore. It wraps complex Firestore querying, pagination, filtering, and material design styling into a plug-and-play module so teams can ship data-rich experiences faster.
|
|
6
6
|
|
|
7
7
|
## Table of Contents
|
|
8
|
+
|
|
8
9
|
- [NG Firebase Table KXP](#ng-firebase-table-kxp)
|
|
9
10
|
- [Table of Contents](#table-of-contents)
|
|
10
11
|
- [Why Firebase Table KXP](#why-firebase-table-kxp)
|
|
@@ -61,47 +62,35 @@ The host application should already depend on:
|
|
|
61
62
|
- Firebase stack: `@angular/fire` (7.x) and `firebase` (9.x)
|
|
62
63
|
- Utilities: `ngx-toastr` (16.x), `moment` (2.29.x)
|
|
63
64
|
|
|
64
|
-
|
|
65
65
|
1. **Import the module**
|
|
66
|
+
|
|
66
67
|
```typescript
|
|
67
|
-
import { NgFirebaseTableKxpModule } from
|
|
68
|
+
import { NgFirebaseTableKxpModule } from "@adryanmmm/ng-firebase-table-kxp";
|
|
68
69
|
|
|
69
70
|
@NgModule({
|
|
70
|
-
imports: [
|
|
71
|
-
BrowserModule,
|
|
72
|
-
BrowserAnimationsModule,
|
|
73
|
-
AngularFireModule.initializeApp(environment.firebase),
|
|
74
|
-
AngularFirestoreModule,
|
|
75
|
-
ToastrModule.forRoot(),
|
|
76
|
-
NgxMaskModule.forRoot(),
|
|
77
|
-
NgFirebaseTableKxpModule
|
|
78
|
-
]
|
|
71
|
+
imports: [BrowserModule, BrowserAnimationsModule, AngularFireModule.initializeApp(environment.firebase), AngularFirestoreModule, ToastrModule.forRoot(), NgxMaskModule.forRoot(), NgFirebaseTableKxpModule],
|
|
79
72
|
})
|
|
80
73
|
export class AppModule {}
|
|
81
74
|
```
|
|
82
75
|
|
|
83
76
|
2. **Drop the component**
|
|
77
|
+
|
|
84
78
|
```typescript
|
|
85
79
|
@Component({
|
|
86
|
-
selector:
|
|
87
|
-
template: `
|
|
88
|
-
<lib-table
|
|
89
|
-
[data]="table"
|
|
90
|
-
[downloadTable]="downloadTable">
|
|
91
|
-
</lib-table>
|
|
92
|
-
`
|
|
80
|
+
selector: "app-users",
|
|
81
|
+
template: ` <lib-table [data]="table" [downloadTable]="downloadTable"> </lib-table> `,
|
|
93
82
|
})
|
|
94
83
|
export class UsersComponent {
|
|
95
84
|
table: TableData = {
|
|
96
|
-
name:
|
|
97
|
-
collection:
|
|
98
|
-
collectionRef: this.firestore.collection(
|
|
85
|
+
name: "users",
|
|
86
|
+
collection: "users",
|
|
87
|
+
collectionRef: this.firestore.collection("users").ref,
|
|
99
88
|
pagination: true,
|
|
100
89
|
download: true,
|
|
101
90
|
displayedColumns: [
|
|
102
|
-
{ property:
|
|
103
|
-
{ property:
|
|
104
|
-
]
|
|
91
|
+
{ property: "name", title: "Name", isFilterable: true, isSortable: true },
|
|
92
|
+
{ property: "email", title: "Email", isFilterable: true },
|
|
93
|
+
],
|
|
105
94
|
};
|
|
106
95
|
|
|
107
96
|
downloadTable = (arrange: any, conditions: any[]) => {
|
|
@@ -113,9 +102,10 @@ export class UsersComponent {
|
|
|
113
102
|
```
|
|
114
103
|
|
|
115
104
|
3. **Make sure global styles include**
|
|
105
|
+
|
|
116
106
|
```scss
|
|
117
|
-
@import
|
|
118
|
-
@import
|
|
107
|
+
@import "@angular/material/prebuilt-themes/indigo-pink.css";
|
|
108
|
+
@import "ngx-toastr/toastr";
|
|
119
109
|
```
|
|
120
110
|
|
|
121
111
|
## Configuration Overview
|
|
@@ -136,26 +126,27 @@ This section provides detailed documentation for all configuration interfaces av
|
|
|
136
126
|
|
|
137
127
|
The main configuration object that defines the entire table behavior.
|
|
138
128
|
|
|
139
|
-
| Property
|
|
140
|
-
|
|
141
|
-
| `name`
|
|
142
|
-
| `collection`
|
|
143
|
-
| `collectionRef`
|
|
144
|
-
| `displayedColumns`
|
|
145
|
-
| `pagination`
|
|
146
|
-
| `download`
|
|
147
|
-
| `sortBy`
|
|
148
|
-
| `conditions`
|
|
149
|
-
| `filterFn`
|
|
150
|
-
| `filterableOptions` | `FilterableOption[]`
|
|
151
|
-
| `url`
|
|
152
|
-
| `isNotClickable`
|
|
153
|
-
| `color`
|
|
154
|
-
| `totalRef`
|
|
155
|
-
| `actionButton`
|
|
156
|
-
| `tabs`
|
|
129
|
+
| Property | Type | Required | Description |
|
|
130
|
+
| ------------------- | ------------------------------------------- | -------- | ------------------------------------------------------------------------------------------ |
|
|
131
|
+
| `name` | `string` | ✅ | Unique identifier for the table. Used internally for caching and state management. |
|
|
132
|
+
| `collection` | `string` | ✅ | Name of the Firestore collection to query. |
|
|
133
|
+
| `collectionRef` | `CollectionReference` | ✅ | Reference to the Firestore collection. Use `firestore.collection('name').ref`. |
|
|
134
|
+
| `displayedColumns` | `Column[]` | ✅ | Array of column definitions. Defines what data to display and how. See [Column](#column). |
|
|
135
|
+
| `pagination` | `boolean` | ✅ | Enable or disable pagination. When `true`, data is loaded in batches. |
|
|
136
|
+
| `download` | `boolean` | ✅ | Enable or disable the export/download functionality. |
|
|
137
|
+
| `sortBy` | `{field: string, order: OrderByDirection}` | ❌ | Default sorting configuration. `order` can be `'asc'` or `'desc'`. |
|
|
138
|
+
| `conditions` | `Condition[]` | ❌ | Array of Firestore where clauses applied before user filters. See [Condition](#condition). |
|
|
139
|
+
| `filterFn` | `(item: any) => boolean` | ❌ | Custom client-side filter function. Applied after Firestore queries. |
|
|
140
|
+
| `filterableOptions` | `FilterableOption[]` | ❌ | Predefined filter options displayed as buttons. See [FilterableOption](#filterableoption). |
|
|
141
|
+
| `url` | `string` | ❌ | Base URL for row navigation. Row clicks navigate to `{url}/{row.id}`. |
|
|
142
|
+
| `isNotClickable` | `boolean` | ❌ | When `true`, disables row click navigation. |
|
|
143
|
+
| `color` | `{bg: string, text: string}` | ❌ | CSS classes for custom table theming. `bg` for background, `text` for text color. |
|
|
144
|
+
| `totalRef` | `{ref: DocumentReference, field: string}[]` | ❌ | References to documents containing total counts. Displayed in the table footer. |
|
|
145
|
+
| `actionButton` | `ActionButton` | ❌ | Configuration for a primary action button (e.g., "Add New"). |
|
|
146
|
+
| `tabs` | `Tab` | ❌ | Configuration for tabbed data views. See [Tab & TabData](#tab--tabdata). |
|
|
157
147
|
|
|
158
148
|
**ActionButton Properties:**
|
|
149
|
+
|
|
159
150
|
- `label` (string): Button text
|
|
160
151
|
- `routerLink` (string): Navigation route
|
|
161
152
|
- `icon` (string): CSS class for icon (e.g., FontAwesome)
|
|
@@ -164,24 +155,25 @@ The main configuration object that defines the entire table behavior.
|
|
|
164
155
|
- `condition` ((row) => boolean): Function to conditionally show the button
|
|
165
156
|
|
|
166
157
|
**Example:**
|
|
158
|
+
|
|
167
159
|
```typescript
|
|
168
160
|
const tableData: TableData = {
|
|
169
|
-
name:
|
|
170
|
-
collection:
|
|
171
|
-
collectionRef: this.firestore.collection(
|
|
161
|
+
name: "products",
|
|
162
|
+
collection: "products",
|
|
163
|
+
collectionRef: this.firestore.collection("products").ref,
|
|
172
164
|
pagination: true,
|
|
173
165
|
download: true,
|
|
174
|
-
sortBy: { field:
|
|
175
|
-
conditions: [
|
|
176
|
-
|
|
166
|
+
sortBy: { field: "createdAt", order: "desc" },
|
|
167
|
+
conditions: [{ operator: "==", firestoreProperty: "active", dashProperty: true }],
|
|
168
|
+
displayedColumns: [
|
|
169
|
+
/* ... */
|
|
177
170
|
],
|
|
178
|
-
displayedColumns: [/* ... */],
|
|
179
171
|
actionButton: {
|
|
180
|
-
label:
|
|
181
|
-
routerLink:
|
|
182
|
-
icon:
|
|
183
|
-
colorClass:
|
|
184
|
-
}
|
|
172
|
+
label: "Add Product",
|
|
173
|
+
routerLink: "/products/new",
|
|
174
|
+
icon: "fa fa-plus",
|
|
175
|
+
colorClass: "bg-blue-500",
|
|
176
|
+
},
|
|
185
177
|
};
|
|
186
178
|
```
|
|
187
179
|
|
|
@@ -191,89 +183,93 @@ const tableData: TableData = {
|
|
|
191
183
|
|
|
192
184
|
Defines how each column is displayed, filtered, and interacted with.
|
|
193
185
|
|
|
194
|
-
| Property
|
|
195
|
-
|
|
196
|
-
| `property`
|
|
197
|
-
| `title`
|
|
198
|
-
| `charLimit`
|
|
199
|
-
| `pipe`
|
|
200
|
-
| `calculateValue`
|
|
201
|
-
| `isSortable`
|
|
202
|
-
| `isFilterable`
|
|
203
|
-
| `isFilterableByDate` | `boolean`
|
|
204
|
-
| `filterPredicates`
|
|
205
|
-
| `hasLink`
|
|
206
|
-
| `hasDownload`
|
|
207
|
-
| `arrayField`
|
|
208
|
-
| `method`
|
|
209
|
-
| `image`
|
|
210
|
-
| `iconClass`
|
|
211
|
-
| `relation`
|
|
212
|
-
| `queryLength`
|
|
186
|
+
| Property | Type | Required | Description |
|
|
187
|
+
| -------------------- | -------------------------------- | -------- | ----------------------------------------------------------------------------- |
|
|
188
|
+
| `property` | `string` | ✅ | Name of the property in the data object to display. |
|
|
189
|
+
| `title` | `string` | ❌ | Column header text. Defaults to `property` if not provided. |
|
|
190
|
+
| `charLimit` | `number` | ❌ | Maximum characters to display. Shows tooltip with full text on hover. |
|
|
191
|
+
| `pipe` | `PipeTransform` | ❌ | Angular pipe for formatting values (e.g., `DatePipe`, `CurrencyPipe`). |
|
|
192
|
+
| `calculateValue` | `(row: any) => any` | ❌ | Function to compute the display value from the row data. |
|
|
193
|
+
| `isSortable` | `boolean` | ❌ | Enable sorting for this column. |
|
|
194
|
+
| `isFilterable` | `boolean` | ❌ | Enable text-based filtering for this column. |
|
|
195
|
+
| `isFilterableByDate` | `boolean` | ❌ | Enable date range filtering. Shows date picker inputs. |
|
|
196
|
+
| `filterPredicates` | `string[]` | ❌ | Additional properties to include when filtering this column. |
|
|
197
|
+
| `hasLink` | `boolean \| string` | ❌ | Make the cell value a clickable link. If string, uses as the navigation path. |
|
|
198
|
+
| `hasDownload` | `boolean \| string` | ❌ | Add download icon to the cell. If string, uses as the download URL property. |
|
|
199
|
+
| `arrayField` | `string` | ❌ | When data is an array of objects, specify which field to display. |
|
|
200
|
+
| `method` | `(row: any, event?: any) => any` | ❌ | Custom click handler for the cell. |
|
|
201
|
+
| `image` | `Image` | ❌ | Configuration for displaying images. See [Image](#image). |
|
|
202
|
+
| `iconClass` | `IconClass[]` | ❌ | Array of icon/button configurations for action buttons in the cell. |
|
|
203
|
+
| `relation` | `Relation` | ❌ | Configuration for joining related collection data. |
|
|
204
|
+
| `queryLength` | `QueryLength` | ❌ | Configuration for counting related documents. |
|
|
213
205
|
|
|
214
206
|
**IconClass Properties:**
|
|
207
|
+
|
|
215
208
|
- `class` (string): CSS classes for the icon/button
|
|
216
209
|
- `text` (string): Text to display alongside icon
|
|
217
210
|
- `condition` ((row) => any): Function to conditionally show the icon
|
|
218
211
|
- `buttonMethod` ((row, event) => any): Click handler for the icon
|
|
219
212
|
|
|
220
213
|
**Relation Properties:**
|
|
214
|
+
|
|
221
215
|
- `collection` (string): Name of the related collection
|
|
222
216
|
- `property` (string): Property in current row containing the related document ID
|
|
223
217
|
- `newProperty` (string): Property from related document to fetch and display
|
|
224
218
|
|
|
225
219
|
**QueryLength Properties:**
|
|
220
|
+
|
|
226
221
|
- `collection` (string): Collection to count documents from
|
|
227
222
|
- `property` (string): Field to match in the query
|
|
228
223
|
- `operator` (WhereFilterOp): Firestore operator (e.g., `'=='`, `'>'`)
|
|
229
224
|
- `value` (string): Property from current row to use as query value
|
|
230
225
|
|
|
231
226
|
**Example:**
|
|
227
|
+
|
|
232
228
|
```typescript
|
|
233
229
|
const columns: Column[] = [
|
|
234
230
|
{
|
|
235
|
-
property:
|
|
236
|
-
title:
|
|
231
|
+
property: "name",
|
|
232
|
+
title: "Product Name",
|
|
237
233
|
isFilterable: true,
|
|
238
234
|
isSortable: true,
|
|
239
|
-
charLimit: 50
|
|
235
|
+
charLimit: 50,
|
|
240
236
|
},
|
|
241
237
|
{
|
|
242
|
-
property:
|
|
243
|
-
title:
|
|
244
|
-
pipe: new CurrencyPipe(
|
|
245
|
-
isSortable: true
|
|
238
|
+
property: "price",
|
|
239
|
+
title: "Price",
|
|
240
|
+
pipe: new CurrencyPipe("en-US"),
|
|
241
|
+
isSortable: true,
|
|
246
242
|
},
|
|
247
243
|
{
|
|
248
|
-
property:
|
|
249
|
-
title:
|
|
250
|
-
pipe: new DatePipe(
|
|
251
|
-
isFilterableByDate: true
|
|
244
|
+
property: "createdAt",
|
|
245
|
+
title: "Created",
|
|
246
|
+
pipe: new DatePipe("en-US"),
|
|
247
|
+
isFilterableByDate: true,
|
|
252
248
|
},
|
|
253
249
|
{
|
|
254
|
-
property:
|
|
255
|
-
title:
|
|
250
|
+
property: "categoryName",
|
|
251
|
+
title: "Category",
|
|
256
252
|
relation: {
|
|
257
|
-
collection:
|
|
258
|
-
property:
|
|
259
|
-
newProperty:
|
|
260
|
-
}
|
|
253
|
+
collection: "categories",
|
|
254
|
+
property: "categoryId",
|
|
255
|
+
newProperty: "name",
|
|
256
|
+
},
|
|
261
257
|
},
|
|
262
258
|
{
|
|
263
|
-
property:
|
|
264
|
-
title:
|
|
259
|
+
property: "actions",
|
|
260
|
+
title: "Actions",
|
|
265
261
|
iconClass: [
|
|
266
262
|
{
|
|
267
|
-
class:
|
|
268
|
-
buttonMethod: (row) => this.editProduct(row)
|
|
263
|
+
class: "fa fa-edit text-blue-500 cursor-pointer",
|
|
264
|
+
buttonMethod: (row) => this.editProduct(row),
|
|
269
265
|
},
|
|
270
266
|
{
|
|
271
|
-
class:
|
|
267
|
+
class: "fa fa-trash text-red-500 cursor-pointer",
|
|
272
268
|
buttonMethod: (row) => this.deleteProduct(row),
|
|
273
|
-
condition: (row) => row.canDelete === true
|
|
274
|
-
}
|
|
275
|
-
]
|
|
276
|
-
}
|
|
269
|
+
condition: (row) => row.canDelete === true,
|
|
270
|
+
},
|
|
271
|
+
],
|
|
272
|
+
},
|
|
277
273
|
];
|
|
278
274
|
```
|
|
279
275
|
|
|
@@ -285,37 +281,34 @@ Configure tabbed views for switching between different data sets within the same
|
|
|
285
281
|
|
|
286
282
|
**Tab Interface:**
|
|
287
283
|
|
|
288
|
-
| Property
|
|
289
|
-
|
|
290
|
-
| `method`
|
|
291
|
-
| `tabsData` | `TabData[]`
|
|
284
|
+
| Property | Type | Required | Description |
|
|
285
|
+
| ---------- | -------------------------------- | -------- | --------------------------------------------------------------------------- |
|
|
286
|
+
| `method` | `(tab: any, event?: any) => any` | ✅ | Function called when a tab is clicked. Use to switch data or apply filters. |
|
|
287
|
+
| `tabsData` | `TabData[]` | ✅ | Array of tab configurations. |
|
|
292
288
|
|
|
293
289
|
**TabData Interface:**
|
|
294
290
|
|
|
295
|
-
| Property
|
|
296
|
-
|
|
297
|
-
| `label`
|
|
298
|
-
| `counter`
|
|
299
|
-
| `counterClass` | `string` | ❌
|
|
291
|
+
| Property | Type | Required | Description |
|
|
292
|
+
| -------------- | -------- | -------- | --------------------------------------------------- |
|
|
293
|
+
| `label` | `string` | ✅ | Text displayed on the tab. |
|
|
294
|
+
| `counter` | `number` | ❌ | Optional counter badge displayed next to the label. |
|
|
295
|
+
| `counterClass` | `string` | ❌ | CSS class for styling the counter badge. |
|
|
300
296
|
|
|
301
297
|
**Example:**
|
|
298
|
+
|
|
302
299
|
```typescript
|
|
303
300
|
const tableData: TableData = {
|
|
304
301
|
// ... other config
|
|
305
302
|
tabs: {
|
|
306
303
|
method: (tab, event) => {
|
|
307
|
-
if (tab.label ===
|
|
304
|
+
if (tab.label === "Active") {
|
|
308
305
|
this.loadActiveProducts();
|
|
309
|
-
} else if (tab.label ===
|
|
306
|
+
} else if (tab.label === "Inactive") {
|
|
310
307
|
this.loadInactiveProducts();
|
|
311
308
|
}
|
|
312
309
|
},
|
|
313
|
-
tabsData: [
|
|
314
|
-
|
|
315
|
-
{ label: 'Inactive', counter: 8, counterClass: 'bg-gray-500' },
|
|
316
|
-
{ label: 'All' }
|
|
317
|
-
]
|
|
318
|
-
}
|
|
310
|
+
tabsData: [{ label: "Active", counter: 42, counterClass: "bg-green-500" }, { label: "Inactive", counter: 8, counterClass: "bg-gray-500" }, { label: "All" }],
|
|
311
|
+
},
|
|
319
312
|
};
|
|
320
313
|
```
|
|
321
314
|
|
|
@@ -325,34 +318,36 @@ const tableData: TableData = {
|
|
|
325
318
|
|
|
326
319
|
Predefined filter buttons that users can click to apply common filters.
|
|
327
320
|
|
|
328
|
-
| Property | Type
|
|
329
|
-
|
|
330
|
-
| `title`
|
|
331
|
-
| `items`
|
|
321
|
+
| Property | Type | Required | Description |
|
|
322
|
+
| -------- | -------------- | -------- | ----------------------------------- |
|
|
323
|
+
| `title` | `string` | ✅ | Label for the filter group. |
|
|
324
|
+
| `items` | `FilterItem[]` | ✅ | Array of individual filter options. |
|
|
332
325
|
|
|
333
326
|
**FilterItem Properties:**
|
|
327
|
+
|
|
334
328
|
- `property` (string): Property to filter on
|
|
335
329
|
- `value` (string \| boolean): Value to filter by
|
|
336
330
|
- `label` (string): Text displayed on the filter button
|
|
337
331
|
|
|
338
332
|
**Example:**
|
|
333
|
+
|
|
339
334
|
```typescript
|
|
340
335
|
const filterableOptions: FilterableOption[] = [
|
|
341
336
|
{
|
|
342
|
-
title:
|
|
337
|
+
title: "Status",
|
|
343
338
|
items: [
|
|
344
|
-
{ property:
|
|
345
|
-
{ property:
|
|
346
|
-
{ property:
|
|
347
|
-
]
|
|
339
|
+
{ property: "status", value: "active", label: "Active" },
|
|
340
|
+
{ property: "status", value: "pending", label: "Pending" },
|
|
341
|
+
{ property: "status", value: "archived", label: "Archived" },
|
|
342
|
+
],
|
|
348
343
|
},
|
|
349
344
|
{
|
|
350
|
-
title:
|
|
345
|
+
title: "Category",
|
|
351
346
|
items: [
|
|
352
|
-
{ property:
|
|
353
|
-
{ property:
|
|
354
|
-
]
|
|
355
|
-
}
|
|
347
|
+
{ property: "category", value: "electronics", label: "Electronics" },
|
|
348
|
+
{ property: "category", value: "clothing", label: "Clothing" },
|
|
349
|
+
],
|
|
350
|
+
},
|
|
356
351
|
];
|
|
357
352
|
```
|
|
358
353
|
|
|
@@ -362,30 +357,31 @@ const filterableOptions: FilterableOption[] = [
|
|
|
362
357
|
|
|
363
358
|
Firestore `where` clauses that are always applied to queries, before any user filters.
|
|
364
359
|
|
|
365
|
-
| Property
|
|
366
|
-
|
|
367
|
-
| `operator`
|
|
368
|
-
| `firestoreProperty` | `string`
|
|
369
|
-
| `dashProperty`
|
|
360
|
+
| Property | Type | Required | Description |
|
|
361
|
+
| ------------------- | -------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
362
|
+
| `operator` | `WhereFilterOp` | ✅ | Firestore comparison operator: `'=='`, `'!='`, `'<'`, `'<='`, `'>'`, `'>='`, `'array-contains'`, `'in'`, `'array-contains-any'`, `'not-in'`. |
|
|
363
|
+
| `firestoreProperty` | `string` | ✅ | Name of the field in Firestore to filter on. |
|
|
364
|
+
| `dashProperty` | `string \| string[]` | ✅ | Value or array of values to compare against. Can be a static value or a property name from your component. |
|
|
370
365
|
|
|
371
366
|
**Example:**
|
|
367
|
+
|
|
372
368
|
```typescript
|
|
373
369
|
const conditions: Condition[] = [
|
|
374
370
|
{
|
|
375
|
-
operator:
|
|
376
|
-
firestoreProperty:
|
|
377
|
-
dashProperty: this.currentTenantId
|
|
371
|
+
operator: "==",
|
|
372
|
+
firestoreProperty: "tenantId",
|
|
373
|
+
dashProperty: this.currentTenantId,
|
|
378
374
|
},
|
|
379
375
|
{
|
|
380
|
-
operator:
|
|
381
|
-
firestoreProperty:
|
|
382
|
-
dashProperty: this.startDate
|
|
376
|
+
operator: ">=",
|
|
377
|
+
firestoreProperty: "createdAt",
|
|
378
|
+
dashProperty: this.startDate,
|
|
383
379
|
},
|
|
384
380
|
{
|
|
385
|
-
operator:
|
|
386
|
-
firestoreProperty:
|
|
387
|
-
dashProperty: [
|
|
388
|
-
}
|
|
381
|
+
operator: "in",
|
|
382
|
+
firestoreProperty: "status",
|
|
383
|
+
dashProperty: ["active", "pending"],
|
|
384
|
+
},
|
|
389
385
|
];
|
|
390
386
|
```
|
|
391
387
|
|
|
@@ -395,13 +391,14 @@ const conditions: Condition[] = [
|
|
|
395
391
|
|
|
396
392
|
Internal interface used for managing sort and filter state. Automatically handled by the table component.
|
|
397
393
|
|
|
398
|
-
| Property
|
|
399
|
-
|
|
400
|
-
| `filters`
|
|
401
|
-
| `sortBy`
|
|
402
|
-
| `elementId` | `{property: string, value: string}`
|
|
394
|
+
| Property | Type | Description |
|
|
395
|
+
| ----------- | ------------------------------------------ | ---------------------------------------------------- |
|
|
396
|
+
| `filters` | `FilterState[]` | Array of active filters per column. |
|
|
397
|
+
| `sortBy` | `{field: string, order: OrderByDirection}` | Current sort configuration. |
|
|
398
|
+
| `elementId` | `{property: string, value: string}` | Optional identifier for highlighting a specific row. |
|
|
403
399
|
|
|
404
400
|
**FilterState Properties:**
|
|
401
|
+
|
|
405
402
|
- `arrange` ('ascending' \| 'descending' \| 'filter' \| 'filterByDate' \| 'equals' \| ''): Type of filter/sort applied
|
|
406
403
|
- `filter` ({property: string, filtering: string}): Text filter configuration
|
|
407
404
|
- `dateFilter` ({initial: Date, final: Date}): Date range filter
|
|
@@ -412,24 +409,25 @@ Internal interface used for managing sort and filter state. Automatically handle
|
|
|
412
409
|
|
|
413
410
|
Configuration for displaying images in table cells.
|
|
414
411
|
|
|
415
|
-
| Property
|
|
416
|
-
|
|
417
|
-
| `class`
|
|
418
|
-
| `path`
|
|
419
|
-
| `url`
|
|
420
|
-
| `default` | `string`
|
|
412
|
+
| Property | Type | Required | Description |
|
|
413
|
+
| --------- | --------- | -------- | --------------------------------------------------------------------------------------- |
|
|
414
|
+
| `class` | `string` | ✅ | CSS classes for styling the image element. |
|
|
415
|
+
| `path` | `string` | ❌ | Property name containing the image path/URL in the row data. |
|
|
416
|
+
| `url` | `boolean` | ❌ | If `true`, treats the value as a complete URL. Otherwise, constructs path from storage. |
|
|
417
|
+
| `default` | `string` | ❌ | Default image URL if the actual image is missing or fails to load. |
|
|
421
418
|
|
|
422
419
|
**Example:**
|
|
420
|
+
|
|
423
421
|
```typescript
|
|
424
422
|
const column: Column = {
|
|
425
|
-
property:
|
|
426
|
-
title:
|
|
423
|
+
property: "avatar",
|
|
424
|
+
title: "Photo",
|
|
427
425
|
image: {
|
|
428
|
-
class:
|
|
429
|
-
path:
|
|
426
|
+
class: "w-10 h-10 rounded-full object-cover",
|
|
427
|
+
path: "photoURL",
|
|
430
428
|
url: true,
|
|
431
|
-
default:
|
|
432
|
-
}
|
|
429
|
+
default: "/assets/default-avatar.png",
|
|
430
|
+
},
|
|
433
431
|
};
|
|
434
432
|
```
|
|
435
433
|
|
|
@@ -449,9 +447,9 @@ The library provides a flexible export system that allows you to download table
|
|
|
449
447
|
#### Basic Example
|
|
450
448
|
|
|
451
449
|
```typescript
|
|
452
|
-
import { TableService } from
|
|
453
|
-
import * as XLSX from
|
|
454
|
-
import moment from
|
|
450
|
+
import { TableService } from "@adryanmmm/ng-firebase-table-kxp";
|
|
451
|
+
import * as XLSX from "xlsx";
|
|
452
|
+
import moment from "moment";
|
|
455
453
|
|
|
456
454
|
export class UsersComponent {
|
|
457
455
|
constructor(private tableService: TableService) {}
|
|
@@ -460,28 +458,28 @@ export class UsersComponent {
|
|
|
460
458
|
downloadTable = async (arrange: Arrange, conditions: Condition[]) => {
|
|
461
459
|
// 1. Fetch all data with current filters
|
|
462
460
|
const rawData = await this.tableService.getItemsData(
|
|
463
|
-
|
|
464
|
-
arrange,
|
|
465
|
-
conditions
|
|
461
|
+
"users", // collection name
|
|
462
|
+
arrange, // current sort/filter state
|
|
463
|
+
conditions, // optional where conditions
|
|
466
464
|
);
|
|
467
465
|
|
|
468
466
|
// 2. Transform data to desired format
|
|
469
|
-
const xlsxData = rawData.map(user => ({
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
467
|
+
const xlsxData = rawData.map((user) => ({
|
|
468
|
+
Name: user.fullName,
|
|
469
|
+
Email: user.email,
|
|
470
|
+
Phone: user.phoneNumber,
|
|
471
|
+
Created: new Date(user.createdAt.seconds * 1000).toLocaleDateString(),
|
|
474
472
|
}));
|
|
475
473
|
|
|
476
474
|
// 3. Generate Excel file
|
|
477
475
|
const worksheet = XLSX.utils.json_to_sheet(xlsxData);
|
|
478
476
|
const workbook = XLSX.utils.book_new();
|
|
479
|
-
XLSX.utils.book_append_sheet(workbook, worksheet,
|
|
480
|
-
|
|
477
|
+
XLSX.utils.book_append_sheet(workbook, worksheet, "Users");
|
|
478
|
+
|
|
481
479
|
// 4. Download file
|
|
482
|
-
const fileName = `Users_${moment().format(
|
|
480
|
+
const fileName = `Users_${moment().format("DD-MM-YYYY-HH-mm-ss")}.xlsx`;
|
|
483
481
|
XLSX.writeFile(workbook, fileName);
|
|
484
|
-
}
|
|
482
|
+
};
|
|
485
483
|
}
|
|
486
484
|
```
|
|
487
485
|
|
|
@@ -489,42 +487,38 @@ export class UsersComponent {
|
|
|
489
487
|
|
|
490
488
|
```typescript
|
|
491
489
|
downloadOrdersTable = async (arrange: Arrange, conditions: Condition[]) => {
|
|
492
|
-
const rawData = await this.tableService.getItemsData(
|
|
493
|
-
'orders',
|
|
494
|
-
arrange,
|
|
495
|
-
conditions
|
|
496
|
-
);
|
|
490
|
+
const rawData = await this.tableService.getItemsData("orders", arrange, conditions);
|
|
497
491
|
|
|
498
492
|
let totalAmount = 0;
|
|
499
493
|
|
|
500
494
|
// Map data and calculate totals
|
|
501
|
-
const xlsxData = rawData.map(order => {
|
|
495
|
+
const xlsxData = rawData.map((order) => {
|
|
502
496
|
totalAmount += order.amount;
|
|
503
497
|
return {
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
498
|
+
"Order ID": order.id,
|
|
499
|
+
Customer: order.customerName,
|
|
500
|
+
Amount: order.amount,
|
|
501
|
+
Date: new Date(order.createdAt.seconds * 1000).toLocaleDateString(),
|
|
502
|
+
Status: order.status,
|
|
509
503
|
};
|
|
510
504
|
});
|
|
511
505
|
|
|
512
506
|
// Add totals row
|
|
513
507
|
xlsxData.push({
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
508
|
+
"Order ID": "",
|
|
509
|
+
Customer: "",
|
|
510
|
+
Amount: totalAmount,
|
|
511
|
+
Date: "TOTAL",
|
|
512
|
+
Status: "",
|
|
519
513
|
});
|
|
520
514
|
|
|
521
515
|
const worksheet = XLSX.utils.json_to_sheet(xlsxData);
|
|
522
516
|
const workbook = XLSX.utils.book_new();
|
|
523
|
-
XLSX.utils.book_append_sheet(workbook, worksheet,
|
|
524
|
-
|
|
525
|
-
const fileName = `Orders_${moment().format(
|
|
517
|
+
XLSX.utils.book_append_sheet(workbook, worksheet, "Orders");
|
|
518
|
+
|
|
519
|
+
const fileName = `Orders_${moment().format("DD-MM-YYYY-HH-mm-ss")}.xlsx`;
|
|
526
520
|
XLSX.writeFile(workbook, fileName);
|
|
527
|
-
}
|
|
521
|
+
};
|
|
528
522
|
```
|
|
529
523
|
|
|
530
524
|
#### Filtering Data Before Export
|
|
@@ -533,17 +527,17 @@ You can apply additional client-side filters based on user permissions:
|
|
|
533
527
|
|
|
534
528
|
```typescript
|
|
535
529
|
downloadTable = async (arrange: Arrange, conditions: Condition[]) => {
|
|
536
|
-
const rawData = await this.tableService.getItemsData(
|
|
530
|
+
const rawData = await this.tableService.getItemsData("orders", arrange, conditions);
|
|
537
531
|
|
|
538
532
|
// Filter based on user permissions
|
|
539
|
-
const filteredData = rawData.filter(order => {
|
|
540
|
-
if (this.currentUser.role ===
|
|
533
|
+
const filteredData = rawData.filter((order) => {
|
|
534
|
+
if (this.currentUser.role === "Admin") return true;
|
|
541
535
|
if (this.currentUser.storeId === order.storeId) return true;
|
|
542
536
|
return false;
|
|
543
537
|
});
|
|
544
538
|
|
|
545
539
|
// ... continue with export
|
|
546
|
-
}
|
|
540
|
+
};
|
|
547
541
|
```
|
|
548
542
|
|
|
549
543
|
#### Using Custom Pipes for Formatting
|
|
@@ -551,33 +545,33 @@ downloadTable = async (arrange: Arrange, conditions: Condition[]) => {
|
|
|
551
545
|
Apply Angular pipes to format data in the export:
|
|
552
546
|
|
|
553
547
|
```typescript
|
|
554
|
-
import { DatePipe, CurrencyPipe } from
|
|
548
|
+
import { DatePipe, CurrencyPipe } from "@angular/common";
|
|
555
549
|
|
|
556
550
|
downloadTable = async (arrange: Arrange, conditions: Condition[]) => {
|
|
557
|
-
const rawData = await this.tableService.getItemsData(
|
|
558
|
-
|
|
559
|
-
const datePipe = new DatePipe(
|
|
560
|
-
const currencyPipe = new CurrencyPipe(
|
|
561
|
-
|
|
562
|
-
const xlsxData = rawData.map(product => ({
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
551
|
+
const rawData = await this.tableService.getItemsData("products", arrange);
|
|
552
|
+
|
|
553
|
+
const datePipe = new DatePipe("en-US");
|
|
554
|
+
const currencyPipe = new CurrencyPipe("en-US");
|
|
555
|
+
|
|
556
|
+
const xlsxData = rawData.map((product) => ({
|
|
557
|
+
Product: product.name,
|
|
558
|
+
Price: currencyPipe.transform(product.price),
|
|
559
|
+
Created: datePipe.transform(product.createdAt.toDate(), "short"),
|
|
560
|
+
Status: product.active ? "Active" : "Inactive",
|
|
567
561
|
}));
|
|
568
562
|
|
|
569
563
|
// ... continue with export
|
|
570
|
-
}
|
|
564
|
+
};
|
|
571
565
|
```
|
|
572
566
|
|
|
573
567
|
#### CSV vs XLSX Format
|
|
574
568
|
|
|
575
569
|
```typescript
|
|
576
570
|
// Export as XLSX (default)
|
|
577
|
-
XLSX.writeFile(workbook,
|
|
571
|
+
XLSX.writeFile(workbook, "data.xlsx");
|
|
578
572
|
|
|
579
573
|
// Export as CSV
|
|
580
|
-
XLSX.writeFile(workbook,
|
|
574
|
+
XLSX.writeFile(workbook, "data.csv", { bookType: "csv", type: "binary" });
|
|
581
575
|
```
|
|
582
576
|
|
|
583
577
|
#### Important Notes
|
|
@@ -600,4 +594,4 @@ This project is released under the MIT License. See the [LICENSE](LICENSE) file
|
|
|
600
594
|
|
|
601
595
|
## Support the Project
|
|
602
596
|
|
|
603
|
-
If this library saves you time, please star the repository and share it with your team.
|
|
597
|
+
If this library saves you time, please star the repository and share it with your team.
|