UNPKG

@progress/kendo-angular-treelist

Version:

Kendo UI TreeList for Angular - Display hierarchical data in an Angular tree grid view that supports sorting, filtering, paging, and much more.

666 lines (665 loc) 30.4 kB
/**----------------------------------------------------------------------------------------- * Copyright © 2025 Progress Software Corporation. All rights reserved. * Licensed under commercial license. See LICENSE.md in the project root for more information *-------------------------------------------------------------------------------------------*/ import { Component, Input, NgZone, Renderer2, ElementRef, HostBinding } from '@angular/core'; import { ChangeNotificationService } from '../data/change-notification.service'; import { isChanged, isPresent } from '../utils'; import { NoRecordsTemplateDirective } from './no-records-template.directive'; import { EditService } from '../editing/edit.service'; import { LocalizationService } from '@progress/kendo-angular-l10n'; import { columnsSpan } from "../columns/column-common"; import { closest, closestInScope, hasClasses, isFocusableWithTabKey, matchesClasses, matchesNodeName } from './common/dom-queries'; import { DomEventsService } from '../common/dom-events.service'; import { ColumnInfoService } from "../common/column-info.service"; import { hasFilterRow } from '../filtering/filterable'; import { NavigationService } from '../navigation/navigation.service'; import { Keys } from '@progress/kendo-angular-common'; import { defaultTrackBy } from '../common/default-track-by'; import { ExpandStateService } from '../expand-state/expand-state.service'; import { SelectionService } from '../selection/selection.service'; import { NON_DATA_CELL_CLASSES, NON_DATA_ROW_CLASSES, IGNORE_CONTAINER_CLASSES, ICON_CLASS, EMPTY_ICON_CLASS, DRAG_HANDLE_CLASS } from './constants'; import { ColumnsContainer } from '../columns/columns-container'; import { IconWrapperComponent } from '@progress/kendo-angular-icons'; import { LevelItemsPipe } from './common/level-items.pipe'; import { LogicalCellDirective } from '../navigation/logical-cell.directive'; import { CellComponent } from './cell.component'; import { LogicalRowDirective } from '../navigation/logical-row.directive'; import { NgTemplateOutlet, NgClass, NgStyle } from '@angular/common'; import * as i0 from "@angular/core"; import * as i1 from "../data/change-notification.service"; import * as i2 from "../editing/edit.service"; import * as i3 from "@progress/kendo-angular-l10n"; import * as i4 from "../common/dom-events.service"; import * as i5 from "../common/column-info.service"; import * as i6 from "../navigation/navigation.service"; import * as i7 from "../expand-state/expand-state.service"; import * as i8 from "../selection/selection.service"; const columnCellIndex = (cell, cells) => { for (let idx = 0; idx < cells.length; idx++) { if (cells[idx] === cell) { return idx; } } }; /** * @hidden */ export class TableBodyComponent { changeNotification; editService; localization; ngZone; renderer; element; domEvents; columnInfoService; navigationService; expandState; selection; hostClass = true; columns = []; allColumns; noRecordsTemplate; view; skip = 0; filterable; noRecordsText; isLocked = false; lockedColumnsCount = 0; totalColumnsCount = 0; virtualColumns; expandIcons; trackBy = defaultTrackBy; totalColumns; noneIcon = { name: 'none', content: '', viewBox: '0 0 24 24' }; clickSubscription; l10nSubscription; cellKeydownSubscription; clickTimeout; headerOffset; rowClass = () => null; constructor(changeNotification, editService, localization, ngZone, renderer, element, domEvents, columnInfoService, navigationService, expandState, selection) { this.changeNotification = changeNotification; this.editService = editService; this.localization = localization; this.ngZone = ngZone; this.renderer = renderer; this.element = element; this.domEvents = domEvents; this.columnInfoService = columnInfoService; this.navigationService = navigationService; this.expandState = expandState; this.selection = selection; this.cellKeydownSubscription = this.navigationService.cellKeydown.subscribe((args) => this.cellKeydownHandler(args)); this.trackByWrapper = this.trackByWrapper.bind(this); this.trackByColumns = this.trackByColumns.bind(this); this.noRecordsText = this.localization.get('noRecords'); this.selection.registerTable(this); } get newDataItem() { return this.editService.newDataItem; } // Number of unlocked columns in the next table, if any unlockedColumnsCount(item) { const allColumns = this.allColumns || this.columns; let allColumnsCount = allColumns.length; allColumns.forEach(column => { if (column.isSpanColumn) { allColumnsCount += column.colspan - 1; } }); const contentColumnsCount = this.totalColumnsCount - this.lockedColumnsCount - allColumnsCount; const headerFooterColumnsCount = this.totalColumnsCount - this.lockedColumnsCount - (this.allColumns || this.columns).length; return item && item.type === 'data' ? contentColumnsCount : headerFooterColumnsCount; } get hasData() { const view = this.view; return view && view.data !== null && view.data !== undefined && view.data.length > 0; } isOdd(item) { return item.index % 2 !== 0; } trackByWrapper(index, item) { return this.trackBy(index, item); } trackByColumns(index, item) { return this.virtualColumns ? index : item; } ngOnChanges(changes) { if (isChanged("columns", changes, false)) { this.changeNotification.notify(); } } addRowLogicalIndex() { return this.columnInfoService.totalLevels + 1; } logicalColIndex(column) { if (!isPresent(column.leafIndex)) { return -1; } return column.leafIndex; } cellExpandable(item, column) { if (!column.expandable || !item.hasChildren) { return false; } return true; } ariaRowExpanded(item) { return this.lockedColumnsCount < 1 ? Boolean(item.expanded) : undefined; } ariaRowSelected(item) { return this.lockedColumnsCount < 1 ? Boolean(item.selected) : undefined; } ariaExpanded(item, column) { if (!column.expandable || !item.hasChildren) { return; } return Boolean(item.expanded); } ariaSelected(item, column, columnIndex) { if (!this.selection.enabled) { return; } return item.selected || this.isCellSelected(item.data, column, columnIndex); } ngOnInit() { this.ngZone.runOutsideAngular(() => { const clickHandler = this.clickHandler.bind(this); const mousedownSubscription = this.renderer.listen(this.element.nativeElement, 'mousedown', clickHandler); const clickSubscription = this.renderer.listen(this.element.nativeElement, 'click', clickHandler); const contextmenuSubscription = this.renderer.listen(this.element.nativeElement, 'contextmenu', clickHandler); this.clickSubscription = () => { mousedownSubscription(); clickSubscription(); contextmenuSubscription(); }; }); let originalNoRecordText = this.localization.get('noRecords'); this.l10nSubscription = this.localization.changes.subscribe(() => { if (this.noRecordsText === originalNoRecordText) { this.noRecordsText = this.localization.get('noRecords'); originalNoRecordText = this.noRecordsText; } }); } ngDoCheck() { this.headerOffset = this.columnInfoService.totalLevels + (hasFilterRow(this.filterable) ? 1 : 0); if (this.view?.data) { this.view.data.forEach((item) => { if (item.type === 'data') { item.isEditing = this.editService.isEdited(item.data); } }); } } ngOnDestroy() { if (this.clickSubscription) { this.clickSubscription(); } if (this.l10nSubscription) { this.l10nSubscription.unsubscribe(); } this.cellKeydownSubscription.unsubscribe(); this.selection.unregisterTable(this); clearTimeout(this.clickTimeout); } isEditingCell(item, column) { return Boolean(item.editContext && this.editService.isEditingColumn(column)); } isEditingRow(item) { return Boolean(item.editContext) || item.isNew; } get columnsContainer() { return this.columnInfoService.columnsContainer; } get hasFooter() { return this.columnsContainer.hasFooter; } get columnsSpan() { return columnsSpan(this.columns); } get allColumnsSpan() { return columnsSpan(this.allColumns || this.columns); } get colSpan() { return this.columnsSpan; } get footerColumns() { return this.isLocked ? this.columnsContainer.lockedColumnsToRender : this.columnsContainer.nonLockedColumnsToRender; } logicalRowIndex(rowIndex) { return 1 + rowIndex + this.headerOffset; } isCellSelected(dataItem, column, columnIndex) { return this.selection.isCellSelected(dataItem, column, columnIndex); } targetArgs(target, skipFocusable) { const { cell, row } = this.targetElements(target); if (cell && (!skipFocusable || target === cell || !isFocusableWithTabKey(target, false))) { const index = columnCellIndex(cell, row.cells); const column = this.columns.toArray()[index]; const columnIndex = this.lockedColumnsCount + index; return { item: this.rowItem(row), column: column, columnIndex: columnIndex }; } } checkIfClickable(target, cell) { return !matchesNodeName('label')(target) && (!hasClasses(target, ICON_CLASS) || hasClasses(target, EMPTY_ICON_CLASS)) && !closestInScope(target, matchesClasses(IGNORE_CONTAINER_CLASSES), cell); } clickHandler(eventArg) { const target = eventArg.target; const targetElementsResult = this.targetElements(target); if (!targetElementsResult) { return; } const { cell, row } = targetElementsResult; const forbiddenCellClasses = NON_DATA_CELL_CLASSES.concat(` ${DRAG_HANDLE_CLASS}`); const isValidCell = cell ? !hasClasses(cell, forbiddenCellClasses) : false; const isValidRow = row ? !hasClasses(row, NON_DATA_ROW_CLASSES) : false; if (isValidRow && isValidCell) { if (this.expandClick(eventArg, row) || this.checkboxClick(cell, row, eventArg) && !this.selection.dragging) { return; } this.editService.preventCellClose(); const focusable = target !== cell && isFocusableWithTabKey(target, false); const targetClickable = this.checkIfClickable(target, cell); if (!focusable && targetClickable) { const args = this.cellClickArgs(cell, row, eventArg); if (!args) { return; } if (eventArg.type === 'mousedown') { this.domEvents.cellMousedown.emit(args); } else { if (args.isEditedColumn || !this.editService.closeCell(eventArg)) { if (eventArg.type === 'click') { this.clickTimeout = setTimeout(() => { this.emitCellClick(args); }, 0); } else { this.emitCellClick(args); } } } } } } emitCellClick(args) { this.domEvents.cellClick.emit(args); } cellKeydownHandler(args) { if (args.code === Keys.Enter || args.code === Keys.NumpadEnter) { this.clickHandler(args); } } cellClickArgs(cell, row, eventArg) { const index = columnCellIndex(cell, row.cells); const column = this.columns.toArray()[index]; const columnIndex = this.lockedColumnsCount + index; const viewItem = this.rowItem(row); if (viewItem.type !== 'data') { return; } const type = eventArg.type === 'keydown' ? 'click' : eventArg.type; return { column: column, columnIndex: columnIndex, viewItem: viewItem, dataItem: viewItem.data, rowIndex: viewItem.index, index: viewItem.index, // backwards compat (https://github.com/telerik/kendo-angular/issues/3101) isEditedColumn: (viewItem.editContext && this.editService.isEditingColumn(column)), isEdited: viewItem.isNew || (viewItem.editContext && this.editService.isEditedColumn(column)), originalEvent: eventArg, type: type }; } targetElements(target) { const element = this.element.nativeElement; let row, body, treelistElement; const currentTarget = target; const treelistTbody = closest(currentTarget, (el) => matchesNodeName('tbody')(el) && el === element); if (!treelistTbody) { return null; } const cell = closest(currentTarget, (el) => { if (!matchesNodeName('td')(el)) { return false; } const parentRow = el.parentElement; if (!parentRow || !matchesNodeName('tr')(parentRow)) { return false; } return parentRow.parentElement === treelistTbody; }); if (cell) { row = cell.parentElement; body = treelistTbody; const targetTreelist = closest(currentTarget, (el) => hasClasses(el, 'k-treelist')); const currentTreelist = closest(element, (el) => hasClasses(el, 'k-treelist')); treelistElement = targetTreelist !== currentTreelist ? targetTreelist : null; } if (cell && body === element && !treelistElement) { return { cell, row }; } return {}; } expandClick(eventArg, row) { if (eventArg.type === 'click' && !hasClasses(eventArg.target, EMPTY_ICON_CLASS) && eventArg.target.closest('.k-treelist-toggle')) { eventArg.preventDefault(); const viewItem = this.rowItem(row); if (viewItem.type === 'data') { this.ngZone.run(() => { this.expandState.toggleState(viewItem.data); }); return true; } } } checkboxClick(cell, row, eventArg) { const isCheckboxCell = hasClasses(cell, 'k-checkbox-cell'); const isCheckbox = eventArg.target.closest('.k-checkbox-wrap'); if (isCheckboxCell && !isCheckbox && this.selection.settings.checkboxOnly) { return; } if (eventArg.type === 'click' && isCheckboxCell && this.selection.enabled && this.selection.rowSelection) { const args = this.cellClickArgs(cell, row, eventArg); this.selection.checkboxClick(args); if (eventArg.target.checked !== this.selection.isRowSelected(args.dataItem)) { eventArg.preventDefault(); } return true; } } rowItem(row) { let viewIndex = row.getAttribute('data-treelist-view-index'); viewIndex = viewIndex ? parseInt(viewIndex, 10) : -1; const viewItem = this.view.at(viewIndex); return viewItem; } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: TableBodyComponent, deps: [{ token: i1.ChangeNotificationService }, { token: i2.EditService }, { token: i3.LocalizationService }, { token: i0.NgZone }, { token: i0.Renderer2 }, { token: i0.ElementRef }, { token: i4.DomEventsService }, { token: i5.ColumnInfoService }, { token: i6.NavigationService }, { token: i7.ExpandStateService }, { token: i8.SelectionService }], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: TableBodyComponent, isStandalone: true, selector: "[kendoTreeListTableBody]", inputs: { columns: "columns", allColumns: "allColumns", noRecordsTemplate: "noRecordsTemplate", view: "view", skip: "skip", filterable: "filterable", noRecordsText: "noRecordsText", isLocked: "isLocked", lockedColumnsCount: "lockedColumnsCount", totalColumnsCount: "totalColumnsCount", virtualColumns: "virtualColumns", expandIcons: "expandIcons", trackBy: "trackBy", totalColumns: "totalColumns", rowClass: "rowClass" }, host: { properties: { "class.k-table-tbody": "this.hostClass" } }, usesOnChanges: true, ngImport: i0, template: ` @if (!hasData) { <tr class="k-grid-norecords"> <td [attr.colspan]="colSpan" class="k-table-td"> @if (noRecordsTemplate?.templateRef) { <ng-container [ngTemplateOutlet]="noRecordsTemplate.templateRef"> </ng-container> } @if (!noRecordsTemplate?.templateRef) { {{noRecordsText}} } </td> </tr> } @for (item of view?.data; track trackByWrapper($index, item); let rowIndex = $index) { @if (item.type === 'data') { <tr kendoTreeListLogicalRow [dataRowIndex]="$any(item).index" [dataItem]="item.data" [logicalRowIndex]="logicalRowIndex(item.rowIndex)" [logicalSlaveRow]="lockedColumnsCount > 0" [logicalCellsCount]="columns.length" [logicalSlaveCellsCount]="unlockedColumnsCount(item)" [totalColumns]="totalColumns" [isNew]="item.isNew" [attr.aria-expanded]="ariaRowExpanded(item)" [ngClass]="rowClass({ dataItem: item.data, index: $any(item).index })" class="k-table-row{{isOdd(item) ? ' k-table-alt-row' : ''}}" [class.k-grid-edit-row]="isEditingRow(item)" [class.k-grid-add-row]="item.isNew" [attr.aria-selected]="ariaRowSelected(item)" [class.k-selected]="item.selected" [attr.data-treelist-view-index]="rowIndex"> @for (column of columns; track trackByColumns($index, column); let columnIndex = $index) { <td kendoTreeListCell [columnIndex]="lockedColumnsCount + columnIndex" [column]="column" [viewItem]="item" [dataItem]="item.data" [level]="item.level" [hasChildren]="item.hasChildren" [isExpanded]="item.expanded" [rowIndex]="item.rowIndex" [loading]="item.loading" [isNew]="item.isNew" [selected]="item.selected" [expandIcons]="expandIcons" kendoTreeListLogicalCell [logicalRowIndex]="logicalRowIndex(item.rowIndex)" [logicalColIndex]="logicalColIndex(column)" [dataRowIndex]="$any(item).index" [column]="column" [colIndex]="columnIndex" [colSpan]="column.colspan" [expandable]="cellExpandable(item, column)" [attr.aria-expanded]="lockedColumnsCount < 1 ? ariaExpanded(item, column) : undefined" [attr.aria-selected]="lockedColumnsCount < 1 ? ariaSelected(item, column, lockedColumnsCount + columnIndex) : undefined" class="k-table-td" [attr.role]="column.tableCellsRole" [ngClass]="column.cssClass" [class.k-grid-edit-cell]="isEditingCell(item, column)" [class.k-selected]="isCellSelected(item.data, column, lockedColumnsCount + columnIndex)" [ngStyle]="column.style" [attr.colspan]="column.colspan" > </td> } </tr> } @if (item.type === 'footer' && hasFooter) { <tr role="row" class="k-footer" [attr.data-treelist-view-index]="rowIndex" kendoTreeListLogicalRow [logicalRowIndex]="logicalRowIndex(item.rowIndex)" [logicalSlaveRow]="lockedColumnsCount > 0" [logicalCellsCount]="columns.length" [logicalSlaveCellsCount]="unlockedColumnsCount(item)" [totalColumns]="totalColumns"> @for (column of footerColumns; track trackByColumns($index, column); let columnIndex = $index) { <td kendoTreeListLogicalCell [logicalRowIndex]="logicalRowIndex(item.rowIndex)" [logicalColIndex]="logicalColIndex(column)" [column]="column" [colIndex]="columnIndex" class="k-table-td" [attr.role]="column.tableCellsRole" [ngClass]="column.footerClass" [ngStyle]="column.footerStyle" > @if ($any(column).expandable) { @for (item of item.level | levelItems; track $index) { <kendo-icon-wrapper name="none" innerCssClass="k-treelist-toggle" [svgIcon]="noneIcon"></kendo-icon-wrapper> } } <ng-container [ngTemplateOutlet]="column.footerTemplateRef" [ngTemplateOutletContext]="{ $implicit: item.aggregates, aggregates: item.aggregates, column: column, columnIndex: columnIndex, field: $any(column).field, items: item.items, parentItem: item.parentItem }"> </ng-container> </td> } </tr> } } `, isInline: true, dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: LogicalRowDirective, selector: "[kendoTreeListLogicalRow]", inputs: ["logicalRowIndex", "logicalSlaveRow", "logicalCellsCount", "logicalSlaveCellsCount", "dataRowIndex", "dataItem", "isNew", "totalColumns"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: CellComponent, selector: "[kendoTreeListCell]", inputs: ["column", "columnIndex", "isNew", "level", "hasChildren", "isExpanded", "loading", "expandIcons", "rowIndex", "selected", "dataItem", "viewItem"] }, { kind: "directive", type: LogicalCellDirective, selector: "[kendoTreeListLogicalCell]", inputs: ["logicalColIndex", "logicalRowIndex", "logicalSlaveCell", "column", "colIndex", "colSpan", "rowSpan", "dataRowIndex", "dataItem", "expandable", "headerLabelText"] }, { kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: IconWrapperComponent, selector: "kendo-icon-wrapper", inputs: ["name", "svgIcon", "innerCssClass", "customFontClass", "size"], exportAs: ["kendoIconWrapper"] }, { kind: "pipe", type: LevelItemsPipe, name: "levelItems" }] }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: TableBodyComponent, decorators: [{ type: Component, args: [{ selector: '[kendoTreeListTableBody]', template: ` @if (!hasData) { <tr class="k-grid-norecords"> <td [attr.colspan]="colSpan" class="k-table-td"> @if (noRecordsTemplate?.templateRef) { <ng-container [ngTemplateOutlet]="noRecordsTemplate.templateRef"> </ng-container> } @if (!noRecordsTemplate?.templateRef) { {{noRecordsText}} } </td> </tr> } @for (item of view?.data; track trackByWrapper($index, item); let rowIndex = $index) { @if (item.type === 'data') { <tr kendoTreeListLogicalRow [dataRowIndex]="$any(item).index" [dataItem]="item.data" [logicalRowIndex]="logicalRowIndex(item.rowIndex)" [logicalSlaveRow]="lockedColumnsCount > 0" [logicalCellsCount]="columns.length" [logicalSlaveCellsCount]="unlockedColumnsCount(item)" [totalColumns]="totalColumns" [isNew]="item.isNew" [attr.aria-expanded]="ariaRowExpanded(item)" [ngClass]="rowClass({ dataItem: item.data, index: $any(item).index })" class="k-table-row{{isOdd(item) ? ' k-table-alt-row' : ''}}" [class.k-grid-edit-row]="isEditingRow(item)" [class.k-grid-add-row]="item.isNew" [attr.aria-selected]="ariaRowSelected(item)" [class.k-selected]="item.selected" [attr.data-treelist-view-index]="rowIndex"> @for (column of columns; track trackByColumns($index, column); let columnIndex = $index) { <td kendoTreeListCell [columnIndex]="lockedColumnsCount + columnIndex" [column]="column" [viewItem]="item" [dataItem]="item.data" [level]="item.level" [hasChildren]="item.hasChildren" [isExpanded]="item.expanded" [rowIndex]="item.rowIndex" [loading]="item.loading" [isNew]="item.isNew" [selected]="item.selected" [expandIcons]="expandIcons" kendoTreeListLogicalCell [logicalRowIndex]="logicalRowIndex(item.rowIndex)" [logicalColIndex]="logicalColIndex(column)" [dataRowIndex]="$any(item).index" [column]="column" [colIndex]="columnIndex" [colSpan]="column.colspan" [expandable]="cellExpandable(item, column)" [attr.aria-expanded]="lockedColumnsCount < 1 ? ariaExpanded(item, column) : undefined" [attr.aria-selected]="lockedColumnsCount < 1 ? ariaSelected(item, column, lockedColumnsCount + columnIndex) : undefined" class="k-table-td" [attr.role]="column.tableCellsRole" [ngClass]="column.cssClass" [class.k-grid-edit-cell]="isEditingCell(item, column)" [class.k-selected]="isCellSelected(item.data, column, lockedColumnsCount + columnIndex)" [ngStyle]="column.style" [attr.colspan]="column.colspan" > </td> } </tr> } @if (item.type === 'footer' && hasFooter) { <tr role="row" class="k-footer" [attr.data-treelist-view-index]="rowIndex" kendoTreeListLogicalRow [logicalRowIndex]="logicalRowIndex(item.rowIndex)" [logicalSlaveRow]="lockedColumnsCount > 0" [logicalCellsCount]="columns.length" [logicalSlaveCellsCount]="unlockedColumnsCount(item)" [totalColumns]="totalColumns"> @for (column of footerColumns; track trackByColumns($index, column); let columnIndex = $index) { <td kendoTreeListLogicalCell [logicalRowIndex]="logicalRowIndex(item.rowIndex)" [logicalColIndex]="logicalColIndex(column)" [column]="column" [colIndex]="columnIndex" class="k-table-td" [attr.role]="column.tableCellsRole" [ngClass]="column.footerClass" [ngStyle]="column.footerStyle" > @if ($any(column).expandable) { @for (item of item.level | levelItems; track $index) { <kendo-icon-wrapper name="none" innerCssClass="k-treelist-toggle" [svgIcon]="noneIcon"></kendo-icon-wrapper> } } <ng-container [ngTemplateOutlet]="column.footerTemplateRef" [ngTemplateOutletContext]="{ $implicit: item.aggregates, aggregates: item.aggregates, column: column, columnIndex: columnIndex, field: $any(column).field, items: item.items, parentItem: item.parentItem }"> </ng-container> </td> } </tr> } } `, standalone: true, imports: [NgTemplateOutlet, LogicalRowDirective, NgClass, CellComponent, LogicalCellDirective, NgStyle, IconWrapperComponent, LevelItemsPipe] }] }], ctorParameters: () => [{ type: i1.ChangeNotificationService }, { type: i2.EditService }, { type: i3.LocalizationService }, { type: i0.NgZone }, { type: i0.Renderer2 }, { type: i0.ElementRef }, { type: i4.DomEventsService }, { type: i5.ColumnInfoService }, { type: i6.NavigationService }, { type: i7.ExpandStateService }, { type: i8.SelectionService }], propDecorators: { hostClass: [{ type: HostBinding, args: ['class.k-table-tbody'] }], columns: [{ type: Input }], allColumns: [{ type: Input }], noRecordsTemplate: [{ type: Input }], view: [{ type: Input }], skip: [{ type: Input }], filterable: [{ type: Input }], noRecordsText: [{ type: Input }], isLocked: [{ type: Input }], lockedColumnsCount: [{ type: Input }], totalColumnsCount: [{ type: Input }], virtualColumns: [{ type: Input }], expandIcons: [{ type: Input }], trackBy: [{ type: Input }], totalColumns: [{ type: Input }], rowClass: [{ type: Input }] } });