Skip to content

Commit 0d5a150

Browse files
feat(query): add queryFieldNameGetterFn callback know which field to use (#434)
in certain occasion we don't have know at hand which field name to use, we might just know depending on certain logic (e.g. if item dataContext has a product type "I" then use field "catalogNumber" but if it's type "P" use field "productCategoryNumber", ...)
1 parent fe7a65a commit 0d5a150

File tree

7 files changed

+97
-20
lines changed

7 files changed

+97
-20
lines changed

src/app/app.component.scss

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ $side-menu-width: 250px;
2020
/** Sidebar (left) and Content (right) */
2121
@media (min-width: 1200px) {
2222
.panel-wm-content .container {
23-
width: calc(1170px - $side-menu-width);
23+
width: calc(1170px - #{$side-menu-width});
2424
}
2525
}
2626

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

+9
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,15 @@ export interface Column {
183183
*/
184184
queryField?: string;
185185

186+
/**
187+
* When you do not know at hand the name of the Field to use for querying,
188+
* the lib will run your callback to find out which Field name you want to use by the logic you defined.
189+
* Useful when you only know the Field name by executing a certain logic in order to get the Field name to query from.
190+
* @param {string} item data context
191+
* @return {string} name of the Field that will end up being used to query
192+
*/
193+
queryFieldNameGetterFn?: (dataContext: any) => string;
194+
186195
/**
187196
* Similar to "queryField" but only used when Filtering (please note that it has higher precendence over "queryField").
188197
* Useful when you want to display a certain field to the UI, but you want to use another field to query for Filtering.

src/app/modules/angular-slickgrid/services/__tests__/filter.service.spec.ts

+36
Original file line numberDiff line numberDiff line change
@@ -788,6 +788,42 @@ describe('FilterService', () => {
788788

789789
expect(output).toBe(true);
790790
});
791+
792+
it('should execute "queryFieldNameGetterFn()" callback and return True when input value matches the full name', () => {
793+
const mockColumn1 = { id: 'name', field: 'name', filterable: true, queryFieldNameGetterFn: (dataContext) => 'fullName' } as Column;
794+
jest.spyOn(gridStub, 'getColumns').mockReturnValue([mockColumn1]);
795+
jest.spyOn(dataViewStub, 'getIdxById').mockReturnValue(0);
796+
797+
service.init(gridStub);
798+
const columnFilters = { name: { columnDef: mockColumn1, columnId: 'name', operator: 'EQ', searchTerms: ['John Doe'] } };
799+
const output = service.customLocalFilter(mockItem1, { dataView: dataViewStub, grid: gridStub, columnFilters });
800+
801+
expect(output).toBe(true);
802+
});
803+
804+
it('should execute "queryFieldNameGetterFn()" callback and return False when input value is not fullName but just the firstName', () => {
805+
const mockColumn1 = { id: 'name', field: 'name', filterable: true, queryFieldNameGetterFn: (dataContext) => 'fullName' } as Column;
806+
jest.spyOn(gridStub, 'getColumns').mockReturnValue([mockColumn1]);
807+
jest.spyOn(dataViewStub, 'getIdxById').mockReturnValue(0);
808+
809+
service.init(gridStub);
810+
const columnFilters = { name: { columnDef: mockColumn1, columnId: 'name', operator: 'EQ', searchTerms: ['John'] } };
811+
const output = service.customLocalFilter(mockItem1, { dataView: dataViewStub, grid: gridStub, columnFilters });
812+
813+
expect(output).toBe(false);
814+
});
815+
816+
it('should execute "queryFieldNameGetterFn()" callback and return False when input value is not fullName but just the lastName', () => {
817+
const mockColumn1 = { id: 'name', field: 'name', filterable: true, queryFieldNameGetterFn: (dataContext) => 'fullName' } as Column;
818+
jest.spyOn(gridStub, 'getColumns').mockReturnValue([mockColumn1]);
819+
jest.spyOn(dataViewStub, 'getIdxById').mockReturnValue(0);
820+
821+
service.init(gridStub);
822+
const columnFilters = { name: { columnDef: mockColumn1, columnId: 'name', operator: 'EQ', searchTerms: ['Doe'] } };
823+
const output = service.customLocalFilter(mockItem1, { dataView: dataViewStub, grid: gridStub, columnFilters });
824+
825+
expect(output).toBe(false);
826+
});
791827
});
792828

793829
describe('onBackendFilterChange method', () => {

src/app/modules/angular-slickgrid/services/__tests__/sort.service.spec.ts

+18
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,24 @@ describe('SortService', () => {
561561
]);
562562
});
563563

564+
it('should sort the data with 2 sorters which the second is by executing the "queryFieldNameGetterFn()" callback and sort by the field returned by it', () => {
565+
const mockSortedCols = [
566+
{ sortCol: { id: 'address', field: 'address', queryField: 'lastName' }, sortAsc: true },
567+
{ sortCol: { id: 'random', field: 'random', queryFieldNameGetterFn: (dataContext) => 'zip' }, sortAsc: false },
568+
] as ColumnSort[];
569+
570+
dataset.sort((row1, row2) => service.sortComparer(mockSortedCols, row1, row2));
571+
572+
expect(dataset).toEqual([
573+
{ firstName: 'John', lastName: 'Doe', age: 22, address: { zip: 123456 } },
574+
{ firstName: 'Jane', lastName: 'Doe', age: 27, address: { zip: 123456 } },
575+
{ firstName: 'Christopher', lastName: 'McDonald', age: 40, address: { zip: 555555 } },
576+
{ firstName: 'Erla', lastName: 'Richard', age: 101, address: { zip: 444444 } },
577+
{ firstName: 'Barbara', lastName: 'Smith', age: 1, address: { zip: 222222 } },
578+
{ firstName: 'Jane', lastName: 'Smith', age: 40, address: { zip: 333333 } },
579+
]);
580+
});
581+
564582
it('should sort the data with a sorter that is a complex object (following the dot notation in its field name)', () => {
565583
const mockSortedCols = [
566584
{ sortCol: { id: 'address', field: 'address.zip' }, sortAsc: true },

src/app/modules/angular-slickgrid/services/filter.service.ts

+7-4
Original file line numberDiff line numberDiff line change
@@ -274,14 +274,17 @@ export class FilterService {
274274
}
275275

276276
const dataKey = columnDef.dataKey;
277-
const fieldName = columnDef.queryFieldFilter || columnDef.queryField || columnDef.field;
278277
const fieldType = columnDef.type || FieldType.string;
279278
const filterSearchType = (columnDef.filterSearchType) ? columnDef.filterSearchType : null;
280-
let cellValue = item[fieldName];
279+
let queryFieldName = columnDef.queryFieldFilter || columnDef.queryField || columnDef.field || '';
280+
if (typeof columnDef.queryFieldNameGetterFn === 'function') {
281+
queryFieldName = columnDef.queryFieldNameGetterFn(item);
282+
}
283+
let cellValue = item[queryFieldName];
281284

282285
// when item is a complex object (dot "." notation), we need to filter the value contained in the object tree
283-
if (fieldName.indexOf('.') >= 0) {
284-
cellValue = getDescendantProperty(item, fieldName);
286+
if (queryFieldName && queryFieldName.indexOf('.') >= 0) {
287+
cellValue = getDescendantProperty(item, queryFieldName);
285288
}
286289

287290
// if we find searchTerms use them but make a deep copy so that we don't affect original array

src/app/modules/angular-slickgrid/services/sort.service.ts

+25-14
Original file line numberDiff line numberDiff line change
@@ -260,29 +260,40 @@ export class SortService {
260260

261261
sortComparer(sortColumns: ColumnSort[], dataRow1: any, dataRow2: any) {
262262
if (Array.isArray(sortColumns)) {
263-
for (let i = 0, l = sortColumns.length; i < l; i++) {
264-
const columnSortObj = sortColumns[i];
265-
if (columnSortObj && columnSortObj.sortCol) {
266-
const sortDirection = columnSortObj.sortAsc ? SortDirectionNumber.asc : SortDirectionNumber.desc;
267-
const sortField = columnSortObj.sortCol.queryFieldSorter || columnSortObj.sortCol.queryField || columnSortObj.sortCol.field;
268-
const fieldType = columnSortObj.sortCol.type || FieldType.string;
269-
let value1 = dataRow1[sortField];
270-
let value2 = dataRow2[sortField];
263+
for (const sortColumn of sortColumns) {
264+
if (sortColumn && sortColumn.sortCol) {
265+
const columnDef = sortColumn.sortCol;
266+
const querySortField = sortColumn.sortCol;
267+
const sortDirection = sortColumn.sortAsc ? SortDirectionNumber.asc : SortDirectionNumber.desc;
268+
let queryFieldName1 = querySortField.queryFieldSorter || querySortField.queryField || querySortField.field;
269+
let queryFieldName2 = queryFieldName1;
270+
const fieldType = querySortField.type || FieldType.string;
271+
272+
// if user provided a query field name getter callback, we need to get the name on each item independently
273+
if (typeof columnDef.queryFieldNameGetterFn === 'function') {
274+
queryFieldName1 = columnDef.queryFieldNameGetterFn(dataRow1);
275+
queryFieldName2 = columnDef.queryFieldNameGetterFn(dataRow2);
276+
}
277+
278+
let value1 = dataRow1[queryFieldName1];
279+
let value2 = dataRow2[queryFieldName2];
271280

272281
// when item is a complex object (dot "." notation), we need to filter the value contained in the object tree
273-
if (sortField && sortField.indexOf('.') >= 0) {
274-
value1 = getDescendantProperty(dataRow1, sortField);
275-
value2 = getDescendantProperty(dataRow2, sortField);
282+
if (queryFieldName1 && queryFieldName1.indexOf('.') >= 0) {
283+
value1 = getDescendantProperty(dataRow1, queryFieldName1);
284+
}
285+
if (queryFieldName2 && queryFieldName2.indexOf('.') >= 0) {
286+
value2 = getDescendantProperty(dataRow2, queryFieldName2);
276287
}
277288

278289
// user could provide his own custom Sorter
279-
if (columnSortObj.sortCol && columnSortObj.sortCol.sorter) {
280-
const customSortResult = columnSortObj.sortCol.sorter(value1, value2, sortDirection, columnSortObj.sortCol);
290+
if (querySortField && querySortField.sorter) {
291+
const customSortResult = querySortField.sorter(value1, value2, sortDirection, querySortField);
281292
if (customSortResult !== SortDirectionNumber.neutral) {
282293
return customSortResult;
283294
}
284295
} else {
285-
const sortResult = sortByFieldType(fieldType, value1, value2, sortDirection, columnSortObj.sortCol);
296+
const sortResult = sortByFieldType(fieldType, value1, value2, sortDirection, querySortField);
286297
if (sortResult !== SortDirectionNumber.neutral) {
287298
return sortResult;
288299
}

test/cypress/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
"author": "Ghislain B.",
1212
"license": "MIT",
1313
"devDependencies": {
14-
"cypress": "^4.3.0",
14+
"cypress": "^4.4.1",
1515
"mocha": "^5.2.0",
1616
"mochawesome": "^3.1.2",
1717
"mochawesome-merge": "^1.0.7",

0 commit comments

Comments
 (0)