From 87ea2f7fd03590919ab55813da755a408c56c9a4 Mon Sep 17 00:00:00 2001 From: Ghislain Beaulac Date: Tue, 9 Jun 2020 08:52:32 -0400 Subject: [PATCH 1/3] feat(typings): add more TS Generic Types --- .../examples/grid-graphql-nopage.component.ts | 24 ++++-- .../components/angular-slickgrid.component.ts | 9 ++- .../extensions/extensionUtility.ts | 6 +- .../extensions/rowDetailViewExtension.ts | 2 +- .../models/column.interface.ts | 13 ++-- .../models/dataView.interface.ts | 6 +- .../graphqlPaginatedResult.interface.ts | 4 +- .../models/graphqlResult.interface.ts | 4 +- .../models/grouping.interface.ts | 6 +- .../models/slickGrid.interface.ts | 2 +- .../services/collection.service.ts | 18 ++--- .../services/grid.service.ts | 74 +++++++++---------- .../angular-slickgrid/services/utilities.ts | 38 +++++----- 13 files changed, 111 insertions(+), 95 deletions(-) diff --git a/src/app/examples/grid-graphql-nopage.component.ts b/src/app/examples/grid-graphql-nopage.component.ts index 9119954f8..06554a1da 100644 --- a/src/app/examples/grid-graphql-nopage.component.ts +++ b/src/app/examples/grid-graphql-nopage.component.ts @@ -17,6 +17,20 @@ import { Observable } from 'rxjs'; const COUNTRIES_API = 'https://countries.trevorblades.com/'; +export interface Country { + countryCode: string; + countryName: string; + countryNative: string; + countryPhone: number; + countryCurrency: string; + countryEmoji: string; + continentCode: string; + continentName: string; + languageCode: string; + languageName: string; + languageNative: string; +}; + @Component({ templateUrl: './grid-graphql-nopage.component.html', styleUrls: ['./grid-graphql-nopage.component.scss'], @@ -155,7 +169,7 @@ export class GridGraphqlWithoutPaginationComponent implements OnInit { // you can define the onInit callback OR enable the "executeProcessCommandOnInit" flag in the service init preProcess: () => !this.isDataLoaded ? this.displaySpinner(true) : '', process: (query) => this.getCountries(query), - postProcess: (result: GraphqlResult) => { + postProcess: (result: GraphqlResult) => { this.metrics = result.metrics; this.displaySpinner(false); this.isDataLoaded = true; @@ -178,8 +192,8 @@ export class GridGraphqlWithoutPaginationComponent implements OnInit { // -- /** Calling the GraphQL backend API to get the Countries with the Query created by the "process" method of GraphqlService */ - getCountries(query: string): Observable { - return this.http.post(COUNTRIES_API, { query }); + getCountries(query: string): Observable> { + return this.http.post>(COUNTRIES_API, { query }); } /** @@ -189,7 +203,7 @@ export class GridGraphqlWithoutPaginationComponent implements OnInit { */ getContinents() { const continentQuery = `query { continents { code, name }}`; - return this.http.post(COUNTRIES_API, { query: continentQuery }); + return this.http.post>(COUNTRIES_API, { query: continentQuery }); } /** @@ -199,6 +213,6 @@ export class GridGraphqlWithoutPaginationComponent implements OnInit { */ getLanguages() { const languageQuery = `query { languages { code, name }}`; - return this.http.post(COUNTRIES_API, { query: languageQuery }); + return this.http.post>(COUNTRIES_API, { query: languageQuery }); } } diff --git a/src/app/modules/angular-slickgrid/components/angular-slickgrid.component.ts b/src/app/modules/angular-slickgrid/components/angular-slickgrid.component.ts index 11573a319..bcb8b3942 100644 --- a/src/app/modules/angular-slickgrid/components/angular-slickgrid.component.ts +++ b/src/app/modules/angular-slickgrid/components/angular-slickgrid.component.ts @@ -20,6 +20,7 @@ import { BackendServiceApi, BackendServiceOption, Column, + ColumnEditor, CustomFooterOption, ExtensionName, GraphqlPaginatedResult, @@ -914,7 +915,7 @@ export class AngularSlickgridComponent implements AfterViewInit, OnDestroy, OnIn /** Load the Editor Collection asynchronously and replace the "collection" property when Observable resolves */ private loadEditorCollectionAsync(column: Column) { - const collectionAsync = column && column.editor && column.editor.collectionAsync; + const collectionAsync = column && column.editor && (column.editor as ColumnEditor).collectionAsync; if (collectionAsync instanceof Observable) { this.subscriptions.push( collectionAsync.subscribe((resolvedCollection) => this.updateEditorCollection(column, resolvedCollection)) @@ -1083,14 +1084,14 @@ export class AngularSlickgridComponent implements AfterViewInit, OnDestroy, OnIn * Since this is called after the async call resolves, the pointer will not be the same as the "column" argument passed. * Once we found the new pointer, we will reassign the "editor" and "collection" to the "internalColumnEditor" so it has newest collection */ - private updateEditorCollection(column: Column, newCollection: any[]) { - column.editor.collection = newCollection; + private updateEditorCollection(column: Column, newCollection: T[]) { + (column.editor as ColumnEditor).collection = newCollection; // find the new column reference pointer const columns = this.grid.getColumns(); if (Array.isArray(columns)) { const columnRef: Column = columns.find((col: Column) => col.id === column.id); - columnRef.internalColumnEditor = column.editor; + columnRef.internalColumnEditor = column.editor as ColumnEditor; } } } diff --git a/src/app/modules/angular-slickgrid/extensions/extensionUtility.ts b/src/app/modules/angular-slickgrid/extensions/extensionUtility.ts index e100c04ed..962ac9b85 100644 --- a/src/app/modules/angular-slickgrid/extensions/extensionUtility.ts +++ b/src/app/modules/angular-slickgrid/extensions/extensionUtility.ts @@ -129,10 +129,10 @@ export class ExtensionUtility { * @params items array * @param property name to sort with */ - sortItems(items: any[], propertyName: string) { + sortItems(items: T[], propertyName: string) { // sort the custom items by their position in the list if (Array.isArray(items)) { - items.sort((itemA: any, itemB: any) => { + items.sort((itemA: T, itemB: T) => { if (itemA && itemB && itemA.hasOwnProperty(propertyName) && itemB.hasOwnProperty(propertyName)) { return itemA[propertyName] - itemB[propertyName]; } @@ -142,7 +142,7 @@ export class ExtensionUtility { } /** Translate the an array of items from an input key and assign to the output key */ - translateItems(items: any[], inputKey: string, outputKey: string) { + translateItems(items: T[], inputKey: string, outputKey: string) { if (Array.isArray(items)) { for (const item of items) { if (item[inputKey]) { diff --git a/src/app/modules/angular-slickgrid/extensions/rowDetailViewExtension.ts b/src/app/modules/angular-slickgrid/extensions/rowDetailViewExtension.ts index bd6102aa5..41020a3ba 100644 --- a/src/app/modules/angular-slickgrid/extensions/rowDetailViewExtension.ts +++ b/src/app/modules/angular-slickgrid/extensions/rowDetailViewExtension.ts @@ -174,7 +174,7 @@ export class RowDetailViewExtension implements Extension { this.rowDetailViewOptions.onAsyncEndUpdate(e, args); } }); - this._eventHandler.subscribe(this._addon.onAfterRowDetailToggle, (e: any, args: { grid: any; item: any; expandedRows: any[]; }) => { + this._eventHandler.subscribe(this._addon.onAfterRowDetailToggle, (e: any, args: { grid: any; item: any; expandedRows: number[]; }) => { // display preload template & re-render all the other Detail Views after toggling // the preload View will eventually go away once the data gets loaded after the "onAsyncEndUpdate" event this.renderPreloadView(); diff --git a/src/app/modules/angular-slickgrid/models/column.interface.ts b/src/app/modules/angular-slickgrid/models/column.interface.ts index 80992fa9e..ddf6aa76b 100644 --- a/src/app/modules/angular-slickgrid/models/column.interface.ts +++ b/src/app/modules/angular-slickgrid/models/column.interface.ts @@ -1,6 +1,7 @@ import { CellMenu } from './cellMenu.interface'; import { ColumnEditor } from './columnEditor.interface'; import { ColumnFilter } from './columnFilter.interface'; +import { Editor } from './editor.interface'; import { EditorValidator } from './editorValidator.interface'; import { FieldType } from './fieldType.enum'; import { Formatter } from './formatter.interface'; @@ -11,9 +12,9 @@ import { MenuCommandItem } from './menuCommandItem.interface'; import { OnEventArgs } from './onEventArgs.interface'; import { Sorter } from './sorter.interface'; -export interface Column { +export interface Column { /** async background post-rendering formatter */ - asyncPostRender?: (domCellNode: any, row: number, dataContext: any, columnDef: Column) => void; + asyncPostRender?: (domCellNode: any, row: number, dataContext: T, columnDef: Column) => void; /** Row Move Behavior, used by the Row Move Manager Plugin */ behavior?: string; @@ -37,13 +38,13 @@ export interface Column { colspan?: number | '*'; /** Data key, for example this could be used as a property key for complex object comparison (e.g. dataKey: 'id') */ - dataKey?: any; + dataKey?: string; /** Do we want default sort to be ascending? True by default */ defaultSortAsc?: boolean; /** Any inline editor function that implements Editor for the cell value or ColumnEditor */ - editor?: any | ColumnEditor; + editor?: ColumnEditor | Editor; /** Default to false, which leads to exclude the column title from the Column Picker. */ excludeFromColumnPicker?: boolean; @@ -148,7 +149,7 @@ export interface Column { internalColumnEditor?: ColumnEditor; /** Label key, for example this could be used as a property key for complex object label display (e.g. dataKey: 'name') */ - labelKey?: any; + labelKey?: string; /** Maximum Width of the column in pixels (number only). */ maxWidth?: number; @@ -190,7 +191,7 @@ export interface Column { * @param {string} item data context * @return {string} name of the Field that will end up being used to query */ - queryFieldNameGetterFn?: (dataContext: any) => string; + queryFieldNameGetterFn?: (dataContext: T) => string; /** * Similar to "queryField" but only used when Filtering (please note that it has higher precendence over "queryField"). diff --git a/src/app/modules/angular-slickgrid/models/dataView.interface.ts b/src/app/modules/angular-slickgrid/models/dataView.interface.ts index cee5ea00e..a86f8250d 100644 --- a/src/app/modules/angular-slickgrid/models/dataView.interface.ts +++ b/src/app/modules/angular-slickgrid/models/dataView.interface.ts @@ -81,7 +81,7 @@ export interface DataView { getItemByIdx(idx: number): number; /** Get row index in the dataset by its Id */ - getIdxById(id: string | number): number; + getIdxById(id: string | number): number | undefined; /** Get item metadata at specific index */ getItemMetadata(index: number): any; @@ -96,7 +96,7 @@ export interface DataView { getRowByItem(item: any): number; /** Get row number of an item in the dataset by its Id */ - getRowById(id: string | number): number; + getRowById(id: string | number): number | undefined; /** Insert an item to the dataset before a specific index */ insertItem(insertBefore: number, item: any): void; @@ -173,7 +173,7 @@ export interface DataView { updateIdxById(startingIndex: number): void; /** Update an item in the dataset by its Id */ - updateItem(id: string | number, item: any): void; + updateItem(id: string | number, item: T): void; // --------------------------- // Available DataView Events diff --git a/src/app/modules/angular-slickgrid/models/graphqlPaginatedResult.interface.ts b/src/app/modules/angular-slickgrid/models/graphqlPaginatedResult.interface.ts index 746ad38ce..f74e3a630 100644 --- a/src/app/modules/angular-slickgrid/models/graphqlPaginatedResult.interface.ts +++ b/src/app/modules/angular-slickgrid/models/graphqlPaginatedResult.interface.ts @@ -1,11 +1,11 @@ import { Metrics } from './metrics.interface'; import { Statistic } from './statistic.interface'; -export interface GraphqlPaginatedResult { +export interface GraphqlPaginatedResult { data: { [datasetName: string]: { /** result set of data objects (array of data) */ - nodes: any[]; + nodes: T[]; /** Total count of items in the table (needed for the Pagination to work) */ totalCount: number; diff --git a/src/app/modules/angular-slickgrid/models/graphqlResult.interface.ts b/src/app/modules/angular-slickgrid/models/graphqlResult.interface.ts index 4ec47150f..5a6597be3 100644 --- a/src/app/modules/angular-slickgrid/models/graphqlResult.interface.ts +++ b/src/app/modules/angular-slickgrid/models/graphqlResult.interface.ts @@ -1,9 +1,9 @@ import { Metrics } from './metrics.interface'; import { Statistic } from './statistic.interface'; -export interface GraphqlResult { +export interface GraphqlResult { data: { - [datasetName: string]: any[]; + [datasetName: string]: T[]; }; /** Some metrics of the last executed query (startTime, endTime, executionTime, itemCount, totalItemCount) */ diff --git a/src/app/modules/angular-slickgrid/models/grouping.interface.ts b/src/app/modules/angular-slickgrid/models/grouping.interface.ts index f95c459fd..d029e8211 100644 --- a/src/app/modules/angular-slickgrid/models/grouping.interface.ts +++ b/src/app/modules/angular-slickgrid/models/grouping.interface.ts @@ -3,9 +3,9 @@ import { GroupingComparerItem } from './groupingComparerItem.interface'; import { GroupingFormatterItem } from './groupingFormatterItem.interface'; import { SortDirectionNumber } from './sortDirectionNumber.enum'; -export type GroupingGetterFunction = (value: any) => string; +export type GroupingGetterFunction = (value: T) => string; -export interface Grouping { +export interface Grouping { /** Grouping Aggregators array */ aggregators?: Aggregator[]; @@ -31,7 +31,7 @@ export interface Grouping { formatter?: (g: GroupingFormatterItem) => string; /** Getter of the Column to be Grouped */ - getter?: string | GroupingGetterFunction; + getter?: string | GroupingGetterFunction; /** Defaults to false, lazy load the Group Totals Calculation */ lazyTotalsCalculation?: boolean; diff --git a/src/app/modules/angular-slickgrid/models/slickGrid.interface.ts b/src/app/modules/angular-slickgrid/models/slickGrid.interface.ts index 61d0244a5..3dcdc22e9 100644 --- a/src/app/modules/angular-slickgrid/models/slickGrid.interface.ts +++ b/src/app/modules/angular-slickgrid/models/slickGrid.interface.ts @@ -154,7 +154,7 @@ export interface SlickGrid { * Returns the databinding item at a given position. * @param index Item index. */ - getDataItem(index: number): any; + getDataItem(index: number): T; /** Returns the size of the databinding source. */ getDataLength(): number; diff --git a/src/app/modules/angular-slickgrid/services/collection.service.ts b/src/app/modules/angular-slickgrid/services/collection.service.ts index a537bbf93..3132acf3c 100644 --- a/src/app/modules/angular-slickgrid/services/collection.service.ts +++ b/src/app/modules/angular-slickgrid/services/collection.service.ts @@ -14,7 +14,7 @@ import { sortByFieldType } from '../sorters/sorterUtilities'; import { uniqueArray } from './utilities'; @Injectable() -export class CollectionService { +export class CollectionService { constructor(@Optional() private translate: TranslateService) { } /** @@ -22,8 +22,8 @@ export class CollectionService { * @param collection * @param filterByOptions */ - filterCollection(collection: any[], filterByOptions: CollectionFilterBy | CollectionFilterBy[], filterResultBy: FilterMultiplePassType | FilterMultiplePassTypeString | null = FilterMultiplePassType.chain): any[] { - let filteredCollection: any[] = []; + filterCollection(collection: T[], filterByOptions: CollectionFilterBy | CollectionFilterBy[], filterResultBy: FilterMultiplePassType | FilterMultiplePassTypeString | null = FilterMultiplePassType.chain): T[] { + let filteredCollection: T[] = []; // when it's array, we will use the new filtered collection after every pass // basically if input collection has 10 items on 1st pass and 1 item is filtered out, then on 2nd pass the input collection will be 9 items @@ -50,8 +50,8 @@ export class CollectionService { * @param collection * @param filterBy */ - singleFilterCollection(collection: any[], filterBy: CollectionFilterBy): any[] { - let filteredCollection: any[] = []; + singleFilterCollection(collection: T[], filterBy: CollectionFilterBy): T[] { + let filteredCollection: T[] = []; if (filterBy) { const objectProperty = filterBy.property; @@ -71,14 +71,14 @@ export class CollectionService { if (objectProperty) { filteredCollection = collection.filter((item) => item[objectProperty].toString().indexOf(value.toString()) !== -1); } else { - filteredCollection = collection.filter((item) => (item !== null && item !== undefined) && item.toString().indexOf(value.toString()) !== -1); + filteredCollection = collection.filter((item: any) => (item !== null && item !== undefined) && item.toString().indexOf(value.toString()) !== -1); } break; case OperatorType.notContains: if (objectProperty) { filteredCollection = collection.filter((item) => item[objectProperty].toString().indexOf(value.toString()) === -1); } else { - filteredCollection = collection.filter((item) => (item !== null && item !== undefined) && item.toString().indexOf(value.toString()) === -1); + filteredCollection = collection.filter((item: any) => (item !== null && item !== undefined) && item.toString().indexOf(value.toString()) === -1); } break; case OperatorType.notEqual: @@ -101,12 +101,12 @@ export class CollectionService { * @param sortByOptions * @param enableTranslateLabel */ - sortCollection(columnDef: Column, collection: any[], sortByOptions: CollectionSortBy | CollectionSortBy[], enableTranslateLabel?: boolean): any[] { + sortCollection(columnDef: Column, collection: T[], sortByOptions: CollectionSortBy | CollectionSortBy[], enableTranslateLabel?: boolean): T[] { if (enableTranslateLabel && (!this.translate || !this.translate.instant)) { throw new Error('[Angular-Slickgrid] requires "ngx-translate" to be installed and configured when the grid option "enableTranslate" is enabled.'); } - let sortedCollection: any[] = []; + let sortedCollection: T[] = []; if (sortByOptions) { if (Array.isArray(sortByOptions)) { diff --git a/src/app/modules/angular-slickgrid/services/grid.service.ts b/src/app/modules/angular-slickgrid/services/grid.service.ts index f4bf4604a..8b450d4b6 100644 --- a/src/app/modules/angular-slickgrid/services/grid.service.ts +++ b/src/app/modules/angular-slickgrid/services/grid.service.ts @@ -70,7 +70,7 @@ export class GridService { } /** Get only visible column definitions and also include any extra columns by some plugins (like Row Selection, Row Detail, ...) */ - getVisibleColumnDefinitions() { + getVisibleColumnDefinitions(): Column[] { return this.sharedService.visibleColumns; } @@ -98,11 +98,11 @@ export class GridService { } /** Get data item by it's row index number */ - getDataItemByRowNumber(rowNumber: number) { + getDataItemByRowNumber(rowNumber: number): T { if (!this._grid || typeof this._grid.getDataItem !== 'function') { throw new Error(`We could not find SlickGrid Grid object or it's "getDataItem" method`); } - return this._grid.getDataItem(rowNumber); + return this._grid.getDataItem(rowNumber); } /** Chain the item Metadata with our implementation of Metadata at given row index */ @@ -184,16 +184,16 @@ export class GridService { } /** Get the Data Item from a grid row index */ - getDataItemByRowIndex(index: number): any { + getDataItemByRowIndex(index: number): T { if (!this._grid || typeof this._grid.getDataItem !== 'function') { throw new Error('We could not find SlickGrid Grid object and/or "getDataItem" method'); } - return this._grid.getDataItem(index); + return this._grid.getDataItem(index); } /** Get the Data Item from an array of grid row indexes */ - getDataItemByRowIndexes(indexes: number[]): any[] { + getDataItemByRowIndexes(indexes: number[]): T[] { if (!this._grid || typeof this._grid.getDataItem !== 'function') { throw new Error('We could not find SlickGrid Grid object and/or "getDataItem" method'); } @@ -218,13 +218,13 @@ export class GridService { } /** Get the currently selected rows item data */ - getSelectedRowsDataItem(): any[] { + getSelectedRowsDataItem(): T[] { if (!this._grid || typeof this._grid.getSelectedRows !== 'function') { throw new Error('We could not find SlickGrid Grid object and/or "getSelectedRows" method'); } const selectedRowIndexes = this._grid.getSelectedRows(); - return this.getDataItemByRowIndexes(selectedRowIndexes); + return this.getDataItemByRowIndexes(selectedRowIndexes); } /** Select the selected row by a row index */ @@ -295,14 +295,14 @@ export class GridService { * @param options: provide the possibility to do certain actions after or during the upsert (highlightRow, resortGrid, selectRow, triggerEvent) * @return rowIndex: typically index 0 when adding to position "top" or a different number when adding to the "bottom" */ - addItem(item: any, options?: GridServiceInsertOption): number { + addItem(item: T, options?: GridServiceInsertOption): number { options = { ...GridServiceInsertOptionDefaults, ...options }; if (!this._grid || !this._gridOptions || !this._dataView) { throw new Error('We could not find SlickGrid Grid, DataView objects'); } const idPropName = this._gridOptions.datasetIdPropertyName || 'id'; - if (!item || !item.hasOwnProperty(idPropName)) { + if (!item || !(idPropName in item)) { throw new Error(`Adding an item requires the item to include an "${idPropName}" property`); } @@ -353,16 +353,16 @@ export class GridService { * @param item object arrays, which must contain unique "id" property and any other suitable properties * @param options: provide the possibility to do certain actions after or during the upsert (highlightRow, resortGrid, selectRow, triggerEvent) */ - addItems(items: any | any[], options?: GridServiceInsertOption): number[] { + addItems(items: T | T[], options?: GridServiceInsertOption): number[] { options = { ...GridServiceInsertOptionDefaults, ...options }; const idPropName = this._gridOptions.datasetIdPropertyName || 'id'; const rowNumbers: number[] = []; // loop through all items to add if (!Array.isArray(items)) { - return [this.addItem(items, options)]; + return [this.addItem(items, options)]; } else { - items.forEach((item: any) => this.addItem(item, { ...options, highlightRow: false, resortGrid: false, triggerEvent: false, selectRow: false })); + items.forEach((item: T) => this.addItem(item, { ...options, highlightRow: false, resortGrid: false, triggerEvent: false, selectRow: false })); } // do we want the item to be sorted in the grid, when set to False it will insert on first row (defaults to false) @@ -425,11 +425,11 @@ export class GridService { * @param options: provide the possibility to do certain actions after or during the upsert (triggerEvent) * @return item id deleted */ - deleteItem(item: any, options?: GridServiceDeleteOption): number | string { + deleteItem(item: T, options?: GridServiceDeleteOption): number | string { options = { ...GridServiceDeleteOptionDefaults, ...options }; const idPropName = this._gridOptions.datasetIdPropertyName || 'id'; - if (!item || !item.hasOwnProperty(idPropName)) { + if (!item || !(idPropName in item)) { throw new Error(`Deleting an item requires the item to include an "${idPropName}" property`); } return this.deleteItemById(item[idPropName], options); @@ -441,21 +441,21 @@ export class GridService { * @param options: provide the possibility to do certain actions after or during the upsert (triggerEvent) * @return item id deleted */ - deleteItems(items: any | any[], options?: GridServiceDeleteOption): number[] | string[] { + deleteItems(items: T | T[], options?: GridServiceDeleteOption): number[] | string[] { options = { ...GridServiceDeleteOptionDefaults, ...options }; const idPropName = this._gridOptions.datasetIdPropertyName || 'id'; // when it's not an array, we can call directly the single item delete if (!Array.isArray(items)) { - this.deleteItem(items, options); + this.deleteItem(items, options); return [items[idPropName]]; } const itemIds = []; - items.forEach((item: any) => { + items.forEach((item: T) => { if (item && item[idPropName] !== undefined) { itemIds.push(item[idPropName]); } - this.deleteItem(item, { ...options, triggerEvent: false }); + this.deleteItem(item, { ...options, triggerEvent: false }); }); // do we want to trigger an event after deleting the item @@ -543,16 +543,16 @@ export class GridService { * @param options: provide the possibility to do certain actions after or during the upsert (highlightRow, selectRow, triggerEvent) * @return grid row index */ - updateItem(item: any, options?: GridServiceUpdateOption): number { + updateItem(item: T, options?: GridServiceUpdateOption): number { options = { ...GridServiceUpdateOptionDefaults, ...options }; const idPropName = this._gridOptions.datasetIdPropertyName || 'id'; - const itemId = (!item || !item.hasOwnProperty(idPropName)) ? undefined : item[idPropName]; + const itemId = (!item || !(idPropName in item)) ? undefined : item[idPropName]; if (itemId === undefined) { throw new Error(`Calling Update of an item requires the item to include an "${idPropName}" property`); } - return this.updateItemById(itemId, item, options); + return this.updateItemById(itemId, item, options); } /** @@ -561,17 +561,17 @@ export class GridService { * @param options: provide the possibility to do certain actions after or during the upsert (highlightRow, selectRow, triggerEvent) * @return grid row indexes */ - updateItems(items: any | any[], options?: GridServiceUpdateOption): number[] { + updateItems(items: T | T[], options?: GridServiceUpdateOption): number[] { options = { ...GridServiceUpdateOptionDefaults, ...options }; // when it's not an array, we can call directly the single item update if (!Array.isArray(items)) { - return [this.updateItem(items, options)]; + return [this.updateItem(items, options)]; } const gridRowNumbers: number[] = []; items.forEach((item: any) => { - gridRowNumbers.push(this.updateItem(item, { ...options, highlightRow: false, selectRow: false, triggerEvent: false })); + gridRowNumbers.push(this.updateItem(item, { ...options, highlightRow: false, selectRow: false, triggerEvent: false })); }); // only highlight at the end, all at once @@ -600,7 +600,7 @@ export class GridService { * @param options: provide the possibility to do certain actions after or during the upsert (highlightRow, selectRow, triggerEvent) * @return grid row number */ - updateItemById(itemId: number | string, item: any, options?: GridServiceUpdateOption): number { + updateItemById(itemId: number | string, item: T, options?: GridServiceUpdateOption): number { options = { ...GridServiceUpdateOptionDefaults, ...options }; if (itemId === undefined) { throw new Error(`Cannot update a row without a valid "id"`); @@ -613,7 +613,7 @@ export class GridService { if (this._dataView.getIdxById(itemId) !== undefined) { // Update the item itself inside the dataView - this._dataView.updateItem(itemId, item); + this._dataView.updateItem(itemId, item); this._grid.updateRow(rowNumber); // do we want to scroll to the row so that it shows in the Viewport (UI) @@ -644,16 +644,16 @@ export class GridService { * @param item object which must contain a unique "id" property and any other suitable properties * @param options: provide the possibility to do certain actions after or during the upsert (highlightRow, resortGrid, selectRow, triggerEvent) */ - upsertItem(item: any, options?: GridServiceInsertOption): { added: number, updated: number } { + upsertItem(item: T, options?: GridServiceInsertOption): { added: number, updated: number } { options = { ...GridServiceInsertOptionDefaults, ...options }; const idPropName = this._gridOptions.datasetIdPropertyName || 'id'; - const itemId = (!item || !item.hasOwnProperty(idPropName)) ? undefined : item[idPropName]; + const itemId = (!item || !(idPropName in item)) ? undefined : item[idPropName]; if (itemId === undefined) { throw new Error(`Calling Upsert of an item requires the item to include an "${idPropName}" property`); } - return this.upsertItemById(itemId, item, options); + return this.upsertItemById(itemId, item, options); } /** @@ -662,15 +662,15 @@ export class GridService { * @param options: provide the possibility to do certain actions after or during the upsert (highlightRow, resortGrid, selectRow, triggerEvent) * @return row numbers in the grid */ - upsertItems(items: any | any[], options?: GridServiceInsertOption): { added: number, updated: number }[] { + upsertItems(items: T | T[], options?: GridServiceInsertOption): { added: number, updated: number }[] { options = { ...GridServiceInsertOptionDefaults, ...options }; // when it's not an array, we can call directly the single item update if (!Array.isArray(items)) { - return [this.upsertItem(items, options)]; + return [this.upsertItem(items, options)]; } const upsertedRows: { added: number, updated: number }[] = []; - items.forEach((item: any) => { - upsertedRows.push(this.upsertItem(item, { ...options, highlightRow: false, resortGrid: false, selectRow: false, triggerEvent: false })); + items.forEach((item: T) => { + upsertedRows.push(this.upsertItem(item, { ...options, highlightRow: false, resortGrid: false, selectRow: false, triggerEvent: false })); }); const rowNumbers = upsertedRows.map((upsertRow) => upsertRow.added !== undefined ? upsertRow.added : upsertRow.updated); // only highlight at the end, all at once @@ -706,7 +706,7 @@ export class GridService { * @param options: provide the possibility to do certain actions after or during the upsert (highlightRow, resortGrid, selectRow, triggerEvent) * @return grid row number in the grid */ - upsertItemById(itemId: number | string, item: any, options?: GridServiceInsertOption): { added: number, updated: number } { + upsertItemById(itemId: number | string, item: T, options?: GridServiceInsertOption): { added: number, updated: number } { let isItemAdded = false; options = { ...GridServiceInsertOptionDefaults, ...options }; if (itemId === undefined) { @@ -716,10 +716,10 @@ export class GridService { let rowNumberAdded: number; let rowNumberUpdated: number; if (this._dataView.getRowById(itemId) === undefined) { - rowNumberAdded = this.addItem(item, options); + rowNumberAdded = this.addItem(item, options); isItemAdded = true; } else { - rowNumberUpdated = this.updateItem(item, { highlightRow: options.highlightRow, selectRow: options.selectRow, triggerEvent: options.triggerEvent }); + rowNumberUpdated = this.updateItem(item, { highlightRow: options.highlightRow, selectRow: options.selectRow, triggerEvent: options.triggerEvent }); isItemAdded = false; } diff --git a/src/app/modules/angular-slickgrid/services/utilities.ts b/src/app/modules/angular-slickgrid/services/utilities.ts index 8d048afbb..ec51e5a65 100644 --- a/src/app/modules/angular-slickgrid/services/utilities.ts +++ b/src/app/modules/angular-slickgrid/services/utilities.ts @@ -14,7 +14,7 @@ declare const $: any; * @param inputItem * @param itemIdPropName */ -export function addToArrayWhenNotExists(inputArray: any[], inputItem: any, itemIdPropName = 'id') { +export function addToArrayWhenNotExists(inputArray: T[], inputItem: T, itemIdPropName = 'id') { let arrayRowIndex = -1; if (typeof inputItem === 'object' && inputItem.hasOwnProperty(itemIdPropName)) { arrayRowIndex = inputArray.findIndex((item) => item[itemIdPropName] === inputItem[itemIdPropName]); @@ -47,15 +47,15 @@ export function addWhiteSpaces(nbSpaces: number): string { * @param options you can provide the following options:: "parentPropName" (defaults to "parent"), "childrenPropName" (defaults to "children") and "identifierPropName" (defaults to "id") * @return roots - hierarchical data view array */ -export function convertParentChildArrayToHierarchicalView(flatArray: any[], options?: { parentPropName?: string; childrenPropName?: string; identifierPropName?: string; }): any[] { +export function convertParentChildArrayToHierarchicalView(flatArray: T[], options?: { parentPropName?: string; childrenPropName?: string; identifierPropName?: string; }): T[] { const childrenPropName = options && options.childrenPropName || 'children'; const parentPropName = options && options.parentPropName || '__parentId'; const identifierPropName = options && options.identifierPropName || 'id'; const hasChildrenFlagPropName = '__hasChildren'; const treeLevelPropName = '__treeLevel'; - const inputArray: any[] = $.extend(true, [], flatArray); + const inputArray: T[] = $.extend(true, [], flatArray); - const roots: any[] = []; // things without parent + const roots: T[] = []; // things without parent // make them accessible by guid on this map const all = {}; @@ -91,8 +91,8 @@ export function convertParentChildArrayToHierarchicalView(flatArray: any[], opti * @param options - you can provide "childrenPropName" (defaults to "children") * @return output - Parent/Child array */ -export function convertHierarchicalViewToParentChildArray(hierarchicalArray: any[], options?: { parentPropName?: string; childrenPropName?: string; identifierPropName?: string; }): any[] { - const outputArray: any[] = []; +export function convertHierarchicalViewToParentChildArray(hierarchicalArray: T[], options?: { parentPropName?: string; childrenPropName?: string; identifierPropName?: string; }): T[] { + const outputArray: T[] = []; convertHierarchicalViewToParentChildArrayByReference($.extend(true, [], hierarchicalArray), outputArray, options, 0); // the output array is the one passed as reference @@ -107,7 +107,7 @@ export function convertHierarchicalViewToParentChildArray(hierarchicalArray: any * @param treeLevel - tree level number * @param parentId - parent ID */ -export function convertHierarchicalViewToParentChildArrayByReference(hierarchicalArray: any[], outputArrayRef: any[], options?: { childrenPropName?: string; parentPropName?: string; hasChildrenFlagPropName?: string; treeLevelPropName?: string; identifierPropName?: string; }, treeLevel = 0, parentId?: string) { +export function convertHierarchicalViewToParentChildArrayByReference(hierarchicalArray: T[], outputArrayRef: T[], options?: { childrenPropName?: string; parentPropName?: string; hasChildrenFlagPropName?: string; treeLevelPropName?: string; identifierPropName?: string; }, treeLevel = 0, parentId?: string) { const childrenPropName = options && options.childrenPropName || 'children'; const identifierPropName = options && options.identifierPropName || 'id'; const hasChildrenFlagPropName = options && options.hasChildrenFlagPropName || '__hasChildren'; @@ -117,7 +117,7 @@ export function convertHierarchicalViewToParentChildArrayByReference(hierarchica if (Array.isArray(hierarchicalArray)) { for (const item of hierarchicalArray) { if (item) { - const itemExist = outputArrayRef.find((itm: any) => itm[identifierPropName] === item[identifierPropName]); + const itemExist = outputArrayRef.find((itm: T) => itm[identifierPropName] === item[identifierPropName]); if (!itemExist) { item[treeLevelPropName] = treeLevel; // save tree level ref item[parentPropName] = parentId || null; @@ -191,22 +191,22 @@ export function deepCopy(obj: any) { * @param predicate * @param childrenPropertyName */ -export function findItemInHierarchicalStructure(hierarchicalArray: any, predicate: (item: any) => boolean, childrenPropertyName: string): any { +export function findItemInHierarchicalStructure(hierarchicalArray: T[], predicate: (item: T) => boolean, childrenPropertyName: string): T { if (!childrenPropertyName) { throw new Error('findRecursive requires parameter "childrenPropertyName"'); } const initialFind = hierarchicalArray.find(predicate); - const elementsWithChildren = hierarchicalArray.filter((x: any) => x.hasOwnProperty(childrenPropertyName) && x[childrenPropertyName]); + const elementsWithChildren = hierarchicalArray.filter((x: T) => x.hasOwnProperty(childrenPropertyName) && x[childrenPropertyName]); if (initialFind) { return initialFind; } else if (elementsWithChildren.length) { - const childElements: any[] = []; - elementsWithChildren.forEach((item: any) => { + const childElements: T[] = []; + elementsWithChildren.forEach((item: T) => { if (item.hasOwnProperty(childrenPropertyName)) { childElements.push(...item[childrenPropertyName]); } }); - return findItemInHierarchicalStructure(childElements, predicate, childrenPropertyName); + return findItemInHierarchicalStructure(childElements, predicate, childrenPropertyName); } return undefined; } @@ -277,7 +277,7 @@ export function htmlEntityEncode(input: any): string { * @param [orderMatters=false] flag if the order matters, if not arrays will be sorted before comparison * @return boolean true if equal, else false */ -export function charArraysEqual(a: any[], b: any[], orderMatters: boolean = false): boolean { +export function charArraysEqual(a: T[], b: T[], orderMatters: boolean = false): boolean { if (!a || !b || !Array.isArray(a) || !Array.isArray(a)) { return false; } @@ -334,7 +334,7 @@ export function castToPromise(input: Promise | Observable, fromServiceN * @param any [defaultVal={}] the default value to return * @return object the found object or default value */ -export function findOrDefault(array: any[], logic: (item: any) => boolean, defaultVal = {}): any { +export function findOrDefault(array: any[], logic: (item: any) => boolean, defaultVal = {}): any { return array.find(logic) || defaultVal; } @@ -428,7 +428,7 @@ export function formatNumber(input: number | string, minDecimal?: number, maxDec } /** From a dot (.) notation path, find and return a property within an object given a path */ -export function getDescendantProperty(obj: any, path: string): any { +export function getDescendantProperty(obj: T, path: string): any { return path.split('.').reduce((acc, part) => acc && acc[part], obj); } @@ -841,7 +841,7 @@ export function sanitizeHtmlToText(htmlString: string) { } /** Set the object value of deeper node from a given dot (.) notation path (e.g.: "user.firstName") */ -export function setDeepValue(obj: any, path: string | string[], value: any) { +export function setDeepValue(obj: T, path: string | string[], value: any) { if (typeof path === 'string') { path = path.split('.'); } @@ -946,9 +946,9 @@ export function toSnakeCase(inputStr: string): string { * @param objectProperty optionally provide an object property to compare (example: 'id') * @return array output without duplicates */ -export function uniqueArray(arr: any[]): any[] { +export function uniqueArray(arr: T[]): T[] { if (Array.isArray(arr) && arr.length > 0) { - return arr.filter((item: any, index: number) => { + return arr.filter((item: T, index: number) => { return arr.indexOf(item) >= index; }); } From b67e657c012ec212b508835f95c04616a02e0ea1 Mon Sep 17 00:00:00 2001 From: Ghislain Beaulac Date: Tue, 9 Jun 2020 09:09:14 -0400 Subject: [PATCH 2/3] refactor(core): fix failing E2E tests --- .../modules/angular-slickgrid/models/column.interface.ts | 2 +- src/app/modules/angular-slickgrid/services/grid.service.ts | 6 +++--- test/cypress/package.json | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/app/modules/angular-slickgrid/models/column.interface.ts b/src/app/modules/angular-slickgrid/models/column.interface.ts index ddf6aa76b..e66150d48 100644 --- a/src/app/modules/angular-slickgrid/models/column.interface.ts +++ b/src/app/modules/angular-slickgrid/models/column.interface.ts @@ -44,7 +44,7 @@ export interface Column { defaultSortAsc?: boolean; /** Any inline editor function that implements Editor for the cell value or ColumnEditor */ - editor?: ColumnEditor | Editor; + editor?: any | ColumnEditor; /** Default to false, which leads to exclude the column title from the Column Picker. */ excludeFromColumnPicker?: boolean; diff --git a/src/app/modules/angular-slickgrid/services/grid.service.ts b/src/app/modules/angular-slickgrid/services/grid.service.ts index 8b450d4b6..b5376a7d2 100644 --- a/src/app/modules/angular-slickgrid/services/grid.service.ts +++ b/src/app/modules/angular-slickgrid/services/grid.service.ts @@ -102,7 +102,7 @@ export class GridService { if (!this._grid || typeof this._grid.getDataItem !== 'function') { throw new Error(`We could not find SlickGrid Grid object or it's "getDataItem" method`); } - return this._grid.getDataItem(rowNumber); + return this._grid.getDataItem(rowNumber); } /** Chain the item Metadata with our implementation of Metadata at given row index */ @@ -189,7 +189,7 @@ export class GridService { throw new Error('We could not find SlickGrid Grid object and/or "getDataItem" method'); } - return this._grid.getDataItem(index); + return this._grid.getDataItem(index); } /** Get the Data Item from an array of grid row indexes */ @@ -613,7 +613,7 @@ export class GridService { if (this._dataView.getIdxById(itemId) !== undefined) { // Update the item itself inside the dataView - this._dataView.updateItem(itemId, item); + this._dataView.updateItem(itemId, item); this._grid.updateRow(rowNumber); // do we want to scroll to the row so that it shows in the Viewport (UI) diff --git a/test/cypress/package.json b/test/cypress/package.json index 1cd109365..c014e9f55 100644 --- a/test/cypress/package.json +++ b/test/cypress/package.json @@ -11,7 +11,7 @@ "author": "Ghislain B.", "license": "MIT", "devDependencies": { - "cypress": "^4.7.0", + "cypress": "^4.8.0", "mocha": "^5.2.0", "mochawesome": "^3.1.2", "mochawesome-merge": "^1.0.7", From 67e1e47c2e1b000af0bcabc2d27154f6b5083d37 Mon Sep 17 00:00:00 2001 From: Ghislain Beaulac Date: Tue, 9 Jun 2020 09:56:52 -0400 Subject: [PATCH 3/3] refactor(editor): add corrent listener when calling removeEventListener --- src/app/modules/angular-slickgrid/editors/dualInputEditor.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/modules/angular-slickgrid/editors/dualInputEditor.ts b/src/app/modules/angular-slickgrid/editors/dualInputEditor.ts index 15127df0e..b7af83fb4 100644 --- a/src/app/modules/angular-slickgrid/editors/dualInputEditor.ts +++ b/src/app/modules/angular-slickgrid/editors/dualInputEditor.ts @@ -138,7 +138,7 @@ export class DualInputEditor implements Editor { const columnId = this.columnDef && this.columnDef.id; const elements = document.querySelectorAll(`.dual-editor-text.editor-${columnId}`); if (elements.length > 0) { - elements.forEach((elm) => elm.removeEventListener('focusout', () => { })); + elements.forEach((elm) => elm.removeEventListener('focusout', this.handleFocusOut.bind(this))); } }