ag-grid-angular
Version:
AG Grid Angular Component
1 lines • 207 kB
Source Map (JSON)
{"version":3,"file":"ag-grid-angular.mjs","sources":["../../../projects/ag-grid-angular/src/lib/angularFrameworkComponentWrapper.ts","../../../projects/ag-grid-angular/src/lib/angularFrameworkEventListenerService.ts","../../../projects/ag-grid-angular/src/lib/angularFrameworkOverrides.ts","../../../projects/ag-grid-angular/src/lib/ag-grid-angular.component.ts","../../../projects/ag-grid-angular/src/lib/ag-grid-angular.module.ts","../../../projects/ag-grid-angular/src/ag-grid-angular.ts"],"sourcesContent":["import type { ComponentRef } from '@angular/core';\nimport { ViewContainerRef } from '@angular/core';\nimport { Component, Injectable, inject } from '@angular/core';\n\nimport type { FrameworkComponentWrapper, IFilter, WrappableInterface } from 'ag-grid-community';\nimport { BaseComponentWrapper, _removeFromParent } from 'ag-grid-community';\n\nimport type { AngularFrameworkOverrides } from './angularFrameworkOverrides';\nimport type { AgFrameworkComponent } from './interfaces';\n\n// To speed up the removal of custom components we create a number of shards to contain them.\n// Removing a single component calls a function within Angular called removeFromArray.\n// This is a lot faster if the array is smaller.\n@Component({\n selector: 'ag-component-container',\n template: '',\n})\nexport class AgComponentContainer {\n public vcr = inject(ViewContainerRef);\n}\nconst NUM_SHARDS = 16;\nlet shardIdx = 0;\n\nfunction createComponentContainers(vcr: ViewContainerRef): Map<number, ComponentRef<AgComponentContainer>> {\n const containerMap = new Map<number, ComponentRef<AgComponentContainer>>();\n for (let i = 0; i < NUM_SHARDS; i++) {\n const container = vcr.createComponent(AgComponentContainer);\n containerMap.set(i, container);\n _removeFromParent(container.location.nativeElement);\n }\n return containerMap;\n}\n\n/**\n * These methods are called on a hot path for every row so we do not want to enter / exit NgZone each time.\n * Also these methods should not be used to update the UI, so we don't need to run them inside Angular.\n */\nconst runOutsideMethods = new Set<keyof IFilter>(['doesFilterPass', 'isFilterActive']);\n\n@Injectable()\nexport class AngularFrameworkComponentWrapper\n extends BaseComponentWrapper<WrappableInterface>\n implements FrameworkComponentWrapper\n{\n private viewContainerRef: ViewContainerRef;\n private angularFrameworkOverrides: AngularFrameworkOverrides;\n private compShards: Map<number, ComponentRef<AgComponentContainer>>;\n\n public setViewContainerRef(\n viewContainerRef: ViewContainerRef,\n angularFrameworkOverrides: AngularFrameworkOverrides\n ) {\n this.viewContainerRef = viewContainerRef;\n this.angularFrameworkOverrides = angularFrameworkOverrides;\n }\n\n protected createWrapper(OriginalConstructor: { new (): any }): WrappableInterface {\n const angularFrameworkOverrides = this.angularFrameworkOverrides;\n const that = this;\n that.compShards ??= createComponentContainers(this.viewContainerRef);\n\n class DynamicAgNg2Component\n extends BaseGuiComponent<any, AgFrameworkComponent<any>>\n implements WrappableInterface\n {\n override init(params: any): void {\n angularFrameworkOverrides.runInsideAngular(() => {\n super.init(params);\n this._componentRef.changeDetectorRef.detectChanges();\n });\n }\n\n protected createComponent(): ComponentRef<AgFrameworkComponent<any>> {\n return that.createComponent(OriginalConstructor);\n }\n\n hasMethod(name: string): boolean {\n return wrapper.getFrameworkComponentInstance()[name] != null;\n }\n\n callMethod(name: string, args: IArguments): void {\n const componentRef = this.getFrameworkComponentInstance();\n const methodCall = componentRef[name];\n\n if (runOutsideMethods.has(name as any)) {\n return methodCall.apply(componentRef, args);\n }\n return angularFrameworkOverrides.runInsideAngular(() => methodCall.apply(componentRef, args));\n }\n\n addMethod(name: string, callback: (...args: any[]) => any): void {\n (wrapper as any)[name] = callback;\n }\n }\n const wrapper = new DynamicAgNg2Component();\n return wrapper;\n }\n\n public createComponent<T>(componentType: { new (...args: any[]): T }): ComponentRef<T> {\n shardIdx = (shardIdx + 1) % NUM_SHARDS;\n const container = this.compShards.get(shardIdx)!;\n return container.instance.vcr.createComponent(componentType);\n }\n}\n\nabstract class BaseGuiComponent<P, T extends AgFrameworkComponent<P>> {\n protected _params: P;\n protected _eGui: HTMLElement;\n protected _componentRef: ComponentRef<T>;\n protected _agAwareComponent: T;\n protected _frameworkComponentInstance: any; // the users component - for accessing methods they create\n\n protected init(params: P): void {\n this._params = params;\n\n this._componentRef = this.createComponent();\n this._agAwareComponent = this._componentRef.instance;\n this._frameworkComponentInstance = this._componentRef.instance;\n this._eGui = this._componentRef.location.nativeElement;\n // Angular appends the component to the DOM, so remove it\n _removeFromParent(this._eGui);\n\n this._agAwareComponent.agInit(this._params);\n }\n\n public getGui(): HTMLElement {\n return this._eGui;\n }\n\n /** `getGui()` returns the `ng-component` element. This returns the actual root element. */\n public getRootElement(): HTMLElement {\n const firstChild = this._eGui.firstChild;\n return firstChild as HTMLElement;\n }\n\n public destroy(): void {\n if (this._frameworkComponentInstance && typeof this._frameworkComponentInstance.destroy === 'function') {\n this._frameworkComponentInstance.destroy();\n }\n this._componentRef?.destroy();\n }\n\n public getFrameworkComponentInstance(): any {\n return this._frameworkComponentInstance;\n }\n\n protected abstract createComponent(): ComponentRef<T>;\n}\n","import type { AngularFrameworkOverrides } from './angularFrameworkOverrides';\n\ntype EventTypeToWrap = string;\n\nexport class AngularFrameworkEventListenerService<\n TEventListener extends (e: any) => void,\n TGlobalEventListener extends (name: string, e: any) => void,\n> {\n // Map from user listener to wrapped listener so we can remove listener provided by user\n private wrappedListeners: Map<EventTypeToWrap, Map<TEventListener, TEventListener>> = new Map();\n private wrappedGlobalListeners: Map<TGlobalEventListener, TGlobalEventListener> = new Map();\n\n constructor(private frameworkOverrides: AngularFrameworkOverrides) {}\n\n public wrap(eventType: EventTypeToWrap, userListener: TEventListener): TEventListener {\n const { frameworkOverrides, wrappedListeners } = this;\n let listener: any = userListener;\n\n if (frameworkOverrides.shouldWrapOutgoing) {\n listener = (event: any) => {\n frameworkOverrides.wrapOutgoing(() => userListener(event));\n };\n\n let eventListeners = wrappedListeners.get(eventType);\n if (!eventListeners) {\n eventListeners = new Map();\n wrappedListeners.set(eventType, eventListeners);\n }\n eventListeners.set(userListener, listener);\n }\n return listener;\n }\n\n public wrapGlobal(userListener: TGlobalEventListener): TGlobalEventListener {\n const { frameworkOverrides, wrappedGlobalListeners } = this;\n let listener: any = userListener;\n\n if (frameworkOverrides.shouldWrapOutgoing) {\n listener = (eventType: any, event: any) => {\n frameworkOverrides.wrapOutgoing(() => userListener(eventType, event));\n };\n wrappedGlobalListeners.set(userListener, listener);\n }\n return listener;\n }\n\n public unwrap(eventType: EventTypeToWrap, userListener: TEventListener): TEventListener {\n const { wrappedListeners } = this;\n const eventListeners = wrappedListeners.get(eventType);\n if (eventListeners) {\n const wrapped = eventListeners.get(userListener);\n if (wrapped) {\n eventListeners.delete(userListener);\n if (eventListeners.size === 0) {\n wrappedListeners.delete(eventType);\n }\n return wrapped;\n }\n }\n return userListener;\n }\n public unwrapGlobal(userListener: TGlobalEventListener): TGlobalEventListener {\n const { wrappedGlobalListeners } = this;\n const wrapped = wrappedGlobalListeners.get(userListener);\n if (wrapped) {\n wrappedGlobalListeners.delete(userListener);\n return wrapped;\n }\n return userListener;\n }\n}\n","import { Injectable, NgZone } from '@angular/core';\n\nimport type {\n FrameworkOverridesIncomingSource,\n IFrameworkEventListenerService,\n LocalEventService,\n} from 'ag-grid-community';\nimport { VanillaFrameworkOverrides } from 'ag-grid-community';\n\nimport { AngularFrameworkEventListenerService } from './angularFrameworkEventListenerService';\n\n@Injectable()\nexport class AngularFrameworkOverrides extends VanillaFrameworkOverrides {\n public override readonly batchFrameworkComps: boolean = true;\n\n // Flag used to control Zone behaviour when running tests as many test features rely on Zone.\n private isRunningWithinTestZone: boolean = false;\n\n private runOutside: <T>(callback: () => T, source?: FrameworkOverridesIncomingSource) => T;\n\n constructor(private _ngZone: NgZone) {\n super('angular');\n\n this.isRunningWithinTestZone =\n (window as any)?.AG_GRID_UNDER_TEST ?? !!(window as any)?.Zone?.AsyncTestZoneSpec;\n\n if (!this._ngZone) {\n this.runOutside = (callback) => callback();\n } else if (this.isRunningWithinTestZone) {\n this.runOutside = (callback, source) => {\n if (source === 'resize-observer' || source === 'popupPositioning') {\n // ensure resize observer callbacks are run outside of Angular even under test due to Jest not supporting ResizeObserver\n // which means it just loops continuously with a setTimeout with no way to flush the queue or have fixture.whenStable() resolve.\n return this._ngZone.runOutsideAngular(callback);\n }\n // When under test run inside Angular so that tests can use fixture.whenStable() to wait for async operations to complete.\n return callback();\n };\n } else {\n this.runOutside = (callback) => this._ngZone.runOutsideAngular(callback);\n }\n }\n\n // Make all events run outside Angular as they often trigger the setup of event listeners\n // By having the event listeners outside Angular we can avoid triggering change detection\n // This also means that if a user calls an AG Grid API method from within their component\n // the internal side effects will not trigger change detection. Without this the events would\n // run inside Angular and trigger change detection as the source of the event was within the angular zone.\n override wrapIncoming: <T>(callback: () => T, source?: FrameworkOverridesIncomingSource) => T = (\n callback,\n source\n ) => this.runOutside(callback, source);\n\n /**\n * The shouldWrapOutgoing property is used to determine if events should be run outside of Angular or not.\n * If an event handler is registered outside of Angular then we should not wrap the event handler\n * with runInsideAngular() as the user may not have wanted this.\n * This is also used to not wrap internal event listeners that are registered with RowNodes and Columns.\n */\n public get shouldWrapOutgoing() {\n return this._ngZone && NgZone.isInAngularZone();\n }\n\n /**\n * Make sure that any code that is executed outside of AG Grid is running within the Angular zone.\n * This means users can update templates and use binding without having to do anything extra.\n */\n override wrapOutgoing: <T>(callback: () => T) => T = (callback) => this.runInsideAngular(callback);\n\n public createLocalEventListenerWrapper(\n existingFrameworkEventListenerService: IFrameworkEventListenerService<any, any> | undefined,\n localEventService: LocalEventService<any>\n ): IFrameworkEventListenerService<any, any> | undefined {\n if (this.shouldWrapOutgoing) {\n return (\n existingFrameworkEventListenerService ??\n (() => {\n localEventService.setFrameworkOverrides(this);\n return new AngularFrameworkEventListenerService(this);\n })()\n );\n }\n\n return undefined;\n }\n\n public createGlobalEventListenerWrapper(): IFrameworkEventListenerService<any, any> {\n return new AngularFrameworkEventListenerService(this);\n }\n\n override isFrameworkComponent(comp: any): boolean {\n if (!comp) {\n return false;\n }\n const prototype = comp.prototype;\n return prototype && 'agInit' in prototype;\n }\n\n runInsideAngular<T>(callback: () => T): T {\n if (!this._ngZone || NgZone.isInAngularZone()) {\n return callback();\n }\n\n // Check for _ngZone existence as it is not present when Zoneless\n return this._ngZone.run(callback);\n }\n\n runOutsideAngular<T>(callback: () => T, source?: FrameworkOverridesIncomingSource): T {\n return this.runOutside(callback, source);\n }\n}\n","// False positive lint error, ElementRef and co can't be type imports\n// eslint-disable-next-line @typescript-eslint/consistent-type-imports\nimport {\n AfterViewInit,\n Component,\n ElementRef,\n EventEmitter,\n Input,\n OnChanges,\n OnDestroy,\n Output,\n ViewContainerRef,\n ViewEncapsulation,\n booleanAttribute,\n} from '@angular/core';\nimport type { AgChartTheme, AgChartThemeOverrides } from 'ag-charts-types';\n\n// @START_IMPORTS@\nimport type {\n AdvancedFilterBuilderVisibleChangedEvent,\n AlignedGrid,\n AsyncTransactionsFlushedEvent,\n BodyScrollEndEvent,\n BodyScrollEvent,\n CellClickedEvent,\n CellContextMenuEvent,\n CellDoubleClickedEvent,\n CellEditRequestEvent,\n CellEditingStartedEvent,\n CellEditingStoppedEvent,\n CellFocusedEvent,\n CellKeyDownEvent,\n CellMouseDownEvent,\n CellMouseOutEvent,\n CellMouseOverEvent,\n CellPosition,\n CellSelectionChangedEvent,\n CellSelectionDeleteEndEvent,\n CellSelectionDeleteStartEvent,\n CellSelectionOptions,\n CellValueChangedEvent,\n ChartCreatedEvent,\n ChartDestroyedEvent,\n ChartOptionsChangedEvent,\n ChartRangeSelectionChangedEvent,\n ChartRefParams,\n ChartToolPanelsDef,\n ColDef,\n ColGroupDef,\n ColTypeDef,\n Column,\n ColumnEverythingChangedEvent,\n ColumnGroupOpenedEvent,\n ColumnHeaderClickedEvent,\n ColumnHeaderContextMenuEvent,\n ColumnHeaderMouseLeaveEvent,\n ColumnHeaderMouseOverEvent,\n ColumnMenuVisibleChangedEvent,\n ColumnMovedEvent,\n ColumnPinnedEvent,\n ColumnPivotChangedEvent,\n ColumnPivotModeChangedEvent,\n ColumnResizedEvent,\n ColumnRowGroupChangedEvent,\n ColumnValueChangedEvent,\n ColumnVisibleEvent,\n ComponentStateChangedEvent,\n ContextMenuVisibleChangedEvent,\n CsvExportParams,\n CutEndEvent,\n CutStartEvent,\n DataTypeDefinition,\n DefaultChartMenuItem,\n DisplayedColumnsChangedEvent,\n DomLayoutType,\n DragCancelledEvent,\n DragStartedEvent,\n DragStoppedEvent,\n ExcelExportParams,\n ExcelStyle,\n ExpandOrCollapseAllEvent,\n FillEndEvent,\n FillOperationParams,\n FillStartEvent,\n FilterChangedEvent,\n FilterModifiedEvent,\n FilterOpenedEvent,\n FindChangedEvent,\n FindOptions,\n FirstDataRenderedEvent,\n FocusGridInnerElementParams,\n FullWidthCellKeyDownEvent,\n GetChartMenuItems,\n GetChartToolbarItems,\n GetContextMenuItems,\n GetDataPath,\n GetGroupRowAggParams,\n GetLocaleTextParams,\n GetMainMenuItems,\n GetRowIdFunc,\n GetServerSideGroupKey,\n GetServerSideGroupLevelParamsParams,\n GridColumnsChangedEvent,\n GridReadyEvent,\n GridSizeChangedEvent,\n GridState,\n HeaderFocusedEvent,\n HeaderPosition,\n IAdvancedFilterBuilderParams,\n IAggFunc,\n IDatasource,\n IRowDragItem,\n IRowNode,\n IServerSideDatasource,\n IViewportDatasource,\n InitialGroupOrderComparatorParams,\n IsApplyServerSideTransaction,\n IsExternalFilterPresentParams,\n IsFullWidthRowParams,\n IsGroupOpenByDefaultParams,\n IsRowFilterable,\n IsRowMaster,\n IsRowPinnable,\n IsRowPinned,\n IsRowSelectable,\n IsServerSideGroup,\n IsServerSideGroupOpenByDefaultParams,\n LoadingCellRendererSelectorFunc,\n MenuItemDef,\n ModelUpdatedEvent,\n NavigateToNextCellParams,\n NavigateToNextHeaderParams,\n NewColumnsLoadedEvent,\n PaginationChangedEvent,\n PaginationNumberFormatterParams,\n PasteEndEvent,\n PasteStartEvent,\n PinnedRowDataChangedEvent,\n PinnedRowsChangedEvent,\n PivotMaxColumnsExceededEvent,\n PostProcessPopupParams,\n PostSortRowsParams,\n ProcessCellForExportParams,\n ProcessDataFromClipboardParams,\n ProcessGroupHeaderForExportParams,\n ProcessHeaderForExportParams,\n ProcessRowParams,\n ProcessUnpinnedColumnsParams,\n RangeDeleteEndEvent,\n RangeDeleteStartEvent,\n RangeSelectionChangedEvent,\n RedoEndedEvent,\n RedoStartedEvent,\n RowClassParams,\n RowClassRules,\n RowClickedEvent,\n RowDataUpdatedEvent,\n RowDoubleClickedEvent,\n RowDragCancelEvent,\n RowDragEndEvent,\n RowDragEnterEvent,\n RowDragLeaveEvent,\n RowDragMoveEvent,\n RowEditingStartedEvent,\n RowEditingStoppedEvent,\n RowGroupOpenedEvent,\n RowGroupingDisplayType,\n RowHeightParams,\n RowModelType,\n RowNumbersOptions,\n RowResizeEndedEvent,\n RowResizeStartedEvent,\n RowSelectedEvent,\n RowSelectionOptions,\n RowStyle,\n RowValueChangedEvent,\n SelectionChangedEvent,\n SelectionColumnDef,\n SendToClipboardParams,\n ServerSideGroupLevelParams,\n SideBarDef,\n SizeColumnsToContentStrategy,\n SizeColumnsToFitGridStrategy,\n SizeColumnsToFitProvidedWidthStrategy,\n SortChangedEvent,\n SortDirection,\n StateUpdatedEvent,\n StatusPanelDef,\n StoreRefreshedEvent,\n TabToNextCellParams,\n TabToNextHeaderParams,\n Theme,\n ToolPanelSizeChangedEvent,\n ToolPanelVisibleChangedEvent,\n TooltipHideEvent,\n TooltipShowEvent,\n TreeDataDisplayType,\n UndoEndedEvent,\n UndoStartedEvent,\n UseGroupTotalRow,\n ViewportChangedEvent,\n VirtualColumnsChangedEvent,\n VirtualRowRemovedEvent,\n} from 'ag-grid-community';\n// @END_IMPORTS@\nimport type { GridApi, GridOptions, GridParams, Module } from 'ag-grid-community';\nimport {\n _BOOLEAN_MIXED_GRID_OPTIONS,\n _combineAttributesAndGridOptions,\n _processOnChange,\n createGrid,\n} from 'ag-grid-community';\n\nimport { AngularFrameworkComponentWrapper } from './angularFrameworkComponentWrapper';\nimport { AngularFrameworkOverrides } from './angularFrameworkOverrides';\n\n@Component({\n selector: 'ag-grid-angular',\n standalone: true,\n template: '',\n providers: [AngularFrameworkOverrides, AngularFrameworkComponentWrapper],\n // tell angular we don't want view encapsulation, we don't want a shadow root\n encapsulation: ViewEncapsulation.None,\n})\nexport class AgGridAngular<TData = any, TColDef extends ColDef<TData> = ColDef<any>>\n implements AfterViewInit, OnChanges, OnDestroy\n{\n // not intended for user to interact with. so putting _ in so if user gets reference\n // to this object, they kind'a know it's not part of the agreed interface\n private _nativeElement: any;\n private _initialised = false;\n private _destroyed = false;\n\n // in order to ensure firing of gridReady is deterministic\n private _holdEvents = true;\n private _resolveFullyReady: () => void;\n private _fullyReady: Promise<void> = new Promise((resolve) => {\n this._resolveFullyReady = resolve;\n });\n\n /** Grid Api available after onGridReady event has fired. */\n public api: GridApi<TData>;\n\n constructor(\n elementDef: ElementRef,\n private _viewContainerRef: ViewContainerRef,\n private _angularFrameworkOverrides: AngularFrameworkOverrides,\n private _frameworkCompWrapper: AngularFrameworkComponentWrapper\n ) {\n this._nativeElement = elementDef.nativeElement;\n this._fullyReady.then(() => {\n // Register the status flag reset before any events are fired\n // so that we can swap to synchronous event firing as soon as the grid is ready\n this._holdEvents = false;\n });\n }\n\n ngAfterViewInit(): void {\n // Run the setup outside of angular so all the event handlers that are created do not trigger change detection\n this._angularFrameworkOverrides.runOutsideAngular(() => {\n this._frameworkCompWrapper.setViewContainerRef(this._viewContainerRef, this._angularFrameworkOverrides);\n\n // Get all the inputs that are valid GridOptions\n const gridOptionKeys = Object.keys(this).filter(\n (key) =>\n !(\n key.startsWith('_') ||\n key == 'gridOptions' ||\n key == 'modules' ||\n this[key as keyof AgGridAngular] instanceof EventEmitter\n )\n );\n\n const coercedGridOptions = {} as GridOptions<TData>;\n gridOptionKeys.forEach((key) => {\n const valueToUse = getValueOrCoercedValue(key, this[key as keyof AgGridAngular]);\n coercedGridOptions[key as keyof GridOptions] = valueToUse;\n });\n\n const mergedGridOps = _combineAttributesAndGridOptions(\n this.gridOptions,\n coercedGridOptions,\n gridOptionKeys\n );\n\n const gridParams: GridParams = {\n globalListener: this.globalListener.bind(this),\n frameworkOverrides: this._angularFrameworkOverrides,\n providedBeanInstances: {\n frameworkCompWrapper: this._frameworkCompWrapper,\n },\n modules: (this.modules || []) as any,\n setThemeOnGridDiv: true,\n };\n\n const api = createGrid(this._nativeElement, mergedGridOps, gridParams);\n if (api) {\n this.api = api;\n }\n\n this._initialised = true;\n\n // sometimes, especially in large client apps gridReady can fire before ngAfterViewInit\n // this ties these together so that gridReady will always fire after agGridAngular's ngAfterViewInit\n // the actual containing component's ngAfterViewInit will fire just after agGridAngular's\n this._resolveFullyReady();\n });\n }\n\n public ngOnChanges(changes: any): void {\n if (this._initialised) {\n // Run the changes outside of angular so any event handlers that are created do not trigger change detection\n this._angularFrameworkOverrides.runOutsideAngular(() => {\n const gridOptions: GridOptions = {};\n for (const key of Object.keys(changes)) {\n const value = changes[key];\n gridOptions[key as keyof GridOptions] = value.currentValue;\n }\n _processOnChange(gridOptions, this.api);\n });\n }\n }\n\n public ngOnDestroy(): void {\n if (this._initialised) {\n // need to do this before the destroy, so we know not to emit any events\n // while tearing down the grid.\n this._destroyed = true;\n // could be null if grid failed to initialise\n this.api?.destroy();\n }\n }\n\n // we'll emit the emit if a user is listening for a given event either on the component via normal angular binding\n // or via gridOptions\n protected isEmitterUsed(eventType: string): boolean {\n const emitter = <EventEmitter<any>>(<any>this)[eventType];\n // For RxJs compatibility we need to check for observed v7+ or observers v6\n const emitterAny = emitter as any;\n const hasEmitter = emitterAny?.observed ?? emitterAny?.observers?.length > 0;\n\n // gridReady => onGridReady\n const asEventName = `on${eventType.charAt(0).toUpperCase()}${eventType.substring(1)}`;\n const hasGridOptionListener = !!this.gridOptions && !!(this.gridOptions as any)[asEventName];\n\n return hasEmitter || hasGridOptionListener;\n }\n\n private globalListener(eventType: string, event: any): void {\n // if we are tearing down, don't emit angular events, as this causes\n // problems with the angular router\n if (this._destroyed) {\n return;\n }\n\n // generically look up the eventType\n const emitter = <EventEmitter<any>>(<any>this)[eventType];\n if (emitter && this.isEmitterUsed(eventType)) {\n // Make sure we emit within the angular zone, so change detection works properly\n const fireEmitter = () => this._angularFrameworkOverrides.runInsideAngular(() => emitter.emit(event));\n\n if (this._holdEvents) {\n // if the user is listening to events, wait for ngAfterViewInit to fire first, then emit the grid events\n this._fullyReady.then(() => fireEmitter());\n } else {\n fireEmitter();\n }\n }\n }\n\n /** Provided an initial gridOptions configuration to the component. If a property is specified in both gridOptions and via component binding the component binding takes precedence. */\n @Input() public gridOptions: GridOptions<TData> | undefined;\n /**\n * Used to register AG Grid Modules directly with this instance of the grid.\n * See [Providing Modules To Individual Grids](https://www.ag-grid.com/angular-data-grid/modules/#providing-modules-to-individual-grids) for more information.\n */\n @Input() public modules: Module[] | undefined;\n\n // @START@\n /** Specifies the status bar components to use in the status bar.\n * @agModule `StatusBarModule`\n */\n @Input() public statusBar: { statusPanels: StatusPanelDef[] } | undefined = undefined;\n /** Specifies the side bar components.\n * @agModule `SideBarModule`\n */\n @Input() public sideBar: SideBarDef | string | string[] | boolean | null | undefined = undefined;\n /** Set to `true` to not show the context menu. Use if you don't want to use the default 'right click' context menu.\n * @default false\n */\n @Input({ transform: booleanAttribute }) public suppressContextMenu: boolean | undefined = undefined;\n /** When using `suppressContextMenu`, you can use the `onCellContextMenu` function to provide your own code to handle cell `contextmenu` events.\n * This flag is useful to prevent the browser from showing its default context menu.\n * @default false\n */\n @Input({ transform: booleanAttribute }) public preventDefaultOnContextMenu: boolean | undefined = undefined;\n /** Allows context menu to show, even when `Ctrl` key is held down.\n * @default false\n * @agModule `ContextMenuModule`\n */\n @Input({ transform: booleanAttribute }) public allowContextMenuWithControlKey: boolean | undefined = undefined;\n /** Changes the display type of the column menu.\n * `'new'` just displays the main list of menu items. `'legacy'` displays a tabbed menu.\n * @default 'new'\n * @initial\n */\n @Input() public columnMenu: 'legacy' | 'new' | undefined = undefined;\n /** Only recommended for use if `columnMenu = 'legacy'`.\n * When `true`, the column menu button will always be shown.\n * When `false`, the column menu button will only show when the mouse is over the column header.\n * When using `columnMenu = 'legacy'`, this will default to `false` instead of `true`.\n * @default true\n */\n @Input({ transform: booleanAttribute }) public suppressMenuHide: boolean | undefined = undefined;\n /** Set to `true` to use the browser's default tooltip instead of using the grid's Tooltip Component.\n * @default false\n * @initial\n * @agModule `TooltipModule`\n */\n @Input({ transform: booleanAttribute }) public enableBrowserTooltips: boolean | undefined = undefined;\n /** The trigger that will cause tooltips to show and hide.\n * - `hover` - The tooltip will show/hide when a cell/header is hovered.\n * - `focus` - The tooltip will show/hide when a cell/header is focused.\n * @default 'hover'\n * @initial\n * @agModule `TooltipModule`\n */\n @Input() public tooltipTrigger: 'hover' | 'focus' | undefined = undefined;\n /** The delay in milliseconds that it takes for tooltips to show up once an element is hovered over.\n * **Note:** This property does not work if `enableBrowserTooltips` is `true`.\n * @default 2000\n * @agModule `TooltipModule`\n */\n @Input() public tooltipShowDelay: number | undefined = undefined;\n /** The delay in milliseconds that it takes for tooltips to hide once they have been displayed.\n * **Note:** This property does not work if `enableBrowserTooltips` is `true` and `tooltipHideTriggers` includes `timeout`.\n * @default 10000\n * @agModule `TooltipModule`\n */\n @Input() public tooltipHideDelay: number | undefined = undefined;\n /** Set to `true` to have tooltips follow the cursor once they are displayed.\n * @default false\n * @initial\n * @agModule `TooltipModule`\n */\n @Input({ transform: booleanAttribute }) public tooltipMouseTrack: boolean | undefined = undefined;\n /** This defines when tooltip will show up for Cells, Headers and SetFilter Items.\n * - `standard` - The tooltip always shows up when the items configured with Tooltips are hovered.\n * - `whenTruncated` - The tooltip will only be displayed when the items hovered have truncated (showing ellipsis) values. This property does not work when `enableBrowserTooltips={true}`.\n * @default `standard`\n * @agModule `TooltipModule`\n */\n @Input() public tooltipShowMode: 'standard' | 'whenTruncated' | undefined = undefined;\n /** Set to `true` to enable tooltip interaction. When this option is enabled, the tooltip will not hide while the\n * tooltip itself it being hovered or has focus.\n * @default false\n * @initial\n * @agModule `TooltipModule`\n */\n @Input({ transform: booleanAttribute }) public tooltipInteraction: boolean | undefined = undefined;\n /** DOM element to use as the popup parent for grid popups (context menu, column menu etc).\n */\n @Input() public popupParent: HTMLElement | null | undefined = undefined;\n /** Set to `true` to also include headers when copying to clipboard using `Ctrl + C` clipboard.\n * @default false\n * @agModule `ClipboardModule`\n */\n @Input({ transform: booleanAttribute }) public copyHeadersToClipboard: boolean | undefined = undefined;\n /** Set to `true` to also include group headers when copying to clipboard using `Ctrl + C` clipboard.\n * @default false\n * @agModule `ClipboardModule`\n */\n @Input({ transform: booleanAttribute }) public copyGroupHeadersToClipboard: boolean | undefined = undefined;\n /** Specify the delimiter to use when copying to clipboard.\n * @default '\\t'\n * @agModule `ClipboardModule`\n */\n @Input() public clipboardDelimiter: string | undefined = undefined;\n /** Set to `true` to copy the cell range or focused cell to the clipboard and never the selected rows.\n * @default false\n * @deprecated v32.2 Use `rowSelection.copySelectedRows` instead.\n */\n @Input({ transform: booleanAttribute }) public suppressCopyRowsToClipboard: boolean | undefined = undefined;\n /** Set to `true` to copy rows instead of ranges when a range with only a single cell is selected.\n * @default false\n * @deprecated v32.2 Use `rowSelection.copySelectedRows` instead.\n */\n @Input({ transform: booleanAttribute }) public suppressCopySingleCellRanges: boolean | undefined = undefined;\n /** Set to `true` to work around a bug with Excel (Windows) that adds an extra empty line at the end of ranges copied to the clipboard.\n * @default false\n * @agModule `ClipboardModule`\n */\n @Input({ transform: booleanAttribute }) public suppressLastEmptyLineOnPaste: boolean | undefined = undefined;\n /** Set to `true` to turn off paste operations within the grid.\n * @default false\n * @agModule `ClipboardModule`\n */\n @Input({ transform: booleanAttribute }) public suppressClipboardPaste: boolean | undefined = undefined;\n /** Set to `true` to stop the grid trying to use the Clipboard API, if it is blocked, and immediately fallback to the workaround.\n * @default false\n * @agModule `ClipboardModule`\n */\n @Input({ transform: booleanAttribute }) public suppressClipboardApi: boolean | undefined = undefined;\n /** Set to `true` to block **cut** operations within the grid.\n * @default false\n * @agModule `ClipboardModule`\n */\n @Input({ transform: booleanAttribute }) public suppressCutToClipboard: boolean | undefined = undefined;\n /** Array of Column / Column Group definitions.\n */\n @Input() public columnDefs: (TColDef | ColGroupDef<TData>)[] | null | undefined = undefined;\n /** A default column definition. Items defined in the actual column definitions get precedence.\n */\n @Input() public defaultColDef: ColDef<TData> | undefined = undefined;\n /** A default column group definition. All column group definitions will use these properties. Items defined in the actual column group definition get precedence.\n * @initial\n */\n @Input() public defaultColGroupDef: Partial<ColGroupDef<TData>> | undefined = undefined;\n /** An object map of custom column types which contain groups of properties that column definitions can reuse by referencing in their `type` property.\n */\n @Input() public columnTypes: { [key: string]: ColTypeDef<TData> } | undefined = undefined;\n /** An object map of cell data types to their definitions.\n * Cell data types can either override/update the pre-defined data types\n * (`'text'`, `'number'`, `'boolean'`, `'date'`, `'dateString'` or `'object'`),\n * or can be custom data types.\n */\n @Input() public dataTypeDefinitions:\n | {\n [cellDataType: string]: DataTypeDefinition<TData>;\n }\n | undefined = undefined;\n /** Keeps the order of Columns maintained after new Column Definitions are updated.\n *\n * @default false\n */\n @Input({ transform: booleanAttribute }) public maintainColumnOrder: boolean | undefined = undefined;\n /** Resets pivot column order when impacted by filters, data or configuration changes\n *\n * @default false\n * @agModule `PivotModule`\n */\n @Input({ transform: booleanAttribute }) public enableStrictPivotColumnOrder: boolean | undefined = undefined;\n /** If `true`, then dots in field names (e.g. `'address.firstLine'`) are not treated as deep references. Allows you to use dots in your field name if you prefer.\n * @default false\n */\n @Input({ transform: booleanAttribute }) public suppressFieldDotNotation: boolean | undefined = undefined;\n /** The height in pixels for the row containing the column label header. If not specified, it uses the theme value of `header-height`.\n */\n @Input() public headerHeight: number | undefined = undefined;\n /** The height in pixels for the rows containing header column groups. If not specified, it uses `headerHeight`.\n */\n @Input() public groupHeaderHeight: number | undefined = undefined;\n /** The height in pixels for the row containing the floating filters. If not specified, it uses the theme value of `header-height`.\n */\n @Input() public floatingFiltersHeight: number | undefined = undefined;\n /** The height in pixels for the row containing the columns when in pivot mode. If not specified, it uses `headerHeight`.\n */\n @Input() public pivotHeaderHeight: number | undefined = undefined;\n /** The height in pixels for the row containing header column groups when in pivot mode. If not specified, it uses `groupHeaderHeight`.\n */\n @Input() public pivotGroupHeaderHeight: number | undefined = undefined;\n /** Allow reordering and pinning columns by dragging columns from the Columns Tool Panel to the grid.\n * @default false\n * @agModule `ColumnsToolPanelModule`\n */\n @Input({ transform: booleanAttribute }) public allowDragFromColumnsToolPanel: boolean | undefined = undefined;\n /** Set to `true` to suppress column moving, i.e. to make the columns fixed position.\n * @default false\n */\n @Input({ transform: booleanAttribute }) public suppressMovableColumns: boolean | undefined = undefined;\n /** If `true`, the `ag-column-moving` class is not added to the grid while columns are moving. In the default themes, this results in no animation when moving columns.\n * @default false\n */\n @Input({ transform: booleanAttribute }) public suppressColumnMoveAnimation: boolean | undefined = undefined;\n /** Set to `true` to suppress moving columns while dragging the Column Header. This option highlights the position where the column will be placed and it will only move it on mouse up.\n * @default false\n */\n @Input({ transform: booleanAttribute }) public suppressMoveWhenColumnDragging: boolean | undefined = undefined;\n /** If `true`, when you drag a column out of the grid (e.g. to the group zone) the column is not hidden.\n * @default false\n */\n @Input({ transform: booleanAttribute }) public suppressDragLeaveHidesColumns: boolean | undefined = undefined;\n /** Enable to prevent column visibility changing when grouped columns are changed.\n * @default false\n */\n @Input() public suppressGroupChangesColumnVisibility:\n | boolean\n | 'suppressHideOnGroup'\n | 'suppressShowOnUngroup'\n | undefined = undefined;\n /** By default, when a column is un-grouped, i.e. using the Row Group Panel, it is made visible in the grid. This property stops the column becoming visible again when un-grouping.\n * @default false\n * @deprecated v33.0.0 - Use `suppressGroupChangesColumnVisibility: 'suppressShowOnUngroup'` instead.\n */\n @Input({ transform: booleanAttribute }) public suppressMakeColumnVisibleAfterUnGroup: boolean | undefined =\n undefined;\n /** If `true`, when you drag a column into a row group panel the column is not hidden.\n * @default false\n * @deprecated v33.0.0 - Use `suppressGroupChangesColumnVisibility: 'suppressHideOnGroup'` instead.\n */\n @Input({ transform: booleanAttribute }) public suppressRowGroupHidesColumns: boolean | undefined = undefined;\n /** Set to `'shift'` to have shift-resize as the default resize operation (same as user holding down `Shift` while resizing).\n */\n @Input() public colResizeDefault: 'shift' | undefined = undefined;\n /** Suppresses auto-sizing columns for columns. In other words, double clicking a column's header's edge will not auto-size.\n * @default false\n * @initial\n */\n @Input({ transform: booleanAttribute }) public suppressAutoSize: boolean | undefined = undefined;\n /** Number of pixels to add to a column width after the [auto-sizing](./column-sizing/#auto-size-columns-to-fit-cell-contents) calculation.\n * Set this if you want to add extra room to accommodate (for example) sort icons, or some other dynamic nature of the header.\n * @default 20\n */\n @Input() public autoSizePadding: number | undefined = undefined;\n /** Set this to `true` to skip the `headerName` when `autoSize` is called by default.\n * @default false\n * @initial\n * @agModule `ColumnAutoSizeModule`\n */\n @Input({ transform: booleanAttribute }) public skipHeaderOnAutoSize: boolean | undefined = undefined;\n /** Auto-size the columns when the grid is loaded. Can size to fit the grid width, fit a provided width, or fit the cell contents.\n * @initial\n * @agModule `ColumnAutoSizeModule`\n */\n @Input() public autoSizeStrategy:\n | SizeColumnsToFitGridStrategy\n | SizeColumnsToFitProvidedWidthStrategy\n | SizeColumnsToContentStrategy\n | undefined = undefined;\n /** A map of component names to components.\n * @initial\n */\n @Input() public components: { [p: string]: any } | undefined = undefined;\n /** Set to `'fullRow'` to enable Full Row Editing. Otherwise leave blank to edit one cell at a time.\n * @agModule `TextEditorModule` / `LargeTextEditorModule` / `NumberEditorModule` / `DateEditorModule` / `CheckboxEditorModule` / `CustomEditorModule` / `SelectEditorModule` / `RichSelectModule`\n */\n @Input() public editType: 'fullRow' | undefined = undefined;\n /** Set to `true` to enable Single Click Editing for cells, to start editing with a single click.\n * @default false\n * @agModule `TextEditorModule` / `LargeTextEditorModule` / `NumberEditorModule` / `DateEditorModule` / `CheckboxEditorModule` / `CustomEditorModule` / `SelectEditorModule` / `RichSelectModule`\n */\n @Input({ transform: booleanAttribute }) public singleClickEdit: boolean | undefined = undefined;\n /** Set to `true` so that neither single nor double click starts editing.\n * @default false\n * @agModule `TextEditorModule` / `LargeTextEditorModule` / `NumberEditorModule` / `DateEditorModule` / `CheckboxEditorModule` / `CustomEditorModule` / `SelectEditorModule` / `RichSelectModule`\n */\n @Input({ transform: booleanAttribute }) public suppressClickEdit: boolean | undefined = undefined;\n /** Set to `true` to stop the grid updating data after `Edit`, `Clipboard` and `Fill Handle` operations. When this is set, it is intended the application will update the data, eg in an external immutable store, and then pass the new dataset to the grid. <br />**Note:** `rowNode.setDataValue()` does not update the value of the cell when this is `True`, it fires `onCellEditRequest` instead.\n * @default false\n * @agModule `TextEditorModule` / `LargeTextEditorModule` / `NumberEditorModule` / `DateEditorModule` / `CheckboxEditorModule` / `CustomEditorModule` / `SelectEditorModule` / `RichSelectModule`\n */\n @Input({ transform: booleanAttribute }) public readOnlyEdit: boolean | undefined = undefined;\n /** Set this to `true` to stop cell editing when grid loses focus.\n * The default is that the grid stays editing until focus goes onto another cell.\n * @default false\n * @initial\n * @agModule `TextEditorModule` / `LargeTextEditorModule` / `NumberEditorModule` / `DateEditorModule` / `CheckboxEditorModule` / `CustomEditorModule` / `SelectEditorModule` / `RichSelectModule`\n */\n @Input({ transform: booleanAttribute }) public stopEditingWhenCellsLoseFocus: boolean | undefined = undefined;\n /** Set to `true` along with `enterNavigatesVerticallyAfterEdit` to have Excel-style behaviour for the `Enter` key.\n * i.e. pressing the `Enter` key will move down to the cell beneath and `Shift+Enter` will move up to the cell above.\n * @default false\n * @agModule `TextEditorModule` / `LargeTextEditorModule` / `NumberEditorModule` / `DateEditorModule` / `CheckboxEditorModule` / `CustomEditorModule` / `SelectEditorModule` / `RichSelectModule`\n */\n @Input({ transform: booleanAttribute }) public enterNavigatesVertically: boolean | undefined = undefined;\n /** Set to `true` along with `enterNavigatesVertically` to have Excel-style behaviour for the 'Enter' key.\n * i.e. pressing the Enter key will move down to the cell beneath and Shift+Enter key will move up to the cell above.\n * @default false\n * @agModule `TextEditorModule` / `LargeTextEditorModule` / `NumberEditorModule` / `DateEditorModule` / `CheckboxEditorModule` / `CustomEditorModule` / `SelectEditorModule` / `RichSelectModule`\n */\n @Input({ transform: booleanAttribute }) public enterNavigatesVerticallyAfterEdit: boolean | undefined = undefined;\n /** Forces Cell Editing to start when backspace is pressed. This is only relevant for MacOS users.\n * @agModule `TextEditorModule` / `LargeTextEditorModule` / `NumberEditorModule` / `DateEditorModule` / `CheckboxEditorModule` / `CustomEditorModule` / `SelectEditorModule` / `RichSelectModule`\n */\n @Input({ transform: booleanAttribute }) public enableCellEditingOnBackspace: boolean | undefined = undefined;\n /** Set to `true` to enable Undo / Redo while editing.\n * @initial\n * @agModule `UndoRedoEditModule`\n */\n @Input({ transform: booleanAttribute }) public undoRedoCellEditing: boolean | undefined = undefined;\n /** Set the size of the undo / redo stack.\n * @default 10\n * @initial\n * @agModule `UndoRedoEditModule`\n */\n @Input() public undoRedoCellEditingLimit: number | undefined = undefined;\n /** A default configuration object used to export to CSV.\n * @agModule `CsvExportModule`\n */\n @Input() public defaultCsvExportParams: CsvExportParams | undefined = undefined;\n /** Prevents the user from exporting the grid to CSV.\n * @default false\n */\n @Input({ transform: booleanAttribute }) public suppressCsvExport: boolean | undefined = undefined;\n /** A default configuration object used to export to Excel.\n * @agModule `ExcelExportModule`\n */\n @Input() public defaultExcelExportParams: ExcelExportParams | undefined = undefined;\n /** Prevents the user from exporting the grid to Excel.\n * @default false\n */\n @Input({ transform: booleanAttribute }) public suppressExcelExport: boolean | undefined = undefined;\n /** A list (array) of Excel styles to be used when exporting to Excel with styles.\n * @initial\n * @agModule `ExcelExportModule`\n */\n @Input() public excelStyles: ExcelStyle[] | undefined = undefined;\n /** Text to find within the grid.\n * @agModule `FindModule`\n */\n @Input() public findSearchValue: string | undefined = undefined;\n /** Options for the Find feature.\n * @agModule `FindModule`\n */\n @Input() public findOptions: FindOptions | undefined = undefined;\n /** Rows are filtered using this text as a Quick Filter.\n * Only supported for Client-Side Row Model.\n * @agModule `QuickFilterModule`\n */\n @Input() public quickFilterText: string | undefined = undefined;\n /** Set to `true` to turn on the Quick Filter cache, used to improve performance when using the Quick Filter.\n * @default false\n * @initial\n * @agModule `QuickFilterModule`\n */\n @Input({ transform: booleanAttribute }) public cacheQuickFilter: boolean | undefined = undefined;\n /** Hidden columns are excluded from the Quick Filter by default.\n * To include hidden columns, set to `true`.\n * @default false\n * @agModule `QuickFilterModule`\n */\n @Input({ transform: booleanAttribute }) public includeHiddenColumnsInQuickFilter: boolean | undefined = undefined;\n /** Changes how the Quick Filter splits the Quick Filter text into search terms.\n * @agModule `QuickFilterModule`\n */\n @Input() public quickFilterParser: ((quickFilter: string) => string[]) | undefined = undefined;\n /** Changes the matching logic for whether a row passes the Quick Filter.\n * @agModule `QuickFilterModule`\n */\n @Input() public quickFilterMatcher:\n | ((quickFilterParts: string[], rowQuickFilterAggregateText: string) => boolean)\n | undefined = undefined;\n /** When pivoting, Quick Filter is only applied on the pivoted data\n * (or aggregated data if `groupAggFiltering = true`).\n * Set to `true` to apply Quick Filter before pivoting (/aggregating) instead.\n * @default false\n * @agModule `QuickFilterModule`\n */\n @Input({ transform: booleanAttribute }) public applyQuickFilterBeforePivotOrAgg: boolean | undefined = undefined;\n /** Set to `true` to override the default tree data filtering behaviour to instead exclude child nodes from filter results.\n * @default false\n * @agModule `TreeDataModule`\n */\n @Input({ t