Skip to content

Commit

Permalink
fix(editor): provide complex object override path for select editor
Browse files Browse the repository at this point in the history
- 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
  • Loading branch information
ghiscoding-SE committed Oct 18, 2019
1 parent 1635af1 commit a93a53d
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 15 deletions.
1 change: 1 addition & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"request": "launch",
"name": "Chrome Debugger",
"url": "http://localhost:4300",
"breakOnLoad": false,
"webRoot": "${workspaceFolder}"
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -264,15 +264,27 @@ describe('SelectEditor', () => {
expect(mockItemData).toEqual({ id: 1, gender: 'female', isActive: true });
});

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

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

expect(mockItemData).toEqual({ id: 1, part: { gender: 'female' }, isActive: true });
expect(mockItemData).toEqual({ id: 1, person: { bio: { gender: 'female' } }, isActive: true });
});

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', () => {
mockColumn.internalColumnEditor.validator = null;
mockColumn.internalColumnEditor.complexObjectPath = 'person.bio';
mockColumn.field = 'person.bio.gender';
mockItemData = { id: 1, person: { bio: { gender: 'male' } }, isActive: true };

editor = new SelectEditor(editorArguments, true);
editor.applyValue(mockItemData, { gender: 'female' });

expect(mockItemData).toEqual({ id: 1, person: { bio: { gender: 'female' } }, isActive: true });
});

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

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

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

expect(output).toEqual([{ label: 'male', value: 'male' }, { label: 'other', value: 'other' }]);
expect(output).toEqual(['male']);
});

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

expect(output).toEqual([{ label: 'male', value: 'male' }, { label: 'other', value: 'other' }]);
});

it('should return object value when using a dot (.) notation and we override the object path using "complexObjectPath" to find correct values', () => {
mockColumn.field = 'employee.bio';
mockItemData = { id: 1, employee: { id: 24, bio: { gender: ['male', 'other'] } }, isActive: true };
mockColumn.internalColumnEditor.complexObjectPath = 'employee.bio.gender';
editor = new SelectEditor(editorArguments, true);
editor.loadValue(mockItemData);
const output = editor.serializeValue();

expect(output).toEqual(['male']);
expect(output).toEqual([{ label: 'male', value: 'male' }, { label: 'other', value: 'other' }]);
});
});

Expand Down
16 changes: 12 additions & 4 deletions src/app/modules/angular-slickgrid/editors/selectEditor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ export class SelectEditor implements Editor {
newValue = state.split(',');
}

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

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

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

if (item && this.columnDef && (item.hasOwnProperty(fieldName) || isComplexObject)) {
const value = (isComplexObject) ? getDescendantProperty(item, fieldName) : item[fieldName];
// when it's a complex object, user could override the object path (where the editable object is located)
// else we use the path provided in the Field Column Definition
const objectPath = this.columnEditor && this.columnEditor.complexObjectPath || fieldName;
const currentValue = (isComplexObject) ? getDescendantProperty(item, objectPath) : item[fieldName];
const value = (isComplexObject && currentValue.hasOwnProperty(this.valueName)) ? currentValue[this.valueName] : currentValue;

if (this.isMultipleSelect && Array.isArray(value)) {
this.loadMultipleValues(value);
} else {
Expand All @@ -383,7 +391,7 @@ export class SelectEditor implements Editor {

loadSingleValue(currentValue: any) {
// keep the default value in memory for references
this.originalValue = currentValue;
this.originalValue = typeof currentValue === 'number' ? `${currentValue}` : currentValue;
this.$editorElm.val(currentValue);

// make sure the prop exists first
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@ export interface ColumnEditor {
/** We could sort the collection by 1 or more properties, or by translated value(s) when enableTranslateLabel is True */
collectionSortBy?: CollectionSortBy | CollectionSortBy[];

/**
* 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
* 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'
* NOTE: Currently only used in the Single/MultipleSelect Editors, we could potentially use it for more Editors in the future if need be.
*/
complexObjectPath?: string;

/** A custom structure can be used instead of the default label/value pair. Commonly used with Select/Multi-Select Editor */
customStructure?: CollectionCustomStructure;

Expand Down

0 comments on commit a93a53d

Please sign in to comment.