@memberjunction/ng-join-grid 4.0.0 → 4.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 +81 -437
- package/package.json +8 -8
package/README.md
CHANGED
|
@@ -1,23 +1,6 @@
|
|
|
1
1
|
# @memberjunction/ng-join-grid
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
|
|
9
|
-
## Features
|
|
10
|
-
|
|
11
|
-
- **Two Operation Modes**:
|
|
12
|
-
- **Entity Mode**: Creates/deletes records in a junction entity for many-to-many relationships
|
|
13
|
-
- **Fields Mode**: Updates fields directly in related records
|
|
14
|
-
- **Flexible Data Sources**: Load data from full entities, views, or arrays
|
|
15
|
-
- **Automatic Grid Generation**: Builds grid structure based on provided entity relationships
|
|
16
|
-
- **Integrated with MemberJunction**: Works seamlessly with the MJ metadata system and BaseEntity objects
|
|
17
|
-
- **Transaction Support**: Manages pending changes with transaction groups
|
|
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
|
|
3
|
+
A checkbox-based grid component for managing many-to-many entity relationships in MemberJunction applications. Supports both junction entity record creation/deletion and direct field editing modes.
|
|
21
4
|
|
|
22
5
|
## Installation
|
|
23
6
|
|
|
@@ -25,52 +8,55 @@ This package provides the `JoinGridComponent` - a flexible grid component design
|
|
|
25
8
|
npm install @memberjunction/ng-join-grid
|
|
26
9
|
```
|
|
27
10
|
|
|
28
|
-
|
|
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`
|
|
11
|
+
## Overview
|
|
45
12
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
13
|
+
The Join Grid displays rows from one entity against columns from another, with checkboxes at each intersection. Checking a box creates a record in the junction entity; unchecking deletes it. An alternative Fields mode allows editing field values directly in related records. All changes are batched in transaction groups for atomic saves.
|
|
14
|
+
|
|
15
|
+
```mermaid
|
|
16
|
+
flowchart LR
|
|
17
|
+
subgraph Rows["Row Entity"]
|
|
18
|
+
R1["User A"]
|
|
19
|
+
R2["User B"]
|
|
20
|
+
R3["User C"]
|
|
21
|
+
end
|
|
22
|
+
subgraph Grid["Join Grid"]
|
|
23
|
+
G["Checkbox Matrix"]
|
|
24
|
+
end
|
|
25
|
+
subgraph Cols["Column Entity"]
|
|
26
|
+
C1["Role 1"]
|
|
27
|
+
C2["Role 2"]
|
|
28
|
+
end
|
|
29
|
+
subgraph Junction["Junction Entity"]
|
|
30
|
+
J["UserRoles records"]
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
R1 --> G
|
|
34
|
+
R2 --> G
|
|
35
|
+
R3 --> G
|
|
36
|
+
C1 --> G
|
|
37
|
+
C2 --> G
|
|
38
|
+
G -->|create/delete| J
|
|
39
|
+
|
|
40
|
+
style Rows fill:#2d6a9f,stroke:#1a4971,color:#fff
|
|
41
|
+
style Grid fill:#7c5295,stroke:#563a6b,color:#fff
|
|
42
|
+
style Cols fill:#2d8659,stroke:#1a5c3a,color:#fff
|
|
43
|
+
style Junction fill:#b8762f,stroke:#8a5722,color:#fff
|
|
44
|
+
```
|
|
53
45
|
|
|
54
46
|
## Usage
|
|
55
47
|
|
|
56
|
-
### Import
|
|
48
|
+
### Module Import
|
|
57
49
|
|
|
58
50
|
```typescript
|
|
59
51
|
import { JoinGridModule } from '@memberjunction/ng-join-grid';
|
|
60
52
|
|
|
61
53
|
@NgModule({
|
|
62
|
-
imports: [
|
|
63
|
-
JoinGridModule,
|
|
64
|
-
// other imports
|
|
65
|
-
],
|
|
66
|
-
// ...
|
|
54
|
+
imports: [JoinGridModule]
|
|
67
55
|
})
|
|
68
|
-
export class YourModule {
|
|
56
|
+
export class YourModule {}
|
|
69
57
|
```
|
|
70
58
|
|
|
71
|
-
###
|
|
72
|
-
|
|
73
|
-
Use this mode when you want to manage relationships between two entities by creating or deleting records in a junction entity.
|
|
59
|
+
### Entity Mode (Many-to-Many)
|
|
74
60
|
|
|
75
61
|
```html
|
|
76
62
|
<mj-join-grid
|
|
@@ -87,9 +73,7 @@ Use this mode when you want to manage relationships between two entities by crea
|
|
|
87
73
|
</mj-join-grid>
|
|
88
74
|
```
|
|
89
75
|
|
|
90
|
-
### Fields Mode
|
|
91
|
-
|
|
92
|
-
Used when you want to edit fields in a related entity:
|
|
76
|
+
### Fields Mode (Direct Editing)
|
|
93
77
|
|
|
94
78
|
```html
|
|
95
79
|
<mj-join-grid
|
|
@@ -99,403 +83,63 @@ Used when you want to edit fields in a related entity:
|
|
|
99
83
|
[JoinEntityName]="'UserPreferences'"
|
|
100
84
|
[JoinEntityRowForeignKey]="'UserID'"
|
|
101
85
|
[JoinEntityDisplayColumns]="['PreferenceType', 'PreferenceValue']"
|
|
102
|
-
[ShowSaveButton]="true"
|
|
103
|
-
[ShowCancelButton]="true">
|
|
86
|
+
[ShowSaveButton]="true">
|
|
104
87
|
</mj-join-grid>
|
|
105
88
|
```
|
|
106
89
|
|
|
107
|
-
##
|
|
90
|
+
## Operation Modes
|
|
108
91
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
92
|
+
| Mode | `CheckBoxValueMode` | Behavior |
|
|
93
|
+
|------|---------------------|----------|
|
|
94
|
+
| Entity + RecordExists | `'RecordExists'` | Checkbox creates/deletes junction records |
|
|
95
|
+
| Entity + ColumnValue | `'ColumnValue'` | Checkbox toggles a boolean field on existing records |
|
|
96
|
+
| Fields | N/A | Displays and edits field values in the join entity |
|
|
113
97
|
|
|
114
|
-
|
|
98
|
+
## Key Inputs
|
|
99
|
+
|
|
100
|
+
### Row Configuration
|
|
115
101
|
|
|
116
|
-
#### General Configuration
|
|
117
102
|
| Input | Type | Default | Description |
|
|
118
103
|
|-------|------|---------|-------------|
|
|
119
|
-
| `
|
|
120
|
-
| `
|
|
121
|
-
| `
|
|
122
|
-
| `
|
|
123
|
-
|
|
124
|
-
#### Row Configuration
|
|
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 |
|
|
135
|
-
|
|
136
|
-
#### Column Configuration
|
|
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 |
|
|
147
|
-
|
|
148
|
-
#### Join Entity Configuration
|
|
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
|
-
```
|
|
195
|
-
|
|
196
|
-
## Examples
|
|
197
|
-
|
|
198
|
-
### User-Role Assignment Grid
|
|
199
|
-
|
|
200
|
-
```typescript
|
|
201
|
-
// Component
|
|
202
|
-
@Component({
|
|
203
|
-
selector: 'app-user-roles',
|
|
204
|
-
template: `
|
|
205
|
-
<mj-join-grid
|
|
206
|
-
[RowsEntityName]="'Users'"
|
|
207
|
-
[RowsEntityDisplayField]="'UserName'"
|
|
208
|
-
[ColumnsEntityName]="'Roles'"
|
|
209
|
-
[ColumnsEntityDisplayField]="'RoleName'"
|
|
210
|
-
[JoinEntityName]="'UserRoles'"
|
|
211
|
-
[JoinEntityRowForeignKey]="'UserID'"
|
|
212
|
-
[JoinEntityColumnForeignKey]="'RoleID'"
|
|
213
|
-
[RowsExtraFilter]="'IsActive = 1'"
|
|
214
|
-
[ColumnsExtraFilter]="'IsActive = 1'"
|
|
215
|
-
[RowsOrderBy]="'UserName ASC'"
|
|
216
|
-
[ColumnsOrderBy]="'RoleName ASC'"
|
|
217
|
-
[ShowSaveButton]="true"
|
|
218
|
-
[ShowCancelButton]="true">
|
|
219
|
-
</mj-join-grid>
|
|
220
|
-
`
|
|
221
|
-
})
|
|
222
|
-
export class UserRolesComponent { }
|
|
223
|
-
```
|
|
224
|
-
|
|
225
|
-
### Product Category Assignment with Custom Values
|
|
226
|
-
|
|
227
|
-
```typescript
|
|
228
|
-
// Component with defaults for new junction records
|
|
229
|
-
@Component({
|
|
230
|
-
selector: 'app-product-categories',
|
|
231
|
-
template: `
|
|
232
|
-
<mj-join-grid
|
|
233
|
-
[RowsEntityName]="'Products'"
|
|
234
|
-
[RowsEntityDisplayField]="'ProductName'"
|
|
235
|
-
[ColumnsEntityName]="'Categories'"
|
|
236
|
-
[ColumnsEntityDisplayField]="'CategoryName'"
|
|
237
|
-
[JoinEntityName]="'ProductCategories'"
|
|
238
|
-
[JoinEntityRowForeignKey]="'ProductID'"
|
|
239
|
-
[JoinEntityColumnForeignKey]="'CategoryID'"
|
|
240
|
-
[NewRecordDefaultValues]="defaultValues"
|
|
241
|
-
[ShowSaveButton]="true"
|
|
242
|
-
[ShowCancelButton]="true">
|
|
243
|
-
</mj-join-grid>
|
|
244
|
-
`
|
|
245
|
-
})
|
|
246
|
-
export class ProductCategoriesComponent {
|
|
247
|
-
defaultValues = {
|
|
248
|
-
'IsPrimary': false,
|
|
249
|
-
'CreatedAt': new Date()
|
|
250
|
-
};
|
|
251
|
-
}
|
|
252
|
-
```
|
|
104
|
+
| `RowsEntityName` | `string` | -- | Entity for rows (required) |
|
|
105
|
+
| `RowsEntityDisplayField` | `string` | -- | Field to display in first column (required) |
|
|
106
|
+
| `RowsEntityDataSource` | `'FullEntity' \| 'ViewName' \| 'Array'` | `'FullEntity'` | Data source type |
|
|
107
|
+
| `RowsExtraFilter` | `string` | -- | SQL filter for rows |
|
|
108
|
+
| `RowsOrderBy` | `string` | -- | SQL order by for rows |
|
|
253
109
|
|
|
254
|
-
###
|
|
110
|
+
### Column Configuration
|
|
255
111
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
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
|
-
}
|
|
363
|
-
}
|
|
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
|
|
112
|
+
| Input | Type | Default | Description |
|
|
113
|
+
|-------|------|---------|-------------|
|
|
114
|
+
| `ColumnsMode` | `'Entity' \| 'Fields'` | `'Entity'` | Column generation mode |
|
|
115
|
+
| `ColumnsEntityName` | `string` | -- | Entity for columns (Entity mode) |
|
|
116
|
+
| `ColumnsEntityDisplayField` | `string` | -- | Field for column headers |
|
|
474
117
|
|
|
475
|
-
|
|
476
|
-
- Creates transaction groups for batch operations
|
|
477
|
-
- Rolls back changes on save failure
|
|
478
|
-
- Supports both immediate save and queued edit modes
|
|
118
|
+
### Join Entity Configuration
|
|
479
119
|
|
|
480
|
-
|
|
120
|
+
| Input | Type | Default | Description |
|
|
121
|
+
|-------|------|---------|-------------|
|
|
122
|
+
| `JoinEntityName` | `string` | -- | Junction entity name (required) |
|
|
123
|
+
| `JoinEntityRowForeignKey` | `string` | -- | FK linking to row entity (required) |
|
|
124
|
+
| `JoinEntityColumnForeignKey` | `string` | -- | FK linking to column entity (required) |
|
|
125
|
+
| `CheckBoxValueMode` | `'RecordExists' \| 'ColumnValue'` | `'RecordExists'` | How checkbox state is determined |
|
|
126
|
+
| `EditMode` | `'None' \| 'Save' \| 'Queue'` | `'None'` | Editing mode for form integration |
|
|
481
127
|
|
|
482
|
-
|
|
483
|
-
- Form editing complete events
|
|
484
|
-
- Save/cancel events
|
|
485
|
-
- Data refresh events
|
|
128
|
+
### Public Methods
|
|
486
129
|
|
|
487
|
-
|
|
130
|
+
| Method | Returns | Description |
|
|
131
|
+
|--------|---------|-------------|
|
|
132
|
+
| `Refresh()` | `Promise<void>` | Reload all grid data |
|
|
133
|
+
| `Save()` | `Promise<boolean>` | Save all pending changes |
|
|
134
|
+
| `CancelEdit()` | `void` | Cancel pending changes |
|
|
488
135
|
|
|
489
|
-
|
|
136
|
+
## Exported Classes
|
|
490
137
|
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
npm run build
|
|
494
|
-
```
|
|
138
|
+
- `JoinGridCell` -- Represents a single cell in the grid
|
|
139
|
+
- `JoinGridRow` -- Represents a row with column data
|
|
495
140
|
|
|
496
|
-
##
|
|
141
|
+
## Dependencies
|
|
497
142
|
|
|
498
|
-
-
|
|
499
|
-
-
|
|
500
|
-
-
|
|
501
|
-
- The component respects entity permissions and validation rules
|
|
143
|
+
- [@memberjunction/core](../../MJCore/README.md) -- Metadata, RunView, BaseEntity
|
|
144
|
+
- [@memberjunction/ng-shared](../shared/README.md) -- Shared Angular utilities
|
|
145
|
+
- `@progress/kendo-angular-grid` -- Grid rendering
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@memberjunction/ng-join-grid",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.2.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,13 +25,13 @@
|
|
|
25
25
|
"@angular/router": "21.1.3"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@memberjunction/core": "4.
|
|
29
|
-
"@memberjunction/core-entities": "4.
|
|
30
|
-
"@memberjunction/global": "4.
|
|
31
|
-
"@memberjunction/ng-base-types": "4.
|
|
32
|
-
"@memberjunction/ng-container-directives": "4.
|
|
33
|
-
"@memberjunction/ng-shared": "4.
|
|
34
|
-
"@memberjunction/ng-shared-generic": "4.
|
|
28
|
+
"@memberjunction/core": "4.2.0",
|
|
29
|
+
"@memberjunction/core-entities": "4.2.0",
|
|
30
|
+
"@memberjunction/global": "4.2.0",
|
|
31
|
+
"@memberjunction/ng-base-types": "4.2.0",
|
|
32
|
+
"@memberjunction/ng-container-directives": "4.2.0",
|
|
33
|
+
"@memberjunction/ng-shared": "4.2.0",
|
|
34
|
+
"@memberjunction/ng-shared-generic": "4.2.0",
|
|
35
35
|
"@progress/kendo-angular-buttons": "22.0.1",
|
|
36
36
|
"@progress/kendo-angular-dialog": "22.0.1",
|
|
37
37
|
"@progress/kendo-angular-dropdowns": "22.0.1",
|