Skip to content
This repository was archived by the owner on Jun 1, 2025. It is now read-only.

Commit a93a53d

Browse files
committed
fix(editor): provide complex object override path for select editor
- when editing a cell with a complex object dot notation, say "user.firstName" and our collection is option/label pair for the user not for their first name, we can use the new property of complexObjectPath: 'user' to tell the editable object to use the "user" and not "user.firstName" when selecting from dropdown
1 parent 1635af1 commit a93a53d

File tree

4 files changed

+53
-15
lines changed

4 files changed

+53
-15
lines changed

.vscode/launch.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"request": "launch",
77
"name": "Chrome Debugger",
88
"url": "http://localhost:4300",
9+
"breakOnLoad": false,
910
"webRoot": "${workspaceFolder}"
1011
},
1112
{

src/app/modules/angular-slickgrid/editors/__tests__/selectEditor.spec.ts

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -264,15 +264,27 @@ describe('SelectEditor', () => {
264264
expect(mockItemData).toEqual({ id: 1, gender: 'female', isActive: true });
265265
});
266266

267-
it('should apply the value to the gender property with a field having dot notation (complex object) that passes validation', () => {
267+
it('should apply the value to the gender (last property) when field has a dot notation (complex object) that passes validation', () => {
268268
mockColumn.internalColumnEditor.validator = null;
269-
mockColumn.field = 'part.gender';
270-
mockItemData = { id: 1, part: { gender: 'male' }, isActive: true };
269+
mockColumn.field = 'person.bio.gender';
270+
mockItemData = { id: 1, person: { bio: { gender: 'male' } }, isActive: true };
271271

272272
editor = new SelectEditor(editorArguments, true);
273273
editor.applyValue(mockItemData, 'female');
274274

275-
expect(mockItemData).toEqual({ id: 1, part: { gender: 'female' }, isActive: true });
275+
expect(mockItemData).toEqual({ id: 1, person: { bio: { gender: 'female' } }, isActive: true });
276+
});
277+
278+
it('should apply the value to the bio property (second last) when field has a dot notation (complex object) value provided is an object and it that passes validation', () => {
279+
mockColumn.internalColumnEditor.validator = null;
280+
mockColumn.internalColumnEditor.complexObjectPath = 'person.bio';
281+
mockColumn.field = 'person.bio.gender';
282+
mockItemData = { id: 1, person: { bio: { gender: 'male' } }, isActive: true };
283+
284+
editor = new SelectEditor(editorArguments, true);
285+
editor.applyValue(mockItemData, { gender: 'female' });
286+
287+
expect(mockItemData).toEqual({ id: 1, person: { bio: { gender: 'female' } }, isActive: true });
276288
});
277289

278290
it('should return item data with an empty string in its value when it fails the custom validation', () => {
@@ -344,27 +356,37 @@ describe('SelectEditor', () => {
344356
expect(output).toEqual([]);
345357
});
346358

347-
it('should return object value when using a dot (.) notation for complex object with a collection of option/label pair', () => {
359+
it('should return value as a string when using a dot (.) notation for complex object with a collection of string values', () => {
348360
mockColumn.field = 'employee.gender';
349-
mockItemData = { id: 1, employee: { gender: ['male', 'other'] }, isActive: true };
361+
mockColumn.internalColumnEditor.collection = ['male', 'female'];
362+
mockItemData = { id: 1, employee: { id: 24, gender: 'male' }, isActive: true };
350363

351364
editor = new SelectEditor(editorArguments, true);
352365
editor.loadValue(mockItemData);
353366
const output = editor.serializeValue();
354367

355-
expect(output).toEqual([{ label: 'male', value: 'male' }, { label: 'other', value: 'other' }]);
368+
expect(output).toEqual(['male']);
356369
});
357370

358-
it('should return value as a string when using a dot (.) notation for complex object with a collection of string values', () => {
371+
it('should return object value when using a dot (.) notation for complex object with a collection of option/label pair', () => {
359372
mockColumn.field = 'employee.gender';
360-
mockColumn.internalColumnEditor.collection = ['male', 'female'];
361-
mockItemData = { id: 1, employee: { gender: 'male' }, isActive: true };
373+
mockItemData = { id: 1, employee: { id: 24, gender: ['male', 'other'] }, isActive: true };
374+
editor = new SelectEditor(editorArguments, true);
375+
editor.loadValue(mockItemData);
376+
const output = editor.serializeValue();
362377

378+
expect(output).toEqual([{ label: 'male', value: 'male' }, { label: 'other', value: 'other' }]);
379+
});
380+
381+
it('should return object value when using a dot (.) notation and we override the object path using "complexObjectPath" to find correct values', () => {
382+
mockColumn.field = 'employee.bio';
383+
mockItemData = { id: 1, employee: { id: 24, bio: { gender: ['male', 'other'] } }, isActive: true };
384+
mockColumn.internalColumnEditor.complexObjectPath = 'employee.bio.gender';
363385
editor = new SelectEditor(editorArguments, true);
364386
editor.loadValue(mockItemData);
365387
const output = editor.serializeValue();
366388

367-
expect(output).toEqual(['male']);
389+
expect(output).toEqual([{ label: 'male', value: 'male' }, { label: 'other', value: 'other' }]);
368390
});
369391
});
370392

src/app/modules/angular-slickgrid/editors/selectEditor.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ export class SelectEditor implements Editor {
322322
newValue = state.split(',');
323323
}
324324

325-
// is the field a complex object, "address.streetNumber"
325+
// is the field a complex object, "user.address.streetNumber"
326326
const isComplexObject = fieldName.indexOf('.') > 0;
327327

328328
// validate the value before applying it (if not valid we'll set an empty string)
@@ -331,7 +331,10 @@ export class SelectEditor implements Editor {
331331

332332
// set the new value to the item datacontext
333333
if (isComplexObject) {
334-
setDeepValue(item, fieldName, newValue);
334+
// when it's a complex object, user could override the object path (where the editable object is located)
335+
// else we use the path provided in the Field Column Definition
336+
const objectPath = this.columnEditor && this.columnEditor.complexObjectPath || fieldName;
337+
setDeepValue(item, objectPath, newValue);
335338
} else {
336339
item[fieldName] = newValue;
337340
}
@@ -357,7 +360,12 @@ export class SelectEditor implements Editor {
357360
const isComplexObject = fieldName.indexOf('.') > 0;
358361

359362
if (item && this.columnDef && (item.hasOwnProperty(fieldName) || isComplexObject)) {
360-
const value = (isComplexObject) ? getDescendantProperty(item, fieldName) : item[fieldName];
363+
// when it's a complex object, user could override the object path (where the editable object is located)
364+
// else we use the path provided in the Field Column Definition
365+
const objectPath = this.columnEditor && this.columnEditor.complexObjectPath || fieldName;
366+
const currentValue = (isComplexObject) ? getDescendantProperty(item, objectPath) : item[fieldName];
367+
const value = (isComplexObject && currentValue.hasOwnProperty(this.valueName)) ? currentValue[this.valueName] : currentValue;
368+
361369
if (this.isMultipleSelect && Array.isArray(value)) {
362370
this.loadMultipleValues(value);
363371
} else {
@@ -383,7 +391,7 @@ export class SelectEditor implements Editor {
383391

384392
loadSingleValue(currentValue: any) {
385393
// keep the default value in memory for references
386-
this.originalValue = currentValue;
394+
this.originalValue = typeof currentValue === 'number' ? `${currentValue}` : currentValue;
387395
this.$editorElm.val(currentValue);
388396

389397
// make sure the prop exists first

src/app/modules/angular-slickgrid/models/columnEditor.interface.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,13 @@ export interface ColumnEditor {
3333
/** We could sort the collection by 1 or more properties, or by translated value(s) when enableTranslateLabel is True */
3434
collectionSortBy?: CollectionSortBy | CollectionSortBy[];
3535

36+
/**
37+
* When providing a dot (.) notation in the "field" property of a column definition, we might want to use a different path for the editable object itself
38+
* For example if we provide a coldef = { field: 'user.name' } but we use a SingleSelect Editor with object values, we could override the path to simply 'user'
39+
* NOTE: Currently only used in the Single/MultipleSelect Editors, we could potentially use it for more Editors in the future if need be.
40+
*/
41+
complexObjectPath?: string;
42+
3643
/** A custom structure can be used instead of the default label/value pair. Commonly used with Select/Multi-Select Editor */
3744
customStructure?: CollectionCustomStructure;
3845

0 commit comments

Comments
 (0)