diff --git a/src/app/examples/custom-angularComponentEditor.ts b/src/app/examples/custom-angularComponentEditor.ts index 13e3c93a9..fc4432cf3 100644 --- a/src/app/examples/custom-angularComponentEditor.ts +++ b/src/app/examples/custom-angularComponentEditor.ts @@ -8,6 +8,7 @@ import { EditorValidator, EditorValidatorOutput, GridOption, + unsubscribeAllObservables, } from './../modules/angular-slickgrid'; /* @@ -15,7 +16,7 @@ import { * KeyDown events are also handled to provide handling for Tab, Shift-Tab, Esc and Ctrl-Enter. */ export class CustomAngularComponentEditor implements Editor { - changeSubscriber: Subscription; + private _subscriptions: Subscription[] = []; /** Angular Component Reference */ componentRef: ComponentRef; @@ -88,9 +89,9 @@ export class CustomAngularComponentEditor implements Editor { Object.assign(this.componentRef.instance, { collection: this.collection }); // when our model (item object) changes, we'll call a save of the slickgrid editor - this.changeSubscriber = this.componentRef.instance.onItemChanged.subscribe((item) => { - this.save(); - }); + this._subscriptions.push( + this.componentRef.instance.onItemChanged.subscribe((item: any) => this.save()) + ); } } @@ -131,8 +132,10 @@ export class CustomAngularComponentEditor implements Editor { destroy() { if (this.componentRef && this.componentRef.destroy) { this.componentRef.destroy(); - this.changeSubscriber.unsubscribe(); } + + // also unsubscribe all Angular Subscriptions + this._subscriptions = unsubscribeAllObservables(this._subscriptions); } /** optional, implement a focus method on your Angular Component */ diff --git a/src/app/examples/custom-angularComponentFilter.ts b/src/app/examples/custom-angularComponentFilter.ts index f22f5418d..b28fd0cbc 100644 --- a/src/app/examples/custom-angularComponentFilter.ts +++ b/src/app/examples/custom-angularComponentFilter.ts @@ -11,6 +11,7 @@ import { OperatorType, OperatorString, SearchTerm, + unsubscribeAllObservables, } from './../modules/angular-slickgrid'; // using external non-typed js libraries @@ -18,7 +19,7 @@ declare const $: any; export class CustomAngularComponentFilter implements Filter { private _shouldTriggerQuery = true; - changeSubscriber: Subscription; + private _subscriptions: Subscription[] = []; /** Angular Component Reference */ componentRef: ComponentRef; @@ -84,11 +85,13 @@ export class CustomAngularComponentFilter implements Filter { // but technically you can pass any values you wish to your Component Object.assign(componentOuput.componentRef.instance, { collection: this.collection }); - this.changeSubscriber = componentOuput.componentRef.instance.onItemChanged.subscribe((item) => { - this.callback(undefined, { columnDef: this.columnDef, operator: this.operator, searchTerms: [item.id], shouldTriggerQuery: this._shouldTriggerQuery }); - // reset flag for next use - this._shouldTriggerQuery = true; - }); + this._subscriptions.push( + componentOuput.componentRef.instance.onItemChanged.subscribe((item) => { + this.callback(undefined, { columnDef: this.columnDef, operator: this.operator, searchTerms: [item.id], shouldTriggerQuery: this._shouldTriggerQuery }); + // reset flag for next use + this._shouldTriggerQuery = true; + }) + ); }); } } @@ -107,8 +110,10 @@ export class CustomAngularComponentFilter implements Filter { destroy() { if (this.componentRef && this.componentRef.destroy) { this.componentRef.destroy(); - this.changeSubscriber.unsubscribe(); } + + // also unsubscribe all Angular Subscriptions + this._subscriptions = unsubscribeAllObservables(this._subscriptions); } /** Set value(s) on the DOM element */ diff --git a/src/app/examples/grid-clientside.component.ts b/src/app/examples/grid-clientside.component.ts index 038b7ba1d..245d9e8c4 100644 --- a/src/app/examples/grid-clientside.component.ts +++ b/src/app/examples/grid-clientside.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit, OnDestroy } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { TranslateService } from '@ngx-translate/core'; import { CustomInputFilter } from './custom-inputFilter'; @@ -25,7 +25,7 @@ const URL_SAMPLE_COLLECTION_DATA = 'assets/data/collection_500_numbers.json'; @Component({ templateUrl: './grid-clientside.component.html' }) -export class GridClientSideComponent implements OnInit { +export class GridClientSideComponent implements OnInit, OnDestroy { title = 'Example 4: Client Side Sort/Filter'; subTitle = ` Sort/Filter on client side only using SlickGrid DataView (Wiki docs) @@ -280,4 +280,8 @@ export class GridClientSideComponent implements OnInit { }); } } + + ngOnDestroy() { + this.angularGrid.destroy(); + } } diff --git a/src/app/examples/grid-contextmenu.component.ts b/src/app/examples/grid-contextmenu.component.ts index 9cc17d161..856c65950 100644 --- a/src/app/examples/grid-contextmenu.component.ts +++ b/src/app/examples/grid-contextmenu.component.ts @@ -1,5 +1,6 @@ -import { Component, OnInit, ViewEncapsulation } from '@angular/core'; +import { Component, OnInit, OnDestroy, ViewEncapsulation } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; +import { Subscription } from 'rxjs'; import { AngularGridInstance, Column, @@ -10,6 +11,7 @@ import { Formatter, Formatters, GridOption, + unsubscribeAllObservables, } from './../modules/angular-slickgrid'; const actionFormatter: Formatter = (row, cell, value, columnDef, dataContext) => { @@ -58,7 +60,7 @@ const taskTranslateFormatter: Formatter = (row: number, cell: number, value: any styleUrls: ['./grid-contextmenu.component.scss'], encapsulation: ViewEncapsulation.None }) -export class GridContextMenuComponent implements OnInit { +export class GridContextMenuComponent implements OnInit, OnDestroy { title = 'Example 26: Cell Menu & Context Menu Plugins'; subTitle = `Add Cell Menu and Context Menu `; + private subscriptions: Subscription[] = []; angularGrid: AngularGridInstance; columnDefinitions: Column[]; gridOptions: GridOption; @@ -116,6 +119,11 @@ export class GridContextMenuComponent implements OnInit { this.dataset = this.getData(1000); } + ngOnDestroy() { + // also unsubscribe all Angular Subscriptions + this.subscriptions = unsubscribeAllObservables(this.subscriptions); + } + prepareGrid() { this.columnDefinitions = [ { id: 'id', name: '#', field: 'id', maxWidth: 45, sortable: true, filterable: true }, @@ -453,8 +461,11 @@ export class GridContextMenuComponent implements OnInit { switchLanguage() { const nextLanguage = (this.selectedLanguage === 'en') ? 'fr' : 'en'; - this.translate.use(nextLanguage).subscribe(() => { - this.selectedLanguage = nextLanguage; - }); + + this.subscriptions.push( + this.translate.use(nextLanguage).subscribe(() => { + this.selectedLanguage = nextLanguage; + }) + ); } } diff --git a/src/app/examples/grid-frozen.component.ts b/src/app/examples/grid-frozen.component.ts index a89a5acef..2401e2f1d 100644 --- a/src/app/examples/grid-frozen.component.ts +++ b/src/app/examples/grid-frozen.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit, ViewEncapsulation } from '@angular/core'; +import { Component, OnInit, OnDestroy, ViewEncapsulation } from '@angular/core'; import { AngularGridInstance, Column, ColumnEditorDualInput, Editors, FieldType, formatNumber, Formatters, Filters, GridOption } from './../modules/angular-slickgrid'; @Component({ @@ -6,7 +6,7 @@ import { AngularGridInstance, Column, ColumnEditorDualInput, Editors, FieldType, styleUrls: ['./grid-frozen.component.scss'], encapsulation: ViewEncapsulation.None, }) -export class GridFrozenComponent implements OnInit { +export class GridFrozenComponent implements OnInit, OnDestroy { title = 'Example 20: Pinned (frozen) Columns/Rows'; subTitle = ` This example demonstrates the use of Pinned (aka frozen) Columns and/or Rows (Wiki docs) @@ -31,6 +31,12 @@ export class GridFrozenComponent implements OnInit { this.prepareDataGrid(); } + ngOnDestroy() { + // unsubscribe every SlickGrid subscribed event (or use the Slick.EventHandler) + this.gridObj.onMouseEnter.unsubscribe(); + this.gridObj.onMouseLeave.unsubscribe(); + } + angularGridReady(angularGrid: AngularGridInstance) { this.angularGrid = angularGrid; this.gridObj = angularGrid.slickGrid; diff --git a/src/app/examples/grid-graphql.component.ts b/src/app/examples/grid-graphql.component.ts index 2b4658c9a..c915cdfb3 100644 --- a/src/app/examples/grid-graphql.component.ts +++ b/src/app/examples/grid-graphql.component.ts @@ -15,6 +15,7 @@ import { MultipleSelectOption, OperatorType, SortDirection, + unsubscribeAllObservables, } from './../modules/angular-slickgrid'; import * as moment from 'moment-mini'; import { Subscription } from 'rxjs'; @@ -43,6 +44,7 @@ export class GridGraphqlComponent implements OnInit, OnDestroy {
  • You can also preload a grid with certain "presets" like Filters / Sorters / Pagination Wiki - Grid Preset `; + private subscriptions: Subscription[] = []; angularGrid: AngularGridInstance; columnDefinitions: Column[]; gridOptions: GridOption; @@ -54,7 +56,6 @@ export class GridGraphqlComponent implements OnInit, OnDestroy { status = { text: 'processing...', class: 'alert alert-danger' }; isWithCursor = false; selectedLanguage: string; - gridStateSub: Subscription; constructor(private translate: TranslateService) { // always start with English for Cypress E2E tests to be consistent @@ -64,7 +65,8 @@ export class GridGraphqlComponent implements OnInit, OnDestroy { } ngOnDestroy() { - this.gridStateSub.unsubscribe(); + // also unsubscribe all Angular Subscriptions + this.subscriptions = unsubscribeAllObservables(this.subscriptions); } ngOnInit(): void { @@ -208,7 +210,9 @@ export class GridGraphqlComponent implements OnInit, OnDestroy { angularGridReady(angularGrid: AngularGridInstance) { this.angularGrid = angularGrid; - this.gridStateSub = this.angularGrid.gridStateService.onGridStateChanged.subscribe((data) => console.log(data)); + this.subscriptions.push( + this.angularGrid.gridStateService.onGridStateChanged.subscribe((data) => console.log(data)) + ); } displaySpinner(isProcessing) { @@ -294,8 +298,10 @@ export class GridGraphqlComponent implements OnInit, OnDestroy { switchLanguage() { const nextLanguage = (this.selectedLanguage === 'en') ? 'fr' : 'en'; - this.translate.use(nextLanguage).subscribe(() => { - this.selectedLanguage = nextLanguage; - }); + this.subscriptions.push( + this.translate.use(nextLanguage).subscribe(() => { + this.selectedLanguage = nextLanguage; + }) + ); } } diff --git a/src/app/examples/grid-headermenu.component.ts b/src/app/examples/grid-headermenu.component.ts index f34582010..62cdd58a9 100644 --- a/src/app/examples/grid-headermenu.component.ts +++ b/src/app/examples/grid-headermenu.component.ts @@ -1,13 +1,14 @@ -import { Component, OnInit, ViewEncapsulation } from '@angular/core'; -import { AngularGridInstance, Column, GridOption } from './../modules/angular-slickgrid'; +import { Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core'; +import { AngularGridInstance, Column, GridOption, unsubscribeAllObservables } from './../modules/angular-slickgrid'; import { TranslateService } from '@ngx-translate/core'; +import { Subscription } from 'rxjs'; @Component({ templateUrl: './grid-headermenu.component.html', styleUrls: ['./grid-headermenu.component.scss'], encapsulation: ViewEncapsulation.None, }) -export class GridHeaderMenuComponent implements OnInit { +export class GridHeaderMenuComponent implements OnInit, OnDestroy { title = 'Example 8: Header Menu Plugin'; subTitle = ` This example demonstrates using the Slick.Plugins.HeaderMenu plugin to easily add menus to colum headers.
    @@ -29,6 +30,7 @@ export class GridHeaderMenuComponent implements OnInit { `; + private subscriptions: Subscription[] = []; angularGrid: AngularGridInstance; columnDefinitions: Column[]; gridOptions: GridOption; @@ -39,6 +41,11 @@ export class GridHeaderMenuComponent implements OnInit { this.selectedLanguage = this.translate.getDefaultLang(); } + ngOnDestroy() { + // also unsubscribe all Angular Subscriptions + this.subscriptions = unsubscribeAllObservables(this.subscriptions); + } + ngOnInit(): void { this.columnDefinitions = [ { id: 'title', name: 'Title', field: 'title', nameKey: 'TITLE' }, @@ -143,8 +150,10 @@ export class GridHeaderMenuComponent implements OnInit { switchLanguage() { const nextLanguage = (this.selectedLanguage === 'en') ? 'fr' : 'en'; - this.translate.use(nextLanguage).subscribe(() => { - this.selectedLanguage = nextLanguage; - }); + this.subscriptions.push( + this.translate.use(nextLanguage).subscribe(() => { + this.selectedLanguage = nextLanguage; + }) + ); } } diff --git a/src/app/examples/grid-localization.component.ts b/src/app/examples/grid-localization.component.ts index 07c777b4d..71d713618 100644 --- a/src/app/examples/grid-localization.component.ts +++ b/src/app/examples/grid-localization.component.ts @@ -1,5 +1,6 @@ -import { Component, OnInit, Injectable } from '@angular/core'; +import { Component, OnDestroy, OnInit, Injectable } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; +import { Subscription } from 'rxjs'; import { AngularGridInstance, Column, @@ -10,7 +11,8 @@ import { Formatter, Formatters, GridOption, - GridStateChange + GridStateChange, + unsubscribeAllObservables } from './../modules/angular-slickgrid'; const NB_ITEMS = 1500; @@ -27,7 +29,7 @@ const taskTranslateFormatter: Formatter = (row: number, cell: number, value: any templateUrl: './grid-localization.component.html' }) @Injectable() -export class GridLocalizationComponent implements OnInit { +export class GridLocalizationComponent implements OnInit, OnDestroy { title = 'Example 12: Localization (i18n)'; subTitle = `Support multiple locales with the ngx-translate plugin, following these steps (Wiki docs)
      @@ -55,6 +57,7 @@ export class GridLocalizationComponent implements OnInit {
    `; + private subscriptions: Subscription[] = []; angularGrid: AngularGridInstance; columnDefinitions: Column[]; gridOptions: GridOption; @@ -70,6 +73,11 @@ export class GridLocalizationComponent implements OnInit { this.selectedLanguage = defaultLang; } + ngOnDestroy() { + // also unsubscribe all Angular Subscriptions + this.subscriptions = unsubscribeAllObservables(this.subscriptions); + } + ngOnInit(): void { this.columnDefinitions = [ { @@ -266,8 +274,10 @@ export class GridLocalizationComponent implements OnInit { switchLanguage() { const nextLanguage = (this.selectedLanguage === 'en') ? 'fr' : 'en'; - this.translate.use(nextLanguage).subscribe(() => { - this.selectedLanguage = nextLanguage; - }); + this.subscriptions.push( + this.translate.use(nextLanguage).subscribe(() => { + this.selectedLanguage = nextLanguage; + }) + ); } } diff --git a/src/app/examples/grid-menu.component.ts b/src/app/examples/grid-menu.component.ts index 8b4016d19..38521d77e 100644 --- a/src/app/examples/grid-menu.component.ts +++ b/src/app/examples/grid-menu.component.ts @@ -1,6 +1,7 @@ -import { Component, Injectable, OnInit, ViewEncapsulation } from '@angular/core'; -import { AngularGridInstance, Column, ExtensionName, FieldType, Filters, Formatters, GridOption } from './../modules/angular-slickgrid'; +import { Component, Injectable, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core'; +import { AngularGridInstance, Column, ExtensionName, FieldType, Filters, Formatters, GridOption, unsubscribeAllObservables } from './../modules/angular-slickgrid'; import { TranslateService } from '@ngx-translate/core'; +import { Subscription } from 'rxjs'; @Component({ templateUrl: './grid-menu.component.html', @@ -8,7 +9,7 @@ import { TranslateService } from '@ngx-translate/core'; encapsulation: ViewEncapsulation.None, }) @Injectable() -export class GridMenuComponent implements OnInit { +export class GridMenuComponent implements OnInit, OnDestroy { title = 'Example 9: Grid Menu Control'; subTitle = ` This example demonstrates using the Slick.Controls.GridMenu plugin to easily add a Grid Menu (aka hamburger menu) on the top right corner of the grid. @@ -24,6 +25,7 @@ export class GridMenuComponent implements OnInit { `; + private subscriptions: Subscription[] = []; angularGrid: AngularGridInstance; columnDefinitions: Column[]; gridOptions: GridOption; @@ -37,6 +39,11 @@ export class GridMenuComponent implements OnInit { this.selectedLanguage = defaultLang; } + ngOnDestroy() { + // also unsubscribe all Angular Subscriptions + this.subscriptions = unsubscribeAllObservables(this.subscriptions); + } + ngOnInit(): void { this.columnDefinitions = [ { id: 'title', name: 'Title', field: 'title', nameKey: 'TITLE', filterable: true, type: FieldType.string }, @@ -202,9 +209,11 @@ export class GridMenuComponent implements OnInit { switchLanguage() { const nextLanguage = (this.selectedLanguage === 'en') ? 'fr' : 'en'; - this.translate.use(nextLanguage).subscribe(() => { - this.selectedLanguage = nextLanguage; - }); + this.subscriptions.push( + this.translate.use(nextLanguage).subscribe(() => { + this.selectedLanguage = nextLanguage; + }) + ); } toggleGridMenu(e) { diff --git a/src/app/examples/grid-range.component.ts b/src/app/examples/grid-range.component.ts index 4bd2c3caa..014dd2bf7 100644 --- a/src/app/examples/grid-range.component.ts +++ b/src/app/examples/grid-range.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit, OnDestroy } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { CustomInputFilter } from './custom-inputFilter'; import { @@ -14,8 +14,10 @@ import { Metrics, MultipleSelectOption, OperatorType, + unsubscribeAllObservables, } from '../modules/angular-slickgrid'; import * as moment from 'moment-mini'; +import { Subscription } from 'rxjs'; const NB_ITEMS = 1500; @@ -34,7 +36,7 @@ const taskTranslateFormatter: Formatter = (row: number, cell: number, value: any @Component({ templateUrl: './grid-range.component.html' }) -export class GridRangeComponent implements OnInit { +export class GridRangeComponent implements OnInit, OnDestroy { title = 'Example 25: Filtering from Range of Search Values'; subTitle = ` This demo shows how to use Filters with Range of Search Values (Wiki docs) @@ -54,7 +56,7 @@ export class GridRangeComponent implements OnInit {
  • Date Range with Flatpickr Date Picker, they will also use the locale, choose a start date then drag or click on the end date
  • `; - + private subscriptions: Subscription[] = []; angularGrid: AngularGridInstance; columnDefinitions: Column[]; gridOptions: GridOption; @@ -75,6 +77,11 @@ export class GridRangeComponent implements OnInit { this.selectedLanguage = defaultLang; } + ngOnDestroy() { + // also unsubscribe all Angular Subscriptions + this.subscriptions = unsubscribeAllObservables(this.subscriptions); + } + ngOnInit(): void { this.columnDefinitions = [ { @@ -275,8 +282,10 @@ export class GridRangeComponent implements OnInit { switchLanguage() { const nextLanguage = (this.selectedLanguage === 'en') ? 'fr' : 'en'; - this.translate.use(nextLanguage).subscribe(() => { - this.selectedLanguage = nextLanguage; - }); + this.subscriptions.push( + this.translate.use(nextLanguage).subscribe(() => { + this.selectedLanguage = nextLanguage; + }) + ); } } diff --git a/src/app/examples/grid-state.component.ts b/src/app/examples/grid-state.component.ts index 472511f74..e9462f17a 100644 --- a/src/app/examples/grid-state.component.ts +++ b/src/app/examples/grid-state.component.ts @@ -1,5 +1,6 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, OnDestroy, OnInit } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; +import { Subscription } from 'rxjs'; import { AngularGridInstance, Column, @@ -9,7 +10,8 @@ import { GridOption, GridState, GridStateChange, - MultipleSelectOption + MultipleSelectOption, + unsubscribeAllObservables } from './../modules/angular-slickgrid'; function randomBetween(min, max) { @@ -22,7 +24,7 @@ const NB_ITEMS = 500; @Component({ templateUrl: './grid-state.component.html' }) -export class GridStateComponent implements OnInit { +export class GridStateComponent implements OnInit, OnDestroy { title = 'Example 16: Grid State & Presets using Local Storage'; subTitle = ` Grid State & Preset (Wiki docs) @@ -35,7 +37,7 @@ export class GridStateComponent implements OnInit {
  • Local Storage is just one option, you can use whichever is more convenient for you (Local Storage, Session Storage, DB, ...)
  • `; - + private subscriptions: Subscription[] = []; angularGrid: AngularGridInstance; columnDefinitions: Column[]; gridOptions: GridOption; @@ -50,6 +52,11 @@ export class GridStateComponent implements OnInit { this.angularGrid = angularGrid; } + ngOnDestroy() { + // also unsubscribe all Angular Subscriptions + this.subscriptions = unsubscribeAllObservables(this.subscriptions); + } + ngOnInit(): void { const presets = JSON.parse(localStorage[LOCAL_STORAGE_KEY] || null); @@ -213,9 +220,11 @@ export class GridStateComponent implements OnInit { switchLanguage() { const nextLanguage = (this.selectedLanguage === 'en') ? 'fr' : 'en'; - this.translate.use(nextLanguage).subscribe(() => { - this.selectedLanguage = nextLanguage; - }); + this.subscriptions.push( + this.translate.use(nextLanguage).subscribe(() => { + this.selectedLanguage = nextLanguage; + }) + ); } useDefaultPresets() { 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 206ac6ca9..6348eabae 100644 --- a/src/app/modules/angular-slickgrid/components/angular-slickgrid.component.ts +++ b/src/app/modules/angular-slickgrid/components/angular-slickgrid.component.ts @@ -661,9 +661,11 @@ export class AngularSlickgridComponent implements AfterViewInit, OnDestroy, OnIn process.then((processResult: GraphqlResult | GraphqlPaginatedResult | any) => executeBackendProcessesCallback(startTime, processResult, backendApi, totalItems)) .catch((error: any) => onBackendError(error, backendApi)); } else if (isObservable(process)) { - process.subscribe( - (processResult: GraphqlResult | GraphqlPaginatedResult | any) => executeBackendProcessesCallback(startTime, processResult, backendApi, totalItems), - (error: any) => onBackendError(error, backendApi) + this.subscriptions.push( + process.subscribe( + (processResult: GraphqlResult | GraphqlPaginatedResult | any) => executeBackendProcessesCallback(startTime, processResult, backendApi, totalItems), + (error: any) => onBackendError(error, backendApi) + ) ); } }); diff --git a/src/app/modules/angular-slickgrid/extensions/cellExternalCopyManagerExtension.ts b/src/app/modules/angular-slickgrid/extensions/cellExternalCopyManagerExtension.ts index 6a4ee1f6e..12ecf79bb 100644 --- a/src/app/modules/angular-slickgrid/extensions/cellExternalCopyManagerExtension.ts +++ b/src/app/modules/angular-slickgrid/extensions/cellExternalCopyManagerExtension.ts @@ -51,6 +51,7 @@ export class CellExternalCopyManagerExtension implements Extension { if (this._addon && this._addon.destroy) { this._addon.destroy(); } + document.removeEventListener('keydown', this.hookUndoShortcutKey.bind(this)); } /** Get the instance of the SlickGrid addon (control or plugin). */ diff --git a/src/app/modules/angular-slickgrid/filters/autoCompleteFilter.ts b/src/app/modules/angular-slickgrid/filters/autoCompleteFilter.ts index 8f4d2f5a7..c98fb8aaf 100644 --- a/src/app/modules/angular-slickgrid/filters/autoCompleteFilter.ts +++ b/src/app/modules/angular-slickgrid/filters/autoCompleteFilter.ts @@ -20,7 +20,7 @@ import { SearchTerm, } from './../models/index'; import { CollectionService } from '../services/collection.service'; -import { castToPromise, getDescendantProperty, toKebabCase } from '../services/utilities'; +import { castToPromise, getDescendantProperty, toKebabCase, unsubscribeAllObservables } from '../services/utilities'; // using external non-typed js libraries declare const $: any; @@ -194,6 +194,8 @@ export class AutoCompleteFilter implements Filter { if (this.$filterElm) { this.$filterElm.off('keyup').remove(); } + // also unsubscribe all RxJS subscriptions + this.subscriptions = unsubscribeAllObservables(this.subscriptions); } /** Set value(s) on the DOM element */ diff --git a/src/app/modules/angular-slickgrid/models/multipleSelectOption.interface.ts b/src/app/modules/angular-slickgrid/models/multipleSelectOption.interface.ts index 174490354..af38bc51e 100644 --- a/src/app/modules/angular-slickgrid/models/multipleSelectOption.interface.ts +++ b/src/app/modules/angular-slickgrid/models/multipleSelectOption.interface.ts @@ -153,6 +153,12 @@ export interface MultipleSelectOption { /** Reloads the Multiple Select. If you’re dynamically adding/removing option tags on the original select via AJAX or DOM manipulation methods, call refresh to reflect the changes. */ refresh?: () => void; + /** Gets the current Multiple-Select options */ + getOptions?: () => MultipleSelectOption; + + /** Set new multiple-select option(s) and refresh the element */ + refreshOptions?: (newOptions: MultipleSelectOption) => void; + /** Gets the selected values. */ getSelects?: () => string | string[]; diff --git a/src/app/modules/angular-slickgrid/services/groupingAndColspan.service.ts b/src/app/modules/angular-slickgrid/services/groupingAndColspan.service.ts index ee8c22d00..e1c6fe145 100644 --- a/src/app/modules/angular-slickgrid/services/groupingAndColspan.service.ts +++ b/src/app/modules/angular-slickgrid/services/groupingAndColspan.service.ts @@ -1,9 +1,11 @@ -import { Injectable, Optional } from '@angular/core'; +import { Injectable } from '@angular/core'; +import { Subscription } from 'rxjs'; import { Column, GridOption, SlickEventHandler, ExtensionName } from './../models/index'; import { ExtensionUtility } from '../extensions/extensionUtility'; import { ExtensionService } from './extension.service'; import { ResizerService } from './resizer.service'; +import { unsubscribeAllObservables } from './utilities'; // using external non-typed js libraries declare let $: any; @@ -15,6 +17,7 @@ declare const Slick: any; export class GroupingAndColspanService { private _eventHandler: SlickEventHandler; private _grid: any; + private subscriptions: Subscription[] = []; constructor(private extensionUtility: ExtensionUtility, private extensionService: ExtensionService, private resizerService: ResizerService) { this._eventHandler = new Slick.EventHandler(); @@ -47,7 +50,9 @@ export class GroupingAndColspanService { this._eventHandler.subscribe(grid.onColumnsResized, () => this.renderPreHeaderRowGroupingTitles()); this._eventHandler.subscribe(grid.onColumnsReordered, () => this.renderPreHeaderRowGroupingTitles()); this._eventHandler.subscribe(dataView.onRowCountChanged, () => this.renderPreHeaderRowGroupingTitles()); - this.resizerService.onGridAfterResize.subscribe(() => this.renderPreHeaderRowGroupingTitles()); + this.subscriptions.push( + this.resizerService.onGridAfterResize.subscribe(() => this.renderPreHeaderRowGroupingTitles()) + ); this._eventHandler.subscribe(grid.onSetOptions, (_e, args) => { // when user changes frozen columns dynamically (e.g. from header menu), we need to re-render the pre-header of the grouping titles @@ -78,6 +83,9 @@ export class GroupingAndColspanService { dispose() { // unsubscribe all SlickGrid events this._eventHandler.unsubscribeAll(); + + // also unsubscribe all Angular Subscriptions + this.subscriptions = unsubscribeAllObservables(this.subscriptions); } /** Create or Render the Pre-Header Row Grouping Titles */ diff --git a/src/app/modules/angular-slickgrid/services/pagination.service.ts b/src/app/modules/angular-slickgrid/services/pagination.service.ts index b19b974d3..ae55d767d 100644 --- a/src/app/modules/angular-slickgrid/services/pagination.service.ts +++ b/src/app/modules/angular-slickgrid/services/pagination.service.ts @@ -93,7 +93,7 @@ export class PaginationService { } if (this._isLocalGrid && this.dataView) { - this.dataView.onPagingInfoChanged.subscribe((e, pagingInfo) => { + this._eventHandler.subscribe(this.dataView.onPagingInfoChanged, (e, pagingInfo) => { if (this._totalItems !== pagingInfo.totalRows) { this._totalItems = pagingInfo.totalRows; this._paginationOptions.totalItems = this._totalItems; @@ -288,14 +288,16 @@ export class PaginationService { reject(process); }); } else if (isObservable(process)) { - process.subscribe( - (processResult: GraphqlResult | GraphqlPaginatedResult | any) => { - resolve(executeBackendProcessesCallback(startTime, processResult, this._backendServiceApi, this._totalItems)); - }, - (error: any) => { - onBackendError(error, this._backendServiceApi); - reject(process); - } + this._subscriptions.push( + process.subscribe( + (processResult: GraphqlResult | GraphqlPaginatedResult | any) => { + resolve(executeBackendProcessesCallback(startTime, processResult, this._backendServiceApi, this._totalItems)); + }, + (error: any) => { + onBackendError(error, this._backendServiceApi); + reject(process); + } + ) ); } this.onPaginationChanged.next(this.getFullPagination()); @@ -315,9 +317,7 @@ export class PaginationService { this._dataTo = this._totalItems; } } - if (this._totalItems > 0 && this._pageNumber === 0) { - this._pageNumber = 1; - } + this._pageNumber = (this._totalItems > 0 && this._pageNumber === 0) ? 1 : this._pageNumber; // do a final check on the From/To and make sure they are not over or below min/max acceptable values if (this._dataTo > this._totalItems) { diff --git a/src/app/modules/angular-slickgrid/services/resizer.service.ts b/src/app/modules/angular-slickgrid/services/resizer.service.ts index 4a90e2fb2..7ed143d41 100644 --- a/src/app/modules/angular-slickgrid/services/resizer.service.ts +++ b/src/app/modules/angular-slickgrid/services/resizer.service.ts @@ -72,15 +72,17 @@ export class ResizerService { // -- 2nd bind a trigger on the Window DOM element, so that it happens also when resizing after first load // -- bind auto-resize to Window object only if it exist - $(window).on(`resize.grid.${this._gridUid}`, (event: Event) => { - this.onGridBeforeResize.next(event); - if (!this._resizePaused) { - // for some yet unknown reason, calling the resize twice removes any stuttering/flickering - // when changing the height and makes it much smoother experience - this.resizeGrid(0, newSizes); - this.resizeGrid(0, newSizes); - } - }); + $(window).on(`resize.grid.${this._gridUid}`, this.handleResizeGrid.bind(this, newSizes)); + } + + handleResizeGrid(newSizes: GridDimension, event: Event) { + this.onGridBeforeResize.next(event); + if (!this._resizePaused) { + // for some yet unknown reason, calling the resize twice removes any stuttering/flickering + // when changing the height and makes it much smoother experience + this.resizeGrid(0, newSizes); + this.resizeGrid(0, newSizes); + } } /** diff --git a/src/assets/lib/multiple-select/multiple-select.js b/src/assets/lib/multiple-select/multiple-select.js index e3626fe6c..c1ad1c8df 100644 --- a/src/assets/lib/multiple-select/multiple-select.js +++ b/src/assets/lib/multiple-select/multiple-select.js @@ -1,6 +1,6 @@ /** * @author zhixin wen - * @version 1.2.2 + * @version 1.3.7 * * http://wenzhixin.net.cn/p/multiple-select/ * @@ -25,6 +25,9 @@ * - "domElmSelectAllHeight" defaults to 39 (as per CSS), that is the DOM element of the "Select All" text area * - "useSelectOptionLabel" (defaults to False), when set to True it will use the