primeng
Version:
[](https://opensource.org/licenses/MIT) [](https://badge.fury.io/js/primeng) [
{"version":3,"file":"primeng-table.mjs","sources":["../../src/app/components/table/table.ts","../../src/app/components/table/primeng-table.ts"],"sourcesContent":["import { NgModule, Component, HostListener, OnInit, OnDestroy, AfterViewInit, Directive, Optional, AfterContentInit,\n Input, Output, EventEmitter, ElementRef, ContentChildren, TemplateRef, QueryList, ViewChild, NgZone, ChangeDetectorRef, OnChanges, SimpleChanges, ChangeDetectionStrategy, Query, ViewEncapsulation, Renderer2} from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { FormsModule } from '@angular/forms';\nimport { PrimeTemplate, SharedModule, FilterMatchMode, FilterOperator, SelectItem, PrimeNGConfig, TranslationKeys, FilterService, OverlayService } from 'primeng/api';\nimport { PaginatorModule } from 'primeng/paginator';\nimport { InputTextModule } from 'primeng/inputtext';\nimport { ButtonModule } from 'primeng/button';\nimport { SelectButtonModule } from 'primeng/selectbutton';\nimport { TriStateCheckboxModule } from 'primeng/tristatecheckbox';\nimport { CalendarModule } from 'primeng/calendar';\nimport { InputNumberModule } from 'primeng/inputnumber';\nimport { DropdownModule } from 'primeng/dropdown';\nimport { DomHandler, ConnectedOverlayScrollHandler } from 'primeng/dom';\nimport { ObjectUtils, UniqueComponentId, ZIndexUtils } from 'primeng/utils';\nimport { SortMeta } from 'primeng/api';\nimport { TableState } from 'primeng/api';\nimport { FilterMetadata } from 'primeng/api';\nimport { Injectable } from '@angular/core';\nimport { BlockableUI } from 'primeng/api';\nimport { Subject, Subscription } from 'rxjs';\nimport { ScrollingModule, CdkVirtualScrollViewport } from '@angular/cdk/scrolling';\nimport {trigger,style,transition,animate,AnimationEvent} from '@angular/animations';\n\n@Injectable()\nexport class TableService {\n\n private sortSource = new Subject<SortMeta|SortMeta[]>();\n private selectionSource = new Subject();\n private contextMenuSource = new Subject<any>();\n private valueSource = new Subject<any>();\n private totalRecordsSource = new Subject<any>();\n private columnsSource = new Subject();\n private resetSource = new Subject();\n\n sortSource$ = this.sortSource.asObservable();\n selectionSource$ = this.selectionSource.asObservable();\n contextMenuSource$ = this.contextMenuSource.asObservable();\n valueSource$ = this.valueSource.asObservable();\n totalRecordsSource$ = this.totalRecordsSource.asObservable();\n columnsSource$ = this.columnsSource.asObservable();\n resetSource$ = this.resetSource.asObservable();\n\n onSort(sortMeta: SortMeta|SortMeta[]) {\n this.sortSource.next(sortMeta);\n }\n\n onSelectionChange() {\n this.selectionSource.next(null);\n }\n\n onResetChange() {\n this.resetSource.next(null);\n }\n\n onContextMenu(data: any) {\n this.contextMenuSource.next(data);\n }\n\n onValueChange(value: any) {\n this.valueSource.next(value);\n }\n\n onTotalRecordsChange(value: number) {\n this.totalRecordsSource.next(value);\n }\n\n onColumnsChange(columns: any[]) {\n this.columnsSource.next(columns);\n }\n}\n\n@Component({\n selector: 'p-table',\n template: `\n <div #container [ngStyle]=\"style\" [class]=\"styleClass\"\n [ngClass]=\"{'p-datatable p-component': true,\n 'p-datatable-hoverable-rows': (rowHover||selectionMode),\n 'p-datatable-auto-layout': autoLayout,\n 'p-datatable-resizable': resizableColumns,\n 'p-datatable-resizable-fit': (resizableColumns && columnResizeMode === 'fit'),\n 'p-datatable-scrollable': scrollable,\n 'p-datatable-scrollable-vertical': scrollable && scrollDirection === 'vertical',\n 'p-datatable-scrollable-horizontal': scrollable && scrollDirection === 'horizontal',\n 'p-datatable-scrollable-both': scrollable && scrollDirection === 'both',\n 'p-datatable-flex-scrollable': (scrollable && scrollHeight === 'flex'),\n 'p-datatable-responsive-stack': responsiveLayout === 'stack',\n 'p-datatable-responsive-scroll': responsiveLayout === 'scroll',\n 'p-datatable-responsive': responsive,\n 'p-datatable-grouped-header': headerGroupedTemplate != null,\n 'p-datatable-grouped-footer': footerGroupedTemplate != null}\" [attr.id]=\"id\">\n <div class=\"p-datatable-loading-overlay p-component-overlay\" *ngIf=\"loading && showLoader\">\n <i [class]=\"'p-datatable-loading-icon pi-spin ' + loadingIcon\"></i>\n </div>\n <div *ngIf=\"captionTemplate\" class=\"p-datatable-header\">\n <ng-container *ngTemplateOutlet=\"captionTemplate\"></ng-container>\n </div>\n <p-paginator [rows]=\"rows\" [first]=\"first\" [totalRecords]=\"totalRecords\" [pageLinkSize]=\"pageLinks\" styleClass=\"p-paginator-top\" [alwaysShow]=\"alwaysShowPaginator\"\n (onPageChange)=\"onPageChange($event)\" [rowsPerPageOptions]=\"rowsPerPageOptions\" *ngIf=\"paginator && (paginatorPosition === 'top' || paginatorPosition =='both')\"\n [templateLeft]=\"paginatorLeftTemplate\" [templateRight]=\"paginatorRightTemplate\" [dropdownAppendTo]=\"paginatorDropdownAppendTo\" [dropdownScrollHeight]=\"paginatorDropdownScrollHeight\"\n [currentPageReportTemplate]=\"currentPageReportTemplate\" [showFirstLastIcon]=\"showFirstLastIcon\" [dropdownItemTemplate]=\"paginatorDropdownItemTemplate\" [showCurrentPageReport]=\"showCurrentPageReport\" [showJumpToPageDropdown]=\"showJumpToPageDropdown\" [showJumpToPageInput]=\"showJumpToPageInput\" [showPageLinks]=\"showPageLinks\"></p-paginator>\n\n <div #wrapper class=\"p-datatable-wrapper\" [ngStyle]=\"{height: scrollHeight}\">\n <table #table *ngIf=\"!virtualScroll\" role=\"table\" class=\"p-datatable-table\" [ngClass]=\"tableStyleClass\" [ngStyle]=\"tableStyle\" [attr.id]=\"id+'-table'\">\n <ng-container *ngTemplateOutlet=\"colGroupTemplate; context {$implicit: columns}\"></ng-container>\n <thead class=\"p-datatable-thead\">\n <ng-container *ngTemplateOutlet=\"headerGroupedTemplate||headerTemplate; context: {$implicit: columns}\"></ng-container>\n </thead>\n <tbody class=\"p-datatable-tbody p-datatable-frozen-tbody\" *ngIf=\"frozenValue||frozenBodyTemplate\" [value]=\"frozenValue\" [frozenRows]=\"true\" [pTableBody]=\"columns\" [pTableBodyTemplate]=\"frozenBodyTemplate\" [frozen]=\"true\"></tbody>\n <tbody class=\"p-datatable-tbody\" [value]=\"dataToRender\" [pTableBody]=\"columns\" [pTableBodyTemplate]=\"bodyTemplate\"></tbody>\n <tfoot *ngIf=\"footerGroupedTemplate||footerTemplate\" class=\"p-datatable-tfoot\">\n <ng-container *ngTemplateOutlet=\"footerGroupedTemplate||footerTemplate; context {$implicit: columns}\"></ng-container>\n </tfoot>\n </table>\n <cdk-virtual-scroll-viewport *ngIf=\"virtualScroll\" [itemSize]=\"virtualRowHeight\" tabindex=\"0\" [style.height]=\"scrollHeight !== 'flex' ? scrollHeight : undefined\" [minBufferPx]=\"minBufferPx\" [maxBufferPx]=\"maxBufferPx\" (scrolledIndexChange)=\"onScrollIndexChange($event)\" class=\"p-datatable-virtual-scrollable-body\">\n <table #table role=\"table\" class=\"p-datatable-table\" [ngClass]=\"tableStyleClass\" [ngStyle]=\"tableStyle\" [attr.id]=\"id+'-table'\">\n <ng-container *ngTemplateOutlet=\"colGroupTemplate; context {$implicit: columns}\"></ng-container>\n <thead #tableHeader class=\"p-datatable-thead\">\n <ng-container *ngTemplateOutlet=\"headerGroupedTemplate||headerTemplate; context: {$implicit: columns}\"></ng-container>\n </thead>\n <tbody class=\"p-datatable-tbody p-datatable-frozen-tbody\" *ngIf=\"frozenValue||frozenBodyTemplate\" [value]=\"frozenValue\" [frozenRows]=\"true\" [pTableBody]=\"columns\" [pTableBodyTemplate]=\"bodyTemplate\" [frozen]=\"true\"></tbody>\n <tbody class=\"p-datatable-tbody\" [value]=\"dataToRender\" [pTableBody]=\"columns\" [pTableBodyTemplate]=\"bodyTemplate\"></tbody>\n <tfoot *ngIf=\"footerGroupedTemplate||footerTemplate\" class=\"p-datatable-tfoot\">\n <ng-container *ngTemplateOutlet=\"footerGroupedTemplate||footerTemplate; context {$implicit: columns}\"></ng-container>\n </tfoot>\n </table>\n </cdk-virtual-scroll-viewport>\n </div>\n\n <p-paginator [rows]=\"rows\" [first]=\"first\" [totalRecords]=\"totalRecords\" [pageLinkSize]=\"pageLinks\" styleClass=\"p-paginator-bottom\" [alwaysShow]=\"alwaysShowPaginator\"\n (onPageChange)=\"onPageChange($event)\" [rowsPerPageOptions]=\"rowsPerPageOptions\" *ngIf=\"paginator && (paginatorPosition === 'bottom' || paginatorPosition =='both')\"\n [templateLeft]=\"paginatorLeftTemplate\" [templateRight]=\"paginatorRightTemplate\" [dropdownAppendTo]=\"paginatorDropdownAppendTo\" [dropdownScrollHeight]=\"paginatorDropdownScrollHeight\"\n [currentPageReportTemplate]=\"currentPageReportTemplate\" [showFirstLastIcon]=\"showFirstLastIcon\" [dropdownItemTemplate]=\"paginatorDropdownItemTemplate\" [showCurrentPageReport]=\"showCurrentPageReport\" [showJumpToPageDropdown]=\"showJumpToPageDropdown\" [showJumpToPageInput]=\"showJumpToPageInput\" [showPageLinks]=\"showPageLinks\"></p-paginator>\n\n <div *ngIf=\"summaryTemplate\" class=\"p-datatable-footer\">\n <ng-container *ngTemplateOutlet=\"summaryTemplate\"></ng-container>\n </div>\n\n <div #resizeHelper class=\"p-column-resizer-helper\" style=\"display:none\" *ngIf=\"resizableColumns\"></div>\n <span #reorderIndicatorUp class=\"pi pi-arrow-down p-datatable-reorder-indicator-up\" style=\"display:none\" *ngIf=\"reorderableColumns\"></span>\n <span #reorderIndicatorDown class=\"pi pi-arrow-up p-datatable-reorder-indicator-down\" style=\"display:none\" *ngIf=\"reorderableColumns\"></span>\n </div>\n `,\n providers: [TableService],\n changeDetection: ChangeDetectionStrategy.Default,\n encapsulation: ViewEncapsulation.None,\n styleUrls: ['./table.css'],\n host: {\n 'class': 'p-element'\n }\n})\nexport class Table implements OnInit, AfterViewInit, AfterContentInit, BlockableUI, OnChanges {\n\n @Input() frozenColumns: any[];\n\n @Input() frozenValue: any[];\n\n @Input() style: any;\n\n @Input() styleClass: string;\n\n @Input() tableStyle: any;\n\n @Input() tableStyleClass: string;\n\n @Input() paginator: boolean;\n\n @Input() pageLinks: number = 5;\n\n @Input() rowsPerPageOptions: any[];\n\n @Input() alwaysShowPaginator: boolean = true;\n\n @Input() paginatorPosition: string = 'bottom';\n\n @Input() paginatorDropdownAppendTo: any;\n\n @Input() paginatorDropdownScrollHeight: string = '200px';\n\n @Input() currentPageReportTemplate: string = '{currentPage} of {totalPages}';\n\n @Input() showCurrentPageReport: boolean;\n\n @Input() showJumpToPageDropdown: boolean;\n\n @Input() showJumpToPageInput: boolean;\n\n @Input() showFirstLastIcon: boolean = true;\n\n @Input() showPageLinks: boolean = true;\n\n @Input() defaultSortOrder: number = 1;\n\n @Input() sortMode: string = 'single';\n\n @Input() resetPageOnSort: boolean = true;\n\n @Input() selectionMode: string;\n\n @Input() selectionPageOnly: boolean;\n\n @Output() selectAllChange: EventEmitter<any> = new EventEmitter();\n\n @Output() selectionChange: EventEmitter<any> = new EventEmitter();\n\n @Input() contextMenuSelection: any;\n\n @Output() contextMenuSelectionChange: EventEmitter<any> = new EventEmitter();\n\n @Input() contextMenuSelectionMode: string = \"separate\";\n\n @Input() dataKey: string;\n\n @Input() metaKeySelection: boolean;\n\n @Input() rowSelectable;\n\n @Input() rowTrackBy: Function = (index: number, item: any) => item;\n\n @Input() lazy: boolean = false;\n\n @Input() lazyLoadOnInit: boolean = true;\n\n @Input() compareSelectionBy: string = 'deepEquals';\n\n @Input() csvSeparator: string = ',';\n\n @Input() exportFilename: string = 'download';\n\n @Input() filters: { [s: string]: FilterMetadata | FilterMetadata[] } = {};\n\n @Input() globalFilterFields: string[];\n\n @Input() filterDelay: number = 300;\n\n @Input() filterLocale: string;\n\n @Input() expandedRowKeys: { [s: string]: boolean; } = {};\n\n @Input() editingRowKeys: { [s: string]: boolean; } = {};\n\n @Input() rowExpandMode: string = 'multiple';\n\n @Input() scrollable: boolean;\n\n @Input() scrollDirection: string = \"vertical\";\n\n @Input() rowGroupMode: string;\n\n @Input() scrollHeight: string;\n\n @Input() virtualScroll: boolean;\n\n @Input() virtualScrollDelay: number = 250;\n\n @Input() virtualRowHeight: number = 28;\n\n @Input() frozenWidth: string;\n\n @Input() responsive: boolean;\n\n @Input() contextMenu: any;\n\n @Input() resizableColumns: boolean;\n\n @Input() columnResizeMode: string = 'fit';\n\n @Input() reorderableColumns: boolean;\n\n @Input() loading: boolean;\n\n @Input() loadingIcon: string = 'pi pi-spinner';\n\n @Input() showLoader: boolean = true;\n\n @Input() rowHover: boolean;\n\n @Input() customSort: boolean;\n\n @Input() showInitialSortBadge: boolean = true;\n\n @Input() autoLayout: boolean;\n\n @Input() exportFunction;\n\n @Input() exportHeader: string;\n\n @Input() stateKey: string;\n\n @Input() stateStorage: string = 'session';\n\n @Input() editMode: string = 'cell';\n\n @Input() groupRowsBy: any;\n\n @Input() groupRowsByOrder: number = 1;\n\n @Input() minBufferPx: number;\n\n @Input() maxBufferPx: number;\n\n @Input() responsiveLayout: string = 'stack';\n\n @Input() breakpoint: string = '960px';\n\n @Output() onRowSelect: EventEmitter<any> = new EventEmitter();\n\n @Output() onRowUnselect: EventEmitter<any> = new EventEmitter();\n\n @Output() onPage: EventEmitter<any> = new EventEmitter();\n\n @Output() onSort: EventEmitter<any> = new EventEmitter();\n\n @Output() onFilter: EventEmitter<any> = new EventEmitter();\n\n @Output() onLazyLoad: EventEmitter<any> = new EventEmitter();\n\n @Output() onRowExpand: EventEmitter<any> = new EventEmitter();\n\n @Output() onRowCollapse: EventEmitter<any> = new EventEmitter();\n\n @Output() onContextMenuSelect: EventEmitter<any> = new EventEmitter();\n\n @Output() onColResize: EventEmitter<any> = new EventEmitter();\n\n @Output() onColReorder: EventEmitter<any> = new EventEmitter();\n\n @Output() onRowReorder: EventEmitter<any> = new EventEmitter();\n\n @Output() onEditInit: EventEmitter<any> = new EventEmitter();\n\n @Output() onEditComplete: EventEmitter<any> = new EventEmitter();\n\n @Output() onEditCancel: EventEmitter<any> = new EventEmitter();\n\n @Output() onHeaderCheckboxToggle: EventEmitter<any> = new EventEmitter();\n\n @Output() sortFunction: EventEmitter<any> = new EventEmitter();\n\n @Output() firstChange: EventEmitter<number> = new EventEmitter();\n\n @Output() rowsChange: EventEmitter<number> = new EventEmitter();\n\n @Output() onStateSave: EventEmitter<any> = new EventEmitter();\n\n @Output() onStateRestore: EventEmitter<any> = new EventEmitter();\n\n @ViewChild('container') containerViewChild: ElementRef;\n\n @ViewChild('resizeHelper') resizeHelperViewChild: ElementRef;\n\n @ViewChild('reorderIndicatorUp') reorderIndicatorUpViewChild: ElementRef;\n\n @ViewChild('reorderIndicatorDown') reorderIndicatorDownViewChild: ElementRef;\n\n @ViewChild('wrapper') wrapperViewChild: ElementRef;\n\n @ViewChild('table') tableViewChild: ElementRef;\n\n @ViewChild('tableHeader') tableHeaderViewChild: ElementRef;\n\n @ViewChild(CdkVirtualScrollViewport) virtualScrollBody: CdkVirtualScrollViewport;\n\n @ContentChildren(PrimeTemplate) templates: QueryList<PrimeTemplate>;\n\n _value: any[] = [];\n\n _columns: any[];\n\n _totalRecords: number = 0;\n\n _first: number = 0;\n\n _rows: number;\n\n filteredValue: any[];\n\n headerTemplate: TemplateRef<any>;\n\n headerGroupedTemplate: TemplateRef<any>;\n\n bodyTemplate: TemplateRef<any>;\n\n loadingBodyTemplate: TemplateRef<any>;\n\n captionTemplate: TemplateRef<any>;\n\n frozenRowsTemplate: TemplateRef<any>;\n\n footerTemplate: TemplateRef<any>;\n\n footerGroupedTemplate: TemplateRef<any>;\n\n summaryTemplate: TemplateRef<any>;\n\n colGroupTemplate: TemplateRef<any>;\n\n expandedRowTemplate: TemplateRef<any>;\n\n groupHeaderTemplate: TemplateRef<any>;\n\n groupFooterTemplate: TemplateRef<any>;\n\n rowspanTemplate: TemplateRef<any>;\n\n frozenExpandedRowTemplate: TemplateRef<any>;\n\n frozenHeaderTemplate: TemplateRef<any>;\n\n frozenBodyTemplate: TemplateRef<any>;\n\n frozenFooterTemplate: TemplateRef<any>;\n\n frozenColGroupTemplate: TemplateRef<any>;\n\n emptyMessageTemplate: TemplateRef<any>;\n\n paginatorLeftTemplate: TemplateRef<any>;\n\n paginatorRightTemplate: TemplateRef<any>;\n\n paginatorDropdownItemTemplate: TemplateRef<any>;\n\n selectionKeys: any = {};\n\n lastResizerHelperX: number;\n\n reorderIconWidth: number;\n\n reorderIconHeight: number;\n\n draggedColumn: any;\n\n draggedRowIndex: number;\n\n droppedRowIndex: number;\n\n rowDragging: boolean;\n\n dropPosition: number;\n\n editingCell: Element;\n\n editingCellData: any;\n\n editingCellField: any;\n\n editingCellRowIndex: number;\n\n selfClick: boolean;\n\n documentEditListener: any;\n\n _multiSortMeta: SortMeta[];\n\n _sortField: string;\n\n _sortOrder: number = 1;\n\n preventSelectionSetterPropagation: boolean;\n\n _selection: any;\n\n _selectAll: boolean | null = null;\n\n anchorRowIndex: number;\n\n rangeRowIndex: number;\n\n filterTimeout: any;\n\n initialized: boolean;\n\n rowTouched: boolean;\n\n restoringSort: boolean;\n\n restoringFilter: boolean;\n\n stateRestored: boolean;\n\n columnOrderStateRestored: boolean;\n\n columnWidthsState: string;\n\n tableWidthState: string;\n\n overlaySubscription: Subscription;\n\n virtualScrollSubscription: Subscription;\n\n resizeColumnElement;\n\n columnResizing: boolean = false;\n\n rowGroupHeaderStyleObject: any = {};\n\n id: string = UniqueComponentId();\n\n styleElement: any;\n\n responsiveStyleElement: any;\n\n constructor(public el: ElementRef, public zone: NgZone, public tableService: TableService, public cd: ChangeDetectorRef, public filterService: FilterService, public overlayService: OverlayService) {}\n\n ngOnInit() {\n if (this.lazy && this.lazyLoadOnInit) {\n if (!this.virtualScroll) {\n this.onLazyLoad.emit(this.createLazyLoadMetadata());\n }\n\n if (this.restoringFilter) {\n this.restoringFilter = false;\n }\n }\n\n if (this.responsiveLayout === 'stack' && !this.scrollable) {\n this.createResponsiveStyle();\n }\n\n this.initialized = true;\n }\n\n ngAfterContentInit() {\n this.templates.forEach((item) => {\n switch (item.getType()) {\n case 'caption':\n this.captionTemplate = item.template;\n break;\n\n case 'header':\n this.headerTemplate = item.template;\n break;\n\n case 'headergrouped':\n this.headerGroupedTemplate = item.template;\n break;\n\n case 'body':\n this.bodyTemplate = item.template;\n break;\n\n case 'loadingbody':\n this.loadingBodyTemplate = item.template;\n break;\n\n case 'footer':\n this.footerTemplate = item.template;\n break;\n\n case 'footergrouped':\n this.footerGroupedTemplate = item.template;\n break;\n\n case 'summary':\n this.summaryTemplate = item.template;\n break;\n\n case 'colgroup':\n this.colGroupTemplate = item.template;\n break;\n\n case 'rowexpansion':\n this.expandedRowTemplate = item.template;\n break;\n\n case 'groupheader':\n this.groupHeaderTemplate = item.template;\n break;\n\n case 'rowspan':\n this.rowspanTemplate = item.template;\n break;\n\n case 'groupfooter':\n this.groupFooterTemplate = item.template;\n break;\n\n case 'frozenrows':\n this.frozenRowsTemplate = item.template;\n break;\n\n case 'frozenheader':\n this.frozenHeaderTemplate = item.template;\n break;\n\n case 'frozenbody':\n this.frozenBodyTemplate = item.template;\n break;\n\n case 'frozenfooter':\n this.frozenFooterTemplate = item.template;\n break;\n\n case 'frozencolgroup':\n this.frozenColGroupTemplate = item.template;\n break;\n\n case 'frozenrowexpansion':\n this.frozenExpandedRowTemplate = item.template;\n break;\n\n case 'emptymessage':\n this.emptyMessageTemplate = item.template;\n break;\n\n case 'paginatorleft':\n this.paginatorLeftTemplate = item.template;\n break;\n\n case 'paginatorright':\n this.paginatorRightTemplate = item.template;\n break;\n\n case 'paginatordropdownitem':\n this.paginatorDropdownItemTemplate = item.template;\n break;\n }\n });\n }\n\n ngAfterViewInit() {\n if (this.isStateful() && this.resizableColumns) {\n this.restoreColumnWidths();\n }\n\n if (this.scrollable && this.virtualScroll) {\n this.virtualScrollSubscription = this.virtualScrollBody.renderedRangeStream.subscribe(range => {\n let top = range.start * this.virtualRowHeight * -1;\n this.tableHeaderViewChild.nativeElement.style.top = top + 'px';\n });\n }\n }\n\n ngOnChanges(simpleChange: SimpleChanges) {\n if (simpleChange.value) {\n if (this.isStateful() && !this.stateRestored) {\n this.restoreState();\n }\n\n this._value = simpleChange.value.currentValue;\n\n if (!this.lazy) {\n this.totalRecords = (this._value ? this._value.length : 0);\n\n if (this.sortMode == 'single' && (this.sortField || this.groupRowsBy))\n this.sortSingle();\n else if (this.sortMode == 'multiple' && (this.multiSortMeta || this.groupRowsBy))\n this.sortMultiple();\n else if (this.hasFilter()) //sort already filters\n this._filter();\n }\n\n this.tableService.onValueChange(simpleChange.value.currentValue);\n }\n\n if (simpleChange.columns) {\n this._columns = simpleChange.columns.currentValue;\n this.tableService.onColumnsChange(simpleChange.columns.currentValue);\n\n if (this._columns && this.isStateful() && this.reorderableColumns && !this.columnOrderStateRestored ) {\n this.restoreColumnOrder();\n }\n }\n\n if (simpleChange.sortField) {\n this._sortField = simpleChange.sortField.currentValue;\n\n //avoid triggering lazy load prior to lazy initialization at onInit\n if ( !this.lazy || this.initialized ) {\n if (this.sortMode === 'single') {\n this.sortSingle();\n }\n }\n }\n\n if (simpleChange.groupRowsBy) {\n //avoid triggering lazy load prior to lazy initialization at onInit\n if ( !this.lazy || this.initialized ) {\n if (this.sortMode === 'single') {\n this.sortSingle();\n }\n }\n }\n\n if (simpleChange.sortOrder) {\n this._sortOrder = simpleChange.sortOrder.currentValue;\n\n //avoid triggering lazy load prior to lazy initialization at onInit\n if ( !this.lazy || this.initialized ) {\n if (this.sortMode === 'single') {\n this.sortSingle();\n }\n }\n }\n\n if (simpleChange.groupRowsByOrder) {\n //avoid triggering lazy load prior to lazy initialization at onInit\n if ( !this.lazy || this.initialized ) {\n if (this.sortMode === 'single') {\n this.sortSingle();\n }\n }\n }\n\n if (simpleChange.multiSortMeta) {\n this._multiSortMeta = simpleChange.multiSortMeta.currentValue;\n if (this.sortMode === 'multiple' && (this.initialized || (!this.lazy && !this.virtualScroll))) {\n this.sortMultiple();\n }\n }\n\n if (simpleChange.selection) {\n this._selection = simpleChange.selection.currentValue;\n\n if (!this.preventSelectionSetterPropagation) {\n this.updateSelectionKeys();\n this.tableService.onSelectionChange();\n }\n this.preventSelectionSetterPropagation = false;\n }\n\n if (simpleChange.selectAll) {\n this._selectAll = simpleChange.selectAll.currentValue;\n\n if (!this.preventSelectionSetterPropagation) {\n this.updateSelectionKeys();\n this.tableService.onSelectionChange();\n\n if (this.isStateful()) {\n this.saveState();\n }\n }\n this.preventSelectionSetterPropagation = false;\n }\n }\n\n @Input() get value(): any[] {\n return this._value;\n }\n set value(val: any[]) {\n this._value = val;\n }\n\n @Input() get columns(): any[] {\n return this._columns;\n }\n set columns(cols: any[]) {\n this._columns = cols;\n }\n\n @Input() get first(): number {\n return this._first;\n }\n set first(val: number) {\n this._first = val;\n }\n\n @Input() get rows(): number {\n return this._rows;\n }\n set rows(val: number) {\n this._rows = val;\n }\n\n @Input() get totalRecords(): number {\n return this._totalRecords;\n }\n set totalRecords(val: number) {\n this._totalRecords = val;\n this.tableService.onTotalRecordsChange(this._totalRecords);\n }\n\n @Input() get sortField(): string {\n return this._sortField;\n }\n\n set sortField(val: string) {\n this._sortField = val;\n }\n\n @Input() get sortOrder(): number {\n return this._sortOrder;\n }\n set sortOrder(val: number) {\n this._sortOrder = val;\n }\n\n @Input() get multiSortMeta(): SortMeta[] {\n return this._multiSortMeta;\n }\n\n set multiSortMeta(val: SortMeta[]) {\n this._multiSortMeta = val;\n }\n\n @Input() get selection(): any {\n return this._selection;\n }\n\n set selection(val: any) {\n this._selection = val;\n }\n\n @Input() get selectAll(): boolean | null {\n return this._selection;\n }\n\n set selectAll(val: boolean | null) {\n this._selection = val;\n }\n\n get dataToRender() {\n let data = this.filteredValue||this.value;\n return data ? ((this.paginator && !this.lazy) ? (data.slice(this.first, this.first + this.rows)) : data) : [];\n }\n\n updateSelectionKeys() {\n if (this.dataKey && this._selection) {\n this.selectionKeys = {};\n if (Array.isArray(this._selection)) {\n for(let data of this._selection) {\n this.selectionKeys[String(ObjectUtils.resolveFieldData(data, this.dataKey))] = 1;\n }\n }\n else {\n this.selectionKeys[String(ObjectUtils.resolveFieldData(this._selection, this.dataKey))] = 1;\n }\n }\n }\n\n onPageChange(event) {\n this.first = event.first;\n this.rows = event.rows;\n\n if (this.lazy) {\n this.onLazyLoad.emit(this.createLazyLoadMetadata());\n }\n\n this.onPage.emit({\n first: this.first,\n rows: this.rows\n });\n\n this.firstChange.emit(this.first);\n this.rowsChange.emit(this.rows);\n this.tableService.onValueChange(this.value);\n\n if (this.isStateful()) {\n this.saveState();\n }\n\n this.anchorRowIndex = null;\n\n if (this.scrollable) {\n this.resetScrollTop();\n }\n }\n\n sort(event) {\n let originalEvent = event.originalEvent;\n\n if (this.sortMode === 'single') {\n this._sortOrder = (this.sortField === event.field) ? this.sortOrder * -1 : this.defaultSortOrder;\n this._sortField = event.field;\n\n if (this.resetPageOnSort) {\n this._first = 0;\n this.firstChange.emit(this._first);\n\n if (this.scrollable) {\n this.resetScrollTop();\n }\n }\n\n this.sortSingle();\n }\n if (this.sortMode === 'multiple') {\n let metaKey = originalEvent.metaKey || originalEvent.ctrlKey;\n let sortMeta = this.getSortMeta(event.field);\n\n if (sortMeta) {\n if (!metaKey) {\n this._multiSortMeta = [{ field: event.field, order: sortMeta.order * -1 }];\n\n if (this.resetPageOnSort) {\n this._first = 0;\n this.firstChange.emit(this._first);\n\n if (this.scrollable) {\n this.resetScrollTop();\n }\n }\n }\n else {\n sortMeta.order = sortMeta.order * -1;\n }\n }\n else {\n if (!metaKey || !this.multiSortMeta) {\n this._multiSortMeta = [];\n\n if (this.resetPageOnSort) {\n this._first = 0;\n this.firstChange.emit(this._first);\n }\n }\n this._multiSortMeta.push({ field: event.field, order: this.defaultSortOrder });\n }\n\n this.sortMultiple();\n }\n\n if (this.isStateful()) {\n this.saveState();\n }\n\n this.anchorRowIndex = null;\n }\n\n sortSingle() {\n let field = this.sortField || this.groupRowsBy;\n let order = this.sortField ? this.sortOrder : this.groupRowsByOrder;\n if (this.groupRowsBy && this.sortField && this.groupRowsBy !== this.sortField) {\n this._multiSortMeta = [this.getGroupRowsMeta(), {field: this.sortField, order: this.sortOrder}];\n this.sortMultiple();\n return;\n }\n\n if (field && order) {\n if (this.restoringSort) {\n this.restoringSort = false;\n }\n\n if (this.lazy) {\n this.onLazyLoad.emit(this.createLazyLoadMetadata());\n }\n else if (this.value) {\n if (this.customSort) {\n this.sortFunction.emit({\n data: this.value,\n mode: this.sortMode,\n field: field,\n order: order\n });\n }\n else {\n this.value.sort((data1, data2) => {\n let value1 = ObjectUtils.resolveFieldData(data1, field);\n let value2 = ObjectUtils.resolveFieldData(data2, field);\n let result = null;\n\n if (value1 == null && value2 != null)\n result = -1;\n else if (value1 != null && value2 == null)\n result = 1;\n else if (value1 == null && value2 == null)\n result = 0;\n else if (typeof value1 === 'string' && typeof value2 === 'string')\n result = value1.localeCompare(value2);\n else\n result = (value1 < value2) ? -1 : (value1 > value2) ? 1 : 0;\n\n return (order * result);\n });\n\n this._value = [...this.value];\n }\n\n if (this.hasFilter()) {\n this._filter();\n }\n }\n\n let sortMeta: SortMeta = {\n field: field,\n order: order\n };\n\n this.onSort.emit(sortMeta);\n this.tableService.onSort(sortMeta);\n }\n }\n\n sortMultiple() {\n if (this.groupRowsBy) {\n if (!this._multiSortMeta)\n this._multiSortMeta = [this.getGroupRowsMeta()]\n else if (this.multiSortMeta[0].field !== this.groupRowsBy)\n this._multiSortMeta = [this.getGroupRowsMeta(), ...this._multiSortMeta]\n }\n\n if (this.multiSortMeta) {\n if (this.lazy) {\n this.onLazyLoad.emit(this.createLazyLoadMetadata());\n }\n else if (this.value) {\n if (this.customSort) {\n this.sortFunction.emit({\n data: this.value,\n mode: this.sortMode,\n multiSortMeta: this.multiSortMeta\n });\n }\n else {\n this.value.sort((data1, data2) => {\n return this.multisortField(data1, data2, this.multiSortMeta, 0);\n });\n\n this._value = [...this.value];\n }\n\n if (this.hasFilter()) {\n this._filter();\n }\n }\n\n this.onSort.emit({\n multisortmeta: this.multiSortMeta\n });\n this.tableService.onSort(this.multiSortMeta);\n }\n }\n\n multisortField(data1, data2, multiSortMeta, index) {\n let value1 = ObjectUtils.resolveFieldData(data1, multiSortMeta[index].field);\n let value2 = ObjectUtils.resolveFieldData(data2, multiSortMeta[index].field);\n let result = null;\n\n if (value1 == null && value2 != null)\n result = -1;\n else if (value1 != null && value2 == null)\n result = 1;\n else if (value1 == null && value2 == null)\n result = 0;\n else if (typeof value1 == 'string' || value1 instanceof String) {\n if (value1.localeCompare && (value1 != value2)) {\n return (multiSortMeta[index].order * value1.localeCompare(value2));\n }\n }\n else {\n result = (value1 < value2) ? -1 : 1;\n }\n\n if (value1 == value2) {\n return (multiSortMeta.length - 1) > (index) ? (this.multisortField(data1, data2, multiSortMeta, index + 1)) : 0;\n }\n\n return (multiSortMeta[index].order * result);\n }\n\n getSortMeta(field: string) {\n if (this.multiSortMeta && this.multiSortMeta.length) {\n for (let i = 0; i < this.multiSortMeta.length; i++) {\n if (this.multiSortMeta[i].field === field) {\n return this.multiSortMeta[i];\n }\n }\n }\n\n return null;\n }\n\n isSorted(field: string) {\n if (this.sortMode === 'single') {\n return (this.sortField && this.sortField === field);\n }\n else if (this.sortMode === 'multiple') {\n let sorted = false;\n if (this.multiSortMeta) {\n for(let i = 0; i < this.multiSortMeta.length; i++) {\n if (this.multiSortMeta[i].field == field) {\n sorted = true;\n break;\n }\n }\n }\n return sorted;\n }\n }\n\n handleRowClick(event) {\n let target = (<HTMLElement> event.originalEvent.target);\n let targetNode = target.nodeName;\n let parentNode = target.parentElement && target.parentElement.nodeName;\n if (targetNode == 'INPUT' || targetNode == 'BUTTON' || targetNode == 'A' ||\n parentNode == 'INPUT' || parentNode == 'BUTTON' || parentNode == 'A' ||\n (DomHandler.hasClass(event.originalEvent.target, 'p-clickable'))) {\n return;\n }\n\n if (this.selectionMode) {\n let rowData = event.rowData;\n let rowIndex = event.rowIndex;\n\n this.preventSelectionSetterPropagation = true;\n if (this.isMultipleSelectionMode() && event.originalEvent.shiftKey && this.anchorRowIndex != null) {\n DomHandler.clearSelection();\n if (this.rangeRowIndex != null) {\n this.clearSelectionRange(event.originalEvent);\n }\n\n this.rangeRowIndex = rowIndex;\n this.selectRange(event.originalEvent, rowIndex);\n }\n else {\n let selected = this.isSelected(rowData);\n\n if (!selected && !this.isRowSelectable(rowData, rowIndex)) {\n return;\n }\n\n let metaSelection = this.rowTouched ? false : this.metaKeySelection;\n let dataKeyValue = this.dataKey ? String(ObjectUtils.resolveFieldData(rowData, this.dataKey)) : null;\n this.anchorRowIndex = rowIndex;\n this.rangeRowIndex = rowIndex;\n\n if (metaSelection) {\n let metaKey = event.originalEvent.metaKey||event.originalEvent.ctrlKey;\n\n if (selected && metaKey) {\n if (this.isSingleSelectionMode()) {\n this._selection = null;\n this.selectionKeys = {};\n this.selectionChange.emit(null);\n }\n else {\n let selectionIndex = this.findIndexInSelection(rowData);\n this._selection = this.selection.filter((val,i) => i!=selectionIndex);\n this.selectionChange.emit(this.selection);\n if (dataKeyValue) {\n delete this.selectionKeys[dataKeyValue];\n }\n }\n\n this.onRowUnselect.emit({originalEvent: event.originalEvent, data: rowData, type: 'row'});\n }\n else {\n if (this.isSingleSelectionMode()) {\n this._selection = rowData;\n this.selectionChange.emit(rowData);\n if (dataKeyValue) {\n this.selectionKeys = {};\n this.selectionKeys[dataKeyValue] = 1;\n }\n }\n else if (this.isMultipleSelectionMode()) {\n if (metaKey) {\n this._selection = this.selection||[];\n }\n else {\n this._selection = [];\n this.selectionKeys = {};\n }\n\n this._selection = [...this.selection,rowData];\n this.selectionChange.emit(this.selection);\n if (dataKeyValue) {\n this.selectionKeys[dataKeyValue] = 1;\n }\n }\n\n this.onRowSelect.emit({originalEvent: event.originalEvent, data: rowData, type: 'row', index: rowIndex});\n }\n }\n else {\n if (this.selectionMode === 'single') {\n if (selected) {\n this._selection = null;\n this.selectionKeys = {};\n this.selectionChange.emit(this.selection);\n this.onRowUnselect.emit({ originalEvent: event.originalEvent, data: rowData, type: 'row', index: rowIndex });\n }\n else {\n this._selection = rowData;\n this.selectionChange.emit(this.selection);\n this.onRowSelect.emit({ originalEvent: event.originalEvent, data: rowData, type: 'row', index: rowIndex });\n if (dataKeyValue) {\n this.selectionKeys = {};\n this.selectionKeys[dataKeyValue] = 1;\n }\n }\n }\n else if (this.selectionMode === 'multiple') {\n if (selected) {\n let selectionIndex = this.findIndexInSelection(rowData);\n this._selection = this.selection.filter((val, i) => i != selectionIndex);\n this.selectionChange.emit(this.selection);\n this.onRowUnselect.emit({ originalEvent: event.originalEvent, data: rowData, type: 'row', index: rowIndex });\n if (dataKeyValue) {\n delete this.selectionKeys[dataKeyValue];\n }\n }\n else {\n this._selection = this.selection ? [...this.selection, rowData] : [rowData];\n this.selectionChange.emit(this.selection);\n this.onRowSelect.emit({ originalEvent: event.originalEvent, data: rowData, type: 'row', index: rowIndex });\n if (dataKeyValue) {\n this.selectionKeys[dataKeyValue] = 1;\n }\n }\n }\n }\n }\n\n this.tableService.onSelectionChange();\n\n if (this.isStateful()) {\n this.saveState();\n }\n }\n\n this.rowTouched = false;\n }\n\n handleRowTouchEnd(event) {\n this.rowTouched = true;\n }\n\n handleRowRightClick(event) {\n if (this.contextMenu) {\n const rowData = event.rowData;\n const rowIndex = event.rowIndex;\n\n if (this.contextMenuSelectionMode === 'separate') {\n this.contextMenuSelection = rowData;\n this.contextMenuSelectionChange.emit(rowData);\n this.onContextMenuSelect.emit({originalEvent: event.originalEvent, data: rowData, index: event.rowIndex});\n this.contextMenu.show(event.originalEvent);\n this.tableService.onContextMenu(rowData);\n }\n else if (this.contextMenuSelectionMode === 'joint') {\n this.preventSelectionSetterPropagation = true;\n let selected = this.isSelected(rowData);\n let dataKeyValue = this.dataKey ? String(ObjectUtils.resolveFieldData(rowData, this.dataKey)) : null;\n\n if (!selected) {\n if (!this.isRowSelectable(rowData, rowIndex)) {\n return;\n }\n\n if (this.isSingleSelectionMode()) {\n this.selection = rowData;\n this.selectionChange.emit(rowData);\n\n if (dataKeyValue) {\n this.selectionKeys = {};\n this.selectionKeys[dataKeyValue] = 1;\n }\n }\n else if (this.isMultipleSelectionMode()) {\n this._selection = this.selection ? [...this.selection, rowData] : [rowData];\n this.selectionChange.emit(this.selection);\n\n if (dataKeyValue) {\n this.selectionKeys[dataKeyValue] = 1;\n }\n }\n }\n\n this.tableService.onSelectionChange();\n this.contextMenu.show(event.originalEvent);\n this.onContextMenuSelect.emit({originalEvent: event, data: rowData, index: event.rowIndex});\n }\n }\n }\n\n selectRange(event: MouseEvent, rowIndex: number) {\n let rangeStart, rangeEnd;\n\n if (this.anchorRowIndex > rowIndex) {\n rangeStart = rowIndex;\n rangeEnd = this.anchorRowIndex;\n }\n else if (this.anchorRowIndex < rowIndex) {\n rangeStart = this.anchorRowIndex;\n rangeEnd = rowIndex;\n }\n else {\n rangeStart = rowIndex;\n rangeEnd = rowIndex;\n }\n\n if (this.lazy && this.paginator) {\n rangeStart -= this.first;\n rangeEnd -= this.first;\n }\n\n let rangeRowsData = [];\n for(let i = rangeStart; i <= rangeEnd; i++) {\n let rangeRowData = this.filteredValue ? this.filteredValue[i] : this.value[i];\n if (!this.isSelected(rangeRowData)) {\n if (!this.isRowSelectable(rangeRowData, rowIndex)) {\n continue;\n }\n\n rangeRowsData.push(rangeRowData);\n this._selection = [...this.selection, rangeRowData];\n let dataKeyValue: string = this.dataKey ? String(ObjectUtils.resolveFieldData(rangeRowData, this.dataKey)) : null;\n if (dataKeyValue) {\n this.selectionKeys[dataKeyValue] = 1;\n }\n }\n }\n this.selectionChange.emit(this.selection);\n this.onRowSelect.emit({originalEvent: event, data: rangeRowsData, type: 'row'});\n }\n\n clearSelectionRange(event: MouseEvent) {\n let rangeStart, rangeEnd;\n\n if (this.rangeRowIndex > this.anchorRowIndex) {\n rangeStart = this.anchorRowIndex;\n rangeEnd = this.rangeRowIndex;\n }\n else if (this.rangeRowIndex < this.anchorRowIndex) {\n rangeStart = this.rangeRowIndex;\n rangeEnd = this.anchorRowIndex;\n }\n else {\n rangeStart = this.rangeRowIndex;\n rangeEnd = this.rangeRowIndex;\n }\n\n for(let i = rangeStart; i <= rangeEnd; i++) {\n let rangeRowData = this.value[i];\n let selectionIndex = this.findIndexInSelection(rangeRowData);\n this._selection = this.selection.filter((val,i) => i!=selectionIndex);\n let dataKeyValue: string = this.dataKey ? String(ObjectUtils.resolveFieldData(rangeRowData, this.dataKey)) : null;\n if (dataKeyValue) {\n delete this.selectionKeys[dataKeyValue];\n }\n this.onRowUnselect.emit({originalEvent: event, data: rangeRowData, type: 'row'});\n }\n }\n\n isSelected(rowData) {\n if (rowData && this.selection) {\n if (this.dataKey) {\n return this.selectionKeys[ObjectUtils.resolveFieldData(rowData, this.dataKey)] !== undefined;\n }\n else {\n if (this.selection instanceof Array)\n return this.findIndexInSelection(rowData) > -1;\n else\n return this.equals(rowData, this.selection);\n }\n }\n\n return false;\n }\n\n findIndexInSelection(rowData: any) {\n let index: number = -1;\n if (this.selection && this.selection.length) {\n for (let i = 0; i < this.selection.length; i++) {\n if (this.equals(rowData, this.selection[i])) {\n index = i;\n break;\n }\n }\n }\n\n