@memberjunction/ng-join-grid 2.42.1 → 2.44.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.
Files changed (2) hide show
  1. package/README.md +368 -43
  2. package/package.json +7 -7
package/README.md CHANGED
@@ -1,17 +1,23 @@
1
- # Join Grid
1
+ # @memberjunction/ng-join-grid
2
2
 
3
3
  The Join Grid component is a powerful Angular grid that allows you to display and edit relationships between two entities, typically in a many-to-many relationship. It provides a checkbox-based interface for mapping relationships between records.
4
4
 
5
+ ## Overview
6
+
7
+ This package provides the `JoinGridComponent` - a flexible grid component designed to manage entity relationships within the MemberJunction framework. It supports both many-to-many relationships through junction entities and direct field editing in related records.
8
+
5
9
  ## Features
6
10
 
7
11
  - **Two Operation Modes**:
8
- - **Entity Mode**: Creates/deletes records in a junction entity
9
- - **Fields Mode**: Updates fields in related records
12
+ - **Entity Mode**: Creates/deletes records in a junction entity for many-to-many relationships
13
+ - **Fields Mode**: Updates fields directly in related records
10
14
  - **Flexible Data Sources**: Load data from full entities, views, or arrays
11
15
  - **Automatic Grid Generation**: Builds grid structure based on provided entity relationships
12
- - **Integrated with MemberJunction**: Works with the MJ metadata system and BaseEntity objects
16
+ - **Integrated with MemberJunction**: Works seamlessly with the MJ metadata system and BaseEntity objects
13
17
  - **Transaction Support**: Manages pending changes with transaction groups
14
18
  - **Form Integration**: Can be integrated with parent form components and respond to form events
19
+ - **Checkbox Value Modes**: Support for both record existence and field value checkboxes
20
+ - **Built-in Save/Cancel**: Configurable buttons for managing changes
15
21
 
16
22
  ## Installation
17
23
 
@@ -19,6 +25,32 @@ The Join Grid component is a powerful Angular grid that allows you to display an
19
25
  npm install @memberjunction/ng-join-grid
20
26
  ```
21
27
 
28
+ ### Prerequisites
29
+
30
+ This package requires the following peer dependencies:
31
+ - `@angular/common`: ^18.0.2
32
+ - `@angular/core`: ^18.0.2
33
+ - `@angular/forms`: ^18.0.2
34
+ - `@angular/router`: ^18.0.2
35
+
36
+ ### Dependencies
37
+
38
+ The component integrates with these MemberJunction packages:
39
+ - `@memberjunction/core-entities`
40
+ - `@memberjunction/global`
41
+ - `@memberjunction/core`
42
+ - `@memberjunction/ng-base-types`
43
+ - `@memberjunction/ng-container-directives`
44
+ - `@memberjunction/ng-shared`
45
+
46
+ It also uses Kendo UI components:
47
+ - `@progress/kendo-angular-buttons`
48
+ - `@progress/kendo-angular-dialog`
49
+ - `@progress/kendo-angular-layout`
50
+ - `@progress/kendo-angular-grid`
51
+ - `@progress/kendo-angular-inputs`
52
+ - `@progress/kendo-angular-indicators`
53
+
22
54
  ## Usage
23
55
 
24
56
  ### Import the Module
@@ -74,51 +106,92 @@ Used when you want to edit fields in a related entity:
74
106
 
75
107
  ## API Reference
76
108
 
109
+ ### Component Selector
110
+ ```html
111
+ <mj-join-grid></mj-join-grid>
112
+ ```
113
+
77
114
  ### Inputs
78
115
 
79
116
  #### General Configuration
80
- - `ShowSaveButton`: boolean - Show/hide the save button
81
- - `ShowCancelButton`: boolean - Show/hide the cancel button
82
- - `EditMode`: 'None' | 'Save' | 'Queue' - Control editing mode
83
- - `NewRecordDefaultValues`: { [key: string]: any } - Default values for new records
117
+ | Input | Type | Default | Description |
118
+ |-------|------|---------|-------------|
119
+ | `ShowSaveButton` | `boolean` | `true` | Show/hide the save button |
120
+ | `ShowCancelButton` | `boolean` | `true` | Show/hide the cancel button |
121
+ | `EditMode` | `'None' \| 'Save' \| 'Queue'` | `'None'` | Control editing mode. Use when embedding in parent forms |
122
+ | `NewRecordDefaultValues` | `{ [key: string]: any }` | - | Default values for new junction records |
84
123
 
85
124
  #### Row Configuration
86
- - `RowsEntityName`: string - Name of the entity for rows
87
- - `RowsEntityDisplayName`: string - (Optional) Display name instead of entity name
88
- - `RowsEntityDisplayField`: string - Field to display in the first column
89
- - `RowsEntityDataSource`: 'FullEntity' | 'ViewName' | 'Array' - Data source for rows
90
- - `RowsEntityViewName`: string - View name when datasource is 'ViewName'
91
- - `RowsExtraFilter`: string - Additional filter for rows
92
- - `RowsOrderBy`: string - Order by clause for rows
93
- - `RowsEntityArray`: BaseEntity[] - Array of entities when datasource is 'Array'
125
+ | Input | Type | Default | Required | Description |
126
+ |-------|------|---------|----------|-------------|
127
+ | `RowsEntityName` | `string` | - | | Name of the entity for rows |
128
+ | `RowsEntityDisplayName` | `string` | - | - | Display name to show instead of entity name |
129
+ | `RowsEntityDisplayField` | `string` | - | | Field to display in the first column |
130
+ | `RowsEntityDataSource` | `'FullEntity' \| 'ViewName' \| 'Array'` | `'FullEntity'` | - | Data source type for rows |
131
+ | `RowsEntityViewName` | `string` | - | When DataSource='ViewName' | User View name to run |
132
+ | `RowsExtraFilter` | `string` | - | - | Additional SQL filter for rows |
133
+ | `RowsOrderBy` | `string` | - | - | SQL order by clause for rows |
134
+ | `RowsEntityArray` | `BaseEntity[]` | - | When DataSource='Array' | Array of entity objects |
94
135
 
95
136
  #### Column Configuration
96
- - `ColumnsMode`: 'Entity' | 'Fields' - Mode for column generation
97
- - `ColumnsEntityName`: string - Name of entity for columns (Entity mode)
98
- - `ColumnsEntityDisplayField`: string - Field to display as column headers (Entity mode)
99
- - `ColumnsEntityDataSource`: 'FullEntity' | 'ViewName' | 'Array' - Data source for columns
100
- - `ColumnsEntityViewName`: string - View name when datasource is 'ViewName'
101
- - `ColumnsExtraFilter`: string - Additional filter for columns
102
- - `ColumnsOrderBy`: string - Order by clause for columns
103
- - `ColumnsEntityArray`: BaseEntity[] - Array of entities when datasource is 'Array'
137
+ | Input | Type | Default | Required | Description |
138
+ |-------|------|---------|----------|-------------|
139
+ | `ColumnsMode` | `'Entity' \| 'Fields'` | `'Entity'` | - | Mode for column generation |
140
+ | `ColumnsEntityName` | `string` | - | When Mode='Entity' | Name of entity for columns |
141
+ | `ColumnsEntityDisplayField` | `string` | - | When Mode='Entity' | Field to display as column headers |
142
+ | `ColumnsEntityDataSource` | `'FullEntity' \| 'ViewName' \| 'Array'` | `'FullEntity'` | - | Data source type for columns |
143
+ | `ColumnsEntityViewName` | `string` | - | When DataSource='ViewName' | User View name to run |
144
+ | `ColumnsExtraFilter` | `string` | - | - | Additional SQL filter for columns |
145
+ | `ColumnsOrderBy` | `string` | - | - | SQL order by clause for columns |
146
+ | `ColumnsEntityArray` | `BaseEntity[]` | - | When DataSource='Array' | Array of entity objects |
104
147
 
105
148
  #### Join Entity Configuration
106
- - `JoinEntityName`: string - Name of junction entity
107
- - `JoinEntityRowForeignKey`: string - Foreign key field for rows
108
- - `JoinEntityColumnForeignKey`: string - Foreign key field for columns
109
- - `JoinEntityDisplayColumns`: string[] - Columns to display (Fields mode)
110
- - `JoinEntityExtraFilter`: string - Additional filter for junction entity
111
- - `CheckBoxValueMode`: 'RecordExists' | 'ColumnValue' - How checkbox values are stored
112
- - `CheckBoxValueField`: string - Field for checkbox values when mode is 'ColumnValue'
113
-
114
- ### Methods
115
-
116
- - `Refresh()`: Load/reload grid data
117
- - `Save()`: Save all pending changes
118
- - `CancelEdit()`: Cancel pending changes
119
- - `UpdateCellValueDirect(row, colIndex, newValue)`: Update a cell value (Fields mode)
120
- - `AddJoinEntityRecord(row)`: Add a new junction record (Fields mode)
121
- - `RemoveJoinEntityRecord(row)`: Remove a junction record (Fields mode)
149
+ | Input | Type | Default | Required | Description |
150
+ |-------|------|---------|----------|-------------|
151
+ | `JoinEntityName` | `string` | - | | Name of the junction/join entity |
152
+ | `JoinEntityRowForeignKey` | `string` | - | ✓ | Foreign key field linking to rows |
153
+ | `JoinEntityColumnForeignKey` | `string` | - | | Foreign key field linking to columns |
154
+ | `JoinEntityDisplayColumns` | `string[]` | - | When ColumnsMode='Fields' | Columns to display from join entity |
155
+ | `JoinEntityExtraFilter` | `string` | - | - | Additional filter for join entity |
156
+ | `CheckBoxValueMode` | `'RecordExists' \| 'ColumnValue'` | `'RecordExists'` | - | How checkbox state is determined |
157
+ | `CheckBoxValueField` | `string` | - | When CheckBoxValueMode='ColumnValue' | Field storing checkbox value |
158
+
159
+ ### Public Methods
160
+
161
+ | Method | Parameters | Returns | Description |
162
+ |--------|------------|---------|-------------|
163
+ | `Refresh()` | - | `Promise<void>` | Reload all grid data |
164
+ | `Save()` | - | `Promise<boolean>` | Save all pending changes |
165
+ | `CancelEdit()` | - | `void` | Cancel all pending changes |
166
+ | `UpdateCellValueDirect()` | `row: JoinGridRow, colIndex: number, newValue: any` | `void` | Update cell value (Fields mode) |
167
+ | `AddJoinEntityRecord()` | `row: JoinGridRow` | `Promise<void>` | Add new join record (Fields mode) |
168
+ | `RemoveJoinEntityRecord()` | `row: JoinGridRow` | `Promise<void>` | Remove join record (Fields mode) |
169
+
170
+ ### Exported Classes
171
+
172
+ #### JoinGridCell
173
+ ```typescript
174
+ export class JoinGridCell {
175
+ index: number;
176
+ RowForeignKeyValue: any;
177
+ ColumnForeignKeyValue?: any;
178
+ data?: BaseEntity; // Used in Entity mode
179
+ value?: any; // Used in Fields mode
180
+ }
181
+ ```
182
+
183
+ #### JoinGridRow
184
+ ```typescript
185
+ export class JoinGridRow {
186
+ FirstColValue: any;
187
+ JoinExists: boolean;
188
+ RowForeignKeyValue: any;
189
+ ColumnData: JoinGridCell[];
190
+
191
+ GetColumnValue(colIndex: number): any;
192
+ constructor(data: any);
193
+ }
194
+ ```
122
195
 
123
196
  ## Examples
124
197
 
@@ -164,13 +237,265 @@ export class UserRolesComponent { }
164
237
  [JoinEntityName]="'ProductCategories'"
165
238
  [JoinEntityRowForeignKey]="'ProductID'"
166
239
  [JoinEntityColumnForeignKey]="'CategoryID'"
167
- [NewRecordDefaultValues]="{'IsPrimary': false}"
240
+ [NewRecordDefaultValues]="defaultValues"
168
241
  [ShowSaveButton]="true"
169
242
  [ShowCancelButton]="true">
170
243
  </mj-join-grid>
171
244
  `
172
245
  })
173
246
  export class ProductCategoriesComponent {
174
- // Additional component logic
247
+ defaultValues = {
248
+ 'IsPrimary': false,
249
+ 'CreatedAt': new Date()
250
+ };
251
+ }
252
+ ```
253
+
254
+ ### Fields Mode Example - User Preferences
255
+
256
+ ```typescript
257
+ // Edit fields directly in the join entity
258
+ @Component({
259
+ selector: 'app-user-preferences',
260
+ template: `
261
+ <mj-join-grid
262
+ [RowsEntityName]="'Users'"
263
+ [RowsEntityDisplayField]="'FullName'"
264
+ [ColumnsMode]="'Fields'"
265
+ [JoinEntityName]="'UserPreferences'"
266
+ [JoinEntityRowForeignKey]="'UserID'"
267
+ [JoinEntityDisplayColumns]="['PreferenceName', 'Value', 'IsEnabled']"
268
+ [JoinEntityExtraFilter]="'CategoryID = 5'"
269
+ [ShowSaveButton]="true"
270
+ [ShowCancelButton]="true">
271
+ </mj-join-grid>
272
+ `
273
+ })
274
+ export class UserPreferencesComponent { }
275
+ ```
276
+
277
+ ### Using Views and Filters
278
+
279
+ ```typescript
280
+ // Component using views and filters for data sources
281
+ @Component({
282
+ selector: 'app-active-user-permissions',
283
+ template: `
284
+ <mj-join-grid
285
+ [RowsEntityName]="'Users'"
286
+ [RowsEntityDisplayField]="'UserName'"
287
+ [RowsEntityDataSource]="'ViewName'"
288
+ [RowsEntityViewName]="'Active Users'"
289
+ [RowsOrderBy]="'LastLogin DESC'"
290
+
291
+ [ColumnsEntityName]="'Permissions'"
292
+ [ColumnsEntityDisplayField]="'PermissionName'"
293
+ [ColumnsExtraFilter]="'IsActive = 1 AND CategoryID IN (1,2,3)'"
294
+ [ColumnsOrderBy]="'DisplayOrder ASC, PermissionName ASC'"
295
+
296
+ [JoinEntityName]="'UserPermissions'"
297
+ [JoinEntityRowForeignKey]="'UserID'"
298
+ [JoinEntityColumnForeignKey]="'PermissionID'"
299
+ [JoinEntityExtraFilter]="'GrantedDate IS NOT NULL'"
300
+
301
+ [ShowSaveButton]="true"
302
+ [ShowCancelButton]="true">
303
+ </mj-join-grid>
304
+ `
305
+ })
306
+ export class ActiveUserPermissionsComponent { }
307
+ ```
308
+
309
+ ### Using Array Data Sources
310
+
311
+ ```typescript
312
+ // Component using pre-loaded arrays
313
+ @Component({
314
+ selector: 'app-team-skills',
315
+ template: `
316
+ <mj-join-grid
317
+ [RowsEntityName]="'Employees'"
318
+ [RowsEntityDisplayField]="'Name'"
319
+ [RowsEntityDataSource]="'Array'"
320
+ [RowsEntityArray]="teamMembers"
321
+
322
+ [ColumnsEntityName]="'Skills'"
323
+ [ColumnsEntityDisplayField]="'SkillName'"
324
+ [ColumnsEntityDataSource]="'Array'"
325
+ [ColumnsEntityArray]="relevantSkills"
326
+
327
+ [JoinEntityName]="'EmployeeSkills'"
328
+ [JoinEntityRowForeignKey]="'EmployeeID'"
329
+ [JoinEntityColumnForeignKey]="'SkillID'"
330
+
331
+ [ShowSaveButton]="true"
332
+ [ShowCancelButton]="true">
333
+ </mj-join-grid>
334
+ `
335
+ })
336
+ export class TeamSkillsComponent implements OnInit {
337
+ teamMembers: BaseEntity[] = [];
338
+ relevantSkills: BaseEntity[] = [];
339
+
340
+ async ngOnInit() {
341
+ // Load team members and skills
342
+ const md = new Metadata();
343
+
344
+ // Load team members
345
+ const employeeEntity = await md.GetEntityObject<BaseEntity>('Employees');
346
+ const teamRv = new RunView();
347
+ const teamResult = await teamRv.RunView({
348
+ EntityName: 'Employees',
349
+ ExtraFilter: 'DepartmentID = 5',
350
+ ResultType: 'entity_object'
351
+ });
352
+ this.teamMembers = teamResult.Results;
353
+
354
+ // Load relevant skills
355
+ const skillsRv = new RunView();
356
+ const skillsResult = await skillsRv.RunView({
357
+ EntityName: 'Skills',
358
+ ExtraFilter: 'CategoryID IN (1,2,3)',
359
+ ResultType: 'entity_object'
360
+ });
361
+ this.relevantSkills = skillsResult.Results;
362
+ }
175
363
  }
176
- ```
364
+ ```
365
+
366
+ ### Embedded in Parent Form with Edit Mode Control
367
+
368
+ ```typescript
369
+ // Component embedded in a larger form
370
+ @Component({
371
+ selector: 'app-employee-form',
372
+ template: `
373
+ <form>
374
+ <!-- Other form fields -->
375
+
376
+ <mj-join-grid
377
+ #skillsGrid
378
+ [RowsEntityName]="'Employees'"
379
+ [RowsEntityDisplayField]="'Name'"
380
+ [RowsEntityArray]="[currentEmployee]"
381
+
382
+ [ColumnsEntityName]="'Skills'"
383
+ [ColumnsEntityDisplayField]="'SkillName'"
384
+
385
+ [JoinEntityName]="'EmployeeSkills'"
386
+ [JoinEntityRowForeignKey]="'EmployeeID'"
387
+ [JoinEntityColumnForeignKey]="'SkillID'"
388
+
389
+ [EditMode]="editMode"
390
+ [ShowSaveButton]="false"
391
+ [ShowCancelButton]="false">
392
+ </mj-join-grid>
393
+
394
+ <button (click)="saveAll()">Save Employee</button>
395
+ <button (click)="cancel()">Cancel</button>
396
+ </form>
397
+ `
398
+ })
399
+ export class EmployeeFormComponent {
400
+ @ViewChild('skillsGrid') skillsGrid!: JoinGridComponent;
401
+
402
+ currentEmployee: BaseEntity;
403
+ editMode: 'None' | 'Save' | 'Queue' = 'Queue';
404
+
405
+ async saveAll() {
406
+ // Save the grid changes
407
+ const gridSaved = await this.skillsGrid.Save();
408
+
409
+ if (gridSaved) {
410
+ // Save other form data
411
+ await this.currentEmployee.Save();
412
+ }
413
+ }
414
+
415
+ cancel() {
416
+ this.skillsGrid.CancelEdit();
417
+ // Cancel other form changes
418
+ }
419
+ }
420
+ ```
421
+
422
+ ### Checkbox Value Mode Example
423
+
424
+ ```typescript
425
+ // Using checkbox to update a field value instead of record existence
426
+ @Component({
427
+ selector: 'app-feature-access',
428
+ template: `
429
+ <mj-join-grid
430
+ [RowsEntityName]="'Users'"
431
+ [RowsEntityDisplayField]="'UserName'"
432
+
433
+ [ColumnsEntityName]="'Features'"
434
+ [ColumnsEntityDisplayField]="'FeatureName'"
435
+
436
+ [JoinEntityName]="'UserFeatureAccess'"
437
+ [JoinEntityRowForeignKey]="'UserID'"
438
+ [JoinEntityColumnForeignKey]="'FeatureID'"
439
+
440
+ [CheckBoxValueMode]="'ColumnValue'"
441
+ [CheckBoxValueField]="'IsEnabled'"
442
+
443
+ [ShowSaveButton]="true"
444
+ [ShowCancelButton]="true">
445
+ </mj-join-grid>
446
+ `
447
+ })
448
+ export class FeatureAccessComponent { }
449
+ ```
450
+
451
+ ## Advanced Usage
452
+
453
+ ### Integration with MemberJunction Forms
454
+
455
+ The Join Grid component can be integrated with MemberJunction's form system and responds to form events:
456
+
457
+ ```typescript
458
+ import { BaseFormComponent } from '@memberjunction/ng-base-forms';
459
+
460
+ @Component({
461
+ template: `
462
+ <mj-join-grid
463
+ [parentForm]="parentFormComponent"
464
+ ...other properties>
465
+ </mj-join-grid>
466
+ `
467
+ })
468
+ export class MyComponent {
469
+ parentFormComponent: BaseFormComponent;
470
+ }
471
+ ```
472
+
473
+ ### Transaction Support
474
+
475
+ The component automatically manages transactions when editing:
476
+ - Creates transaction groups for batch operations
477
+ - Rolls back changes on save failure
478
+ - Supports both immediate save and queued edit modes
479
+
480
+ ### Event Handling
481
+
482
+ The component emits events through the MemberJunction event system:
483
+ - Form editing complete events
484
+ - Save/cancel events
485
+ - Data refresh events
486
+
487
+ ## Building
488
+
489
+ To build the package:
490
+
491
+ ```bash
492
+ cd packages/Angular/Generic/join-grid
493
+ npm run build
494
+ ```
495
+
496
+ ## Notes
497
+
498
+ - The component requires proper entity metadata configuration in MemberJunction
499
+ - Junction entities must have appropriate foreign key relationships defined
500
+ - When using Fields mode, ensure the join entity has the display columns configured
501
+ - The component respects entity permissions and validation rules
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@memberjunction/ng-join-grid",
3
- "version": "2.42.1",
3
+ "version": "2.44.0",
4
4
  "description": "MemberJunction: Grid component that is able to display/edit the relationship between two entities. For example being able to edit Users + Roles in a single grid.",
5
5
  "main": "./dist/public-api.js",
6
6
  "typings": "./dist/public-api.d.ts",
@@ -25,12 +25,12 @@
25
25
  "@angular/router": "18.0.2"
26
26
  },
27
27
  "dependencies": {
28
- "@memberjunction/core-entities": "2.42.1",
29
- "@memberjunction/global": "2.42.1",
30
- "@memberjunction/core": "2.42.1",
31
- "@memberjunction/ng-base-types": "2.42.1",
32
- "@memberjunction/ng-container-directives": "2.42.1",
33
- "@memberjunction/ng-shared": "2.42.1",
28
+ "@memberjunction/core-entities": "2.44.0",
29
+ "@memberjunction/global": "2.44.0",
30
+ "@memberjunction/core": "2.44.0",
31
+ "@memberjunction/ng-base-types": "2.44.0",
32
+ "@memberjunction/ng-container-directives": "2.44.0",
33
+ "@memberjunction/ng-shared": "2.44.0",
34
34
  "@progress/kendo-angular-buttons": "16.2.0",
35
35
  "@progress/kendo-angular-dialog": "16.2.0",
36
36
  "@progress/kendo-angular-layout": "16.2.0",