UNPKG

ng-devui

Version:

DevUI components based on Angular

673 lines (662 loc) 494 kB
import * as i0 from '@angular/core'; import { InjectionToken, Directive, TemplateRef, Component, ChangeDetectionStrategy, ContentChild, EventEmitter, Input, Output, Pipe, Inject, ViewChild, HostBinding, forwardRef, HostListener, ViewChildren, ContentChildren, ElementRef, Injectable, NgModule } from '@angular/core'; import * as i1$1 from '@angular/common'; import { DOCUMENT, CommonModule } from '@angular/common'; import * as i2 from '@angular/cdk/scrolling'; import { ScrollingModule } from '@angular/cdk/scrolling'; import * as i5$1 from 'ng-devui/dragdrop'; import { DragDropModule } from 'ng-devui/dragdrop'; import * as i3 from '@angular/forms'; import { FormsModule } from '@angular/forms'; import * as i3$1 from 'ng-devui/checkbox'; import { CheckBoxModule } from 'ng-devui/checkbox'; import * as i4$2 from 'ng-devui/tooltip'; import { TooltipModule } from 'ng-devui/tooltip'; import * as i1 from 'ng-devui/i18n'; import { I18nFormat } from 'ng-devui/i18n'; import { InputNumberComponent, InputNumberModule } from 'ng-devui/input-number'; import { SelectComponent, SelectModule } from 'ng-devui/select'; import { TreeSelectComponent, TreeSelectModule } from 'ng-devui/tree-select'; import * as i4$1 from 'ng-devui/utils'; import { formatDate, stopPropagationIfExist, SafePipeModule, LazyLoadModule } from 'ng-devui/utils'; import { Subject, fromEvent, of, BehaviorSubject, merge } from 'rxjs'; import { takeUntil, switchMap, tap } from 'rxjs/operators'; import * as i4 from 'ng-devui/common'; import { DCommonModule } from 'ng-devui/common'; import * as i5 from 'ng-devui/datepicker'; import { DatepickerModule } from 'ng-devui/datepicker'; import * as i6 from 'ng-devui/button'; import { ButtonModule } from 'ng-devui/button'; import * as i7 from 'ng-devui/dropdown'; import { DropDownModule } from 'ng-devui/dropdown'; import * as i8 from 'ng-devui/search'; import { SearchModule } from 'ng-devui/search'; import * as i6$1 from 'ng-devui/popover'; import { PopoverModule } from 'ng-devui/popover'; import { BadgeModule } from 'ng-devui/badge'; import { PaginationModule } from 'ng-devui/pagination'; import * as i6$2 from 'ng-devui/toggle'; import { ToggleModule } from 'ng-devui/toggle'; import { WindowRefModule } from 'ng-devui/window-ref'; import * as i1$2 from 'ng-devui/modal'; import { cloneDeep } from 'lodash-es'; const DATA_TABLE = new InjectionToken('DATA_TABLE'); const DATA_TABLE_ROW = new InjectionToken('DATA_TABLE_ROW'); var ForceUpdateReason; (function (ForceUpdateReason) { ForceUpdateReason[ForceUpdateReason["RowUpdate"] = 0] = "RowUpdate"; })(ForceUpdateReason || (ForceUpdateReason = {})); class EditorDirective { constructor(viewContainerRef) { this.viewContainerRef = viewContainerRef; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: EditorDirective, deps: [{ token: i0.ViewContainerRef }], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.13", type: EditorDirective, selector: "[dEditorHost]", ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: EditorDirective, decorators: [{ type: Directive, args: [{ selector: '[dEditorHost]', }] }], ctorParameters: () => [{ type: i0.ViewContainerRef }] }); class DataTableCellEditTmplComponent { static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DataTableCellEditTmplComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: DataTableCellEditTmplComponent, selector: "d-cell-edit", queries: [{ propertyName: "template", first: true, predicate: TemplateRef, descendants: true }], ngImport: i0, template: '', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DataTableCellEditTmplComponent, decorators: [{ type: Component, args: [{ selector: 'd-cell-edit', template: '', changeDetection: ChangeDetectionStrategy.OnPush }] }], propDecorators: { template: [{ type: ContentChild, args: [TemplateRef] }] } }); class DataTableCellViewTmplComponent { static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DataTableCellViewTmplComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: DataTableCellViewTmplComponent, selector: "d-cell", queries: [{ propertyName: "template", first: true, predicate: TemplateRef, descendants: true }], ngImport: i0, template: '', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DataTableCellViewTmplComponent, decorators: [{ type: Component, args: [{ selector: 'd-cell', template: '', changeDetection: ChangeDetectionStrategy.OnPush }] }], propDecorators: { template: [{ type: ContentChild, args: [TemplateRef] }] } }); class DataTableHeadCellTmplComponent { static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DataTableHeadCellTmplComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: DataTableHeadCellTmplComponent, selector: "d-head-cell", queries: [{ propertyName: "template", first: true, predicate: TemplateRef, descendants: true }], ngImport: i0, template: '', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DataTableHeadCellTmplComponent, decorators: [{ type: Component, args: [{ selector: 'd-head-cell', template: '', changeDetection: ChangeDetectionStrategy.OnPush }] }], propDecorators: { template: [{ type: ContentChild, args: [TemplateRef] }] } }); class DataTableColumnTmplComponent { constructor() { this.headCellApplyAll = false; this.nestedColumnIndent = 16; this.order = Number.MAX_VALUE; this.nestedColumn = false; this.filterChange = new EventEmitter(); this.filterToggle = new EventEmitter(); this.filterMultiple = true; this.orderChange = new EventEmitter(); this.widthChange = new EventEmitter(); // 鼠标是否移动到当前表头单元格 this.selected = false; // @deprecated this.fieldType = 'text'; } ngOnChanges(changes) { const { order, width } = changes; if (order) { this.orderChange.emit(order.currentValue); } if (width) { this.widthChange.emit(width.currentValue); } } ngOnDestroy() { this.orderChange.unsubscribe(); } emitFilterData(filterData) { this.filterChange.emit(filterData); } emitFilterToggle(data) { this.filterToggle.emit(data); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DataTableColumnTmplComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: DataTableColumnTmplComponent, selector: "d-column", inputs: { advancedHeader: "advancedHeader", headCellApplyAll: "headCellApplyAll", maxWidth: "maxWidth", minWidth: "minWidth", field: "field", header: "header", sortable: "sortable", editable: "editable", filterable: "filterable", closeFilterWhenScroll: "closeFilterWhenScroll", cellClass: "cellClass", nestedColumnIndent: "nestedColumnIndent", width: "width", fixedLeft: "fixedLeft", fixedRight: "fixedRight", extraOptions: "extraOptions", order: "order", nestedColumn: "nestedColumn", filterList: "filterList", filterMultiple: "filterMultiple", filterIconActive: "filterIconActive", filterBoxWidth: "filterBoxWidth", filterBoxHeight: "filterBoxHeight", beforeFilter: "beforeFilter", customFilterTemplate: "customFilterTemplate", extraFilterTemplate: "extraFilterTemplate", searchFn: "searchFn", fieldType: "fieldType" }, outputs: { filterChange: "filterChange", filterToggle: "filterToggle" }, queries: [{ propertyName: "cellCmp", first: true, predicate: DataTableCellViewTmplComponent, descendants: true }, { propertyName: "cellEditCmp", first: true, predicate: DataTableCellEditTmplComponent, descendants: true }, { propertyName: "headCellTmpl", first: true, predicate: DataTableHeadCellTmplComponent, descendants: true }], usesOnChanges: true, ngImport: i0, template: '', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DataTableColumnTmplComponent, decorators: [{ type: Component, args: [{ selector: 'd-column', template: '', changeDetection: ChangeDetectionStrategy.OnPush, }] }], propDecorators: { advancedHeader: [{ type: Input }], headCellApplyAll: [{ type: Input }], maxWidth: [{ type: Input }], minWidth: [{ type: Input }], field: [{ type: Input }], header: [{ type: Input }], sortable: [{ type: Input }], editable: [{ type: Input }], filterable: [{ type: Input }], closeFilterWhenScroll: [{ type: Input }], cellClass: [{ type: Input }], nestedColumnIndent: [{ type: Input }], width: [{ type: Input }], fixedLeft: [{ type: Input }], fixedRight: [{ type: Input }], extraOptions: [{ type: Input }], order: [{ type: Input }], nestedColumn: [{ type: Input }], filterList: [{ type: Input }], filterChange: [{ type: Output }], filterToggle: [{ type: Output }], filterMultiple: [{ type: Input }], filterIconActive: [{ type: Input }], filterBoxWidth: [{ type: Input }], filterBoxHeight: [{ type: Input }], beforeFilter: [{ type: Input }], cellCmp: [{ type: ContentChild, args: [DataTableCellViewTmplComponent] }], cellEditCmp: [{ type: ContentChild, args: [DataTableCellEditTmplComponent] }], headCellTmpl: [{ type: ContentChild, args: [DataTableHeadCellTmplComponent] }], customFilterTemplate: [{ type: Input }], extraFilterTemplate: [{ type: Input }], searchFn: [{ type: Input }], fieldType: [{ type: Input }] } }); class DisPlayCellValuePipe { constructor() { this.datePicker = { format: { date: 'y-MM-dd', time: 'y-MM-dd HH:mm', }, }; } transform(rowItem, rowIndex, column, lang) { if (!column || !column.field) { return null; } if (column.field === '$index') { return rowIndex + 1; } if (lang) { const format = I18nFormat.localFormat[lang]; this.datePicker.format.date = format.short; this.datePicker.format.time = format.medinum; } const cellItem = rowItem[column.field]; switch (column.fieldType) { case 'date': { let pattern; if (column.extraOptions && column.extraOptions.dateFormat) { pattern = column.extraOptions.dateFormat; } else { pattern = column.extraOptions && column.extraOptions.showTime ? this.datePicker.format.time : this.datePicker.format.date; } return cellItem ? formatDate(new Date(cellItem), pattern) : ''; } case 'select': case 'treeSelect': if (column.extraOptions && column.extraOptions.multiple) { if (cellItem && cellItem.length > 0) { let displayValue; if (column.extraOptions.filterKey) { displayValue = cellItem.map((item) => item[column.extraOptions.filterKey]).join(';'); } else { displayValue = cellItem.join(';'); } return displayValue; } else { return '--'; } } else { let displayValue; if (column.extraOptions && column.extraOptions.filterKey) { displayValue = cellItem ? cellItem[column.extraOptions.filterKey] : cellItem; } else { displayValue = cellItem; } return displayValue === 0 ? 0 : displayValue || '--'; } default: if (cellItem === null || cellItem === undefined || cellItem === '') { return '--'; } else { return cellItem; } } } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DisPlayCellValuePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); } static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "18.2.13", ngImport: i0, type: DisPlayCellValuePipe, name: "disPlayCellValuePipe" }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DisPlayCellValuePipe, decorators: [{ type: Pipe, args: [{ name: 'disPlayCellValuePipe' }] }] }); class DataTableCellComponent { get isEditable() { return this.column.editable && !this.isCellEdit && this.column.extraOptions?.editableTip !== 'btn' && !this.fieldEditDenied; } constructor(dt, rowComponent, i18n, changeDetectorRef, componentFactoryResolver, cellRef, ngZone) { this.dt = dt; this.rowComponent = rowComponent; this.i18n = i18n; this.changeDetectorRef = changeDetectorRef; this.componentFactoryResolver = componentFactoryResolver; this.cellRef = cellRef; this.ngZone = ngZone; this.clickCount = 0; // 记录点击次数 this.unsubscribe$ = new Subject(); } ngOnInit() { this.forceUpdateSubscription = this.rowComponent.forceUpdateEvent.subscribe(() => this.forceUpdate()); if (this.column.editable) { this.tdMousedownSubscription = fromEvent(this.cellRef.nativeElement, 'mousedown').subscribe(() => { this.clickInTd = true; }); this.tdMouseupSubscription = fromEvent(this.cellRef.nativeElement, 'mouseup').subscribe(() => { this.clickInTd = false; }); } this.ngZone.runOutsideAngular(() => { this.cellRef.nativeElement.addEventListener('mouseup', this.onCellClick.bind(this)); this.cellRef.nativeElement.addEventListener('dblclick', this.onCellDBClick.bind(this)); }); this.lang = I18nFormat.getLocaleAndLang().currentLanguage; this.i18n .langChange() .pipe(takeUntil(this.unsubscribe$)) .subscribe(() => { this.lang = I18nFormat.getLocaleAndLang().currentLanguage; }); } ngOnChanges(changes) { const rowItem = changes.rowItem; if (rowItem) { this.updateEditable(rowItem); } } updateEditable(rowItem) { const currentConfig = rowItem.currentValue.$editDeniedConfig; if (!currentConfig) { if (this.fieldEditDenied) { this.fieldEditDenied = false; } } else { const index = currentConfig.findIndex((config) => { return config === this.column.field; }); if (index === -1) { if (this.fieldEditDenied) { this.fieldEditDenied = false; } } else { if (!this.fieldEditDenied) { this.fieldEditDenied = true; } } } } onCellClick($event) { const cellSelectedEventArg = { rowIndex: this.rowIndex, colIndex: this.colIndex, column: this.column, rowItem: this.rowItem, cellComponent: this, rowComponent: this.rowComponent, event: $event, }; this.clickCount++; if (this.clickCount === 1) { this.timeoutId = setTimeout(() => { if (this.clickCount === 1) { this.dt.onCellClick(cellSelectedEventArg); } this.clickCount = 0; clearTimeout(this.timeoutId); }, this.timeout); } } onCellDBClick($event) { const cellSelectedEventArg = { rowIndex: this.rowIndex, colIndex: this.colIndex, column: this.column, rowItem: this.rowItem, cellComponent: this, rowComponent: this.rowComponent, }; this.dt.onCellDBClick(cellSelectedEventArg); } forceUpdate() { this.changeDetectorRef.markForCheck(); } finishCellEdit($event) { if (this.editModel !== 'cell') { return; } this.isCellEdit = false; this.editorHost.viewContainerRef.clear(); this.dynamicEditorActive = false; this.templateEditorActive = false; if (this.documentClickSubscription) { this.unSubscription(this.documentClickSubscription); } if (this.cellEditorClickSubscription) { this.unSubscription(this.cellEditorClickSubscription); } stopPropagationIfExist($event); this.dt.onCellEditEnd({ rowIndex: this.rowIndex, colIndex: this.colIndex, column: this.column, rowItem: this.rowItem, cellComponent: this, rowComponent: this.rowComponent, }); } isCellEditEnable(column, rowItem) { if (this.editModel === 'cell') { return this.isCellEdit; } return this.isEditRow; } ngOnDestroy() { if (this.forceUpdateSubscription) { this.unSubscription(this.forceUpdateSubscription); } if (this.documentClickSubscription) { this.unSubscription(this.documentClickSubscription); } if (this.cellEditorClickSubscription) { this.unSubscription(this.cellEditorClickSubscription); } if (this.cellActionSubscription) { this.unSubscription(this.cellActionSubscription); } if (this.tdMousedownSubscription) { this.unSubscription(this.tdMousedownSubscription); } if (this.tdMouseupSubscription) { this.unSubscription(this.tdMouseupSubscription); } if (this.unsubscribe$) { this.unsubscribe$.unsubscribe(); } } unSubscription(sbscription) { if (sbscription) { sbscription.unsubscribe(); /* eslint-disable-next-line no-param-reassign */ sbscription = null; } } creatCellEditor() { let componentFactory; let editorComponent; switch (this.column.fieldType) { case 'number': editorComponent = InputNumberComponent; break; case 'select': editorComponent = SelectComponent; break; case 'treeSelect': editorComponent = TreeSelectComponent; break; default: this.templateEditorActive = true; } if (editorComponent) { componentFactory = this.componentFactoryResolver.resolveComponentFactory(editorComponent); const viewContainerRef = this.editorHost.viewContainerRef; viewContainerRef.clear(); const componentRef = viewContainerRef.createComponent(componentFactory); const componentInstance = componentRef.instance; if (this.column.extraOptions) { componentFactory.inputs.forEach((input) => { if (this.column.extraOptions[input.templateName]) { componentInstance[input.propName] = this.column.extraOptions[input.templateName]; } }); } componentInstance.writeValue(this.rowItem[this.column.field]); componentInstance.registerOnChange((value) => { this.rowItem[this.column.field] = value; if (this.column.extraOptions && this.column.extraOptions.finishEditingAfterValueChange) { this.finishCellEdit(); } }); this.dynamicEditorActive = true; } } cellEditing($event) { $event.stopPropagation(); $event.preventDefault(); let beforeEdit = Promise.resolve(undefined); if (this.dt.beforeCellEdit) { beforeEdit = this.dt.beforeCellEdit(this.rowItem, this.column); } beforeEdit.then((extraOptions) => { if (extraOptions) { this.column.extraOptions = extraOptions; } this.dt.cellEditorClickEvent.emit($event); const cellSelectedEventArg = { rowIndex: this.rowIndex, colIndex: this.colIndex, column: this.column, rowItem: this.rowItem, cellComponent: this, rowComponent: this.rowComponent, }; if (this.column.editable && this.editModel === 'cell') { this.isCellEdit = true; this.creatCellEditor(); this.documentClickSubscription = this.dt.documentClickEvent.subscribe((event) => { const containTarget = !this.cellRef.nativeElement.contains(event.target) && !this.clickInTd; const flag = this.dt.beforeCellEditEnd && containTarget ? this.dt.beforeCellEditEnd(this.rowItem, this.column) : true; if ((event === 'cancel' || containTarget) && flag) { this.ngZone.run(() => { this.finishCellEdit(); }); } this.clickInTd = false; }); this.cellEditorClickSubscription = this.dt.cellEditorClickEvent.subscribe((event) => { const containTarget = this.cellRef.nativeElement.contains(event.target); const flag = this.dt.beforeCellEditEnd && !containTarget ? this.dt.beforeCellEditEnd(this.rowItem, this.column) : true; if (!containTarget && flag) { this.finishCellEdit(); } }); this.dt.onCellEditStart(cellSelectedEventArg); } }); } toggleChildTable(rowItem) { rowItem.$isChildTableOpen = !rowItem.$isChildTableOpen; this.dt.setRowChildToggleStatus(rowItem, rowItem.$isChildTableOpen); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DataTableCellComponent, deps: [{ token: DATA_TABLE }, { token: DATA_TABLE_ROW }, { token: i1.I18nService }, { token: i0.ChangeDetectorRef }, { token: i0.ComponentFactoryResolver }, { token: i0.ElementRef }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: DataTableCellComponent, selector: "d-data-table-cell,[dDataTableCell]", inputs: { rowIndex: "rowIndex", colIndex: "colIndex", column: "column", rowItem: "rowItem", editModel: "editModel", isEditRow: "isEditRow", timeout: "timeout", tableLevel: "tableLevel" }, host: { properties: { "class.editable-cell": "this.isEditable" } }, viewQueries: [{ propertyName: "editorHost", first: true, predicate: EditorDirective, descendants: true }], usesOnChanges: true, ngImport: i0, template: "<ng-template *ngIf=\"!column.editable || fieldEditDenied\" [ngTemplateOutlet]=\"cellViewTemp\"></ng-template>\n\n<ng-container *ngIf=\"column.editable && !fieldEditDenied\">\n <div *ngIf=\"!isCellEdit && column.extraOptions?.editableTip === 'btn'\" class=\"cell-container\">\n <div class=\"cell-container-inner not-break modify-holder\">\n <ng-template [ngTemplateOutlet]=\"cellViewTemp\"></ng-template>\n <span class=\"cell-modify\" (click)=\"cellEditing($event)\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\" width=\"16px\" height=\"16px\" version=\"1.1\">\n <g fill=\"none\" fill-rule=\"evenodd\" stroke=\"none\" stroke-width=\"1\">\n <path\n d=\"M 9.48528 3.41421 L 10.8995 2 L 13.7279 4.82843 L 12.3137 6.24264 L 9.48528 3.41421 Z M 5.24264 13.3137 L 2.41421 13.3137 L 2.41421 10.4853 L 8.77817 4.12132 L 11.6066 6.94975 L 5.24264 13.3137 Z\"\n />\n </g>\n </svg>\n </span>\n </div>\n </div>\n <div *ngIf=\"!isCellEdit && column.extraOptions?.editableTip !== 'btn'\" class=\"cell-editable\" (click)=\"cellEditing($event)\">\n <ng-template [ngTemplateOutlet]=\"cellViewTemp\"></ng-template>\n </div>\n <div [ngClass]=\"{ 'edit-padding-fix': dynamicEditorActive }\">\n <ng-template dEditorHost></ng-template>\n </div>\n <ng-template\n *ngIf=\"templateEditorActive\"\n [ngTemplateOutlet]=\"column.cellEditCmp ? column.cellEditCmp.template : column.fieldType === 'date' ? dateEditTempate : textEditTempate\"\n [ngTemplateOutletContext]=\"{\n rowIndex: rowIndex,\n colIndex: colIndex,\n column: column,\n rowItem: rowItem,\n $implicit: this,\n row: rowComponent\n }\"\n >\n </ng-template>\n</ng-container>\n\n<ng-template #cellViewTemp>\n <ng-container *ngIf=\"column.nestedColumn\">\n <span *ngIf=\"tableLevel > 0\" class=\"cell-placeholder\" [ngStyle]=\"{ width: tableLevel * column.nestedColumnIndent + 'px' }\"></span>\n <span [style.visibility]=\"rowItem.children ? 'visible' : 'hidden'\" class=\"childtable-toggler\" (click)=\"toggleChildTable(rowItem)\">\n <ng-container *ngIf=\"rowItem['$isChildTableOpen']\">\n <svg\n *ngIf=\"!column.extraOptions?.iconFoldTable\"\n width=\"16px\"\n height=\"16px\"\n viewBox=\"0 0 16 16\"\n version=\"1.1\"\n xmlns=\"http://www.w3.org/2000/svg\"\n xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n class=\"svg-icon svg-icon-close\"\n >\n <g stroke=\"none\" stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\">\n <rect x=\"0.5\" y=\"0.5\" width=\"15\" height=\"15\" rx=\"2\"></rect>\n <rect x=\"4\" y=\"7\" width=\"8\" height=\"2\"></rect>\n </g>\n </svg>\n\n <span *ngIf=\"column.extraOptions?.iconFoldTable\" [innerHTML]=\"column.extraOptions?.iconFoldTable | safe : 'html'\"></span>\n </ng-container>\n <ng-container *ngIf=\"!rowItem['$isChildTableOpen']\">\n <svg\n *ngIf=\"!column.extraOptions?.iconUnFoldTable\"\n width=\"16px\"\n height=\"16px\"\n viewBox=\"0 0 16 16\"\n version=\"1.1\"\n xmlns=\"http://www.w3.org/2000/svg\"\n xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n class=\"svg-icon svg-icon-open-folder\"\n >\n <g stroke=\"none\" stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\">\n <rect x=\"0.5\" y=\"0.5\" width=\"15\" height=\"15\" rx=\"2\"></rect>\n <path\n d=\"M8.75,4 L8.75,7.25 L12,7.25 L12,8.75 L8.749,8.75 L8.75,12 L7.25,12 L7.249,8.75 L4,8.75 L4,7.25 L7.25,7.25 L7.25,4 L8.75,4 Z\"\n ></path>\n </g>\n </svg>\n <span *ngIf=\"column.extraOptions?.iconUnFoldTable\" [innerHTML]=\"column.extraOptions?.iconUnFoldTable | safe : 'html'\"></span>\n </ng-container>\n </span>\n </ng-container>\n <ng-template\n [ngTemplateOutlet]=\"column.cellCmp ? column.cellCmp.template : textViewTemplate\"\n [ngTemplateOutletContext]=\"{\n rowIndex: rowIndex,\n colIndex: colIndex,\n column: column,\n rowItem: rowItem,\n cellItem: rowItem[column.field],\n cellValue: rowItem | disPlayCellValuePipe : rowIndex : column : lang,\n $implicit: this,\n row: rowComponent,\n tableLevel: tableLevel\n }\"\n >\n </ng-template>\n</ng-template>\n\n<ng-template let-cellValue=\"cellValue\" #textViewTemplate>\n <span [attr.title]=\"cellValue\">{{ cellValue }}</span>\n</ng-template>\n\n<ng-template let-rowIndex=\"rowIndex\" let-colIndex=\"colIndex\" let-rowItem=\"rowItem\" let-column=\"column\" let-cell=\"\" #textEditTempate>\n <form class=\"form-inline edit-padding-fix\">\n <div class=\"form-group full-width\">\n <div class=\"input-group full-width\">\n <input\n [dAutoFocus]=\"cell && cell.isCellEdit\"\n [type]=\"column.fieldType\"\n class=\"devui-form-control\"\n [name]=\"column.field\"\n [(ngModel)]=\"rowItem[column.field]\"\n [attr.maxlength]=\"column.extraOptions?.maxlength\"\n [attr.minlength]=\"column.extraOptions?.minlength\"\n [attr.pattern]=\"column.extraOptions?.pattern\"\n (keydown.enter)=\"cell && cell.finishCellEdit($event)\"\n [placeholder]=\"column.placeholder || ''\"\n />\n </div>\n </div>\n </form>\n</ng-template>\n\n<ng-template let-rowIndex=\"rowIndex\" let-colIndex=\"colIndex\" let-rowItem=\"rowItem\" let-column=\"column\" let-cell=\"\" #dateEditTempate>\n <form class=\"form-inline edit-padding-fix\">\n <div class=\"devui-form-group\">\n <div class=\"devui-input-group devui-dropdown-origin\">\n <input\n class=\"devui-form-control search\"\n [name]=\"column.field\"\n [(ngModel)]=\"rowItem[column.field]\"\n dDatepicker\n appendToBody\n [dateFormat]=\"column.extraOptions?.dateFormat\"\n #datePicker=\"datepicker\"\n (ngModelChange)=\"!column.extraOptions?.showTime && cell && cell.finishCellEdit()\"\n [maxDate]=\"column.extraOptions?.maxDate\"\n [minDate]=\"column.extraOptions?.minDate\"\n [cssClass]=\"column.extraOptions?.cssClass\"\n [dateConfig]=\"column.extraOptions?.dateConfig\"\n [locale]=\"column.extraOptions?.locale\"\n [customViewTemplate]=\"column.extraOptions?.customViewTemplate\"\n [disabled]=\"column.extraOptions?.disabled\"\n [placeholder]=\"column.placeholder || ''\"\n [autoOpen]=\"true\"\n [showTime]=\"column.extraOptions?.showTime\"\n />\n <div class=\"devui-input-group-addon\" (click)=\"datePicker.toggle()\">\n <i class=\"icon icon-calendar\"></i>\n </div>\n </div>\n </div>\n </form>\n</ng-template>\n", dependencies: [{ kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i1$1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i3.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i4.AutoFocusDirective, selector: "[dAutoFocus]", inputs: ["dAutoFocus"] }, { kind: "component", type: i5.DatePickerAppendToBodyComponent, selector: "[dDatepicker][appendToBody]", inputs: ["appendToBodyDirections", "locale", "cssClass", "disabled", "dateConverter", "customViewTemplate", "autoOpen", "showAnimation", "showTime", "dateConfig", "dateFormat", "maxDate", "minDate"], outputs: ["selectedDateChange"], exportAs: ["datepicker"] }, { kind: "directive", type: EditorDirective, selector: "[dEditorHost]" }, { kind: "pipe", type: i4$1.SafePipe, name: "safe" }, { kind: "pipe", type: DisPlayCellValuePipe, name: "disPlayCellValuePipe" }] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DataTableCellComponent, decorators: [{ type: Component, args: [{ selector: 'd-data-table-cell,[dDataTableCell]', preserveWhitespaces: false, template: "<ng-template *ngIf=\"!column.editable || fieldEditDenied\" [ngTemplateOutlet]=\"cellViewTemp\"></ng-template>\n\n<ng-container *ngIf=\"column.editable && !fieldEditDenied\">\n <div *ngIf=\"!isCellEdit && column.extraOptions?.editableTip === 'btn'\" class=\"cell-container\">\n <div class=\"cell-container-inner not-break modify-holder\">\n <ng-template [ngTemplateOutlet]=\"cellViewTemp\"></ng-template>\n <span class=\"cell-modify\" (click)=\"cellEditing($event)\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\" width=\"16px\" height=\"16px\" version=\"1.1\">\n <g fill=\"none\" fill-rule=\"evenodd\" stroke=\"none\" stroke-width=\"1\">\n <path\n d=\"M 9.48528 3.41421 L 10.8995 2 L 13.7279 4.82843 L 12.3137 6.24264 L 9.48528 3.41421 Z M 5.24264 13.3137 L 2.41421 13.3137 L 2.41421 10.4853 L 8.77817 4.12132 L 11.6066 6.94975 L 5.24264 13.3137 Z\"\n />\n </g>\n </svg>\n </span>\n </div>\n </div>\n <div *ngIf=\"!isCellEdit && column.extraOptions?.editableTip !== 'btn'\" class=\"cell-editable\" (click)=\"cellEditing($event)\">\n <ng-template [ngTemplateOutlet]=\"cellViewTemp\"></ng-template>\n </div>\n <div [ngClass]=\"{ 'edit-padding-fix': dynamicEditorActive }\">\n <ng-template dEditorHost></ng-template>\n </div>\n <ng-template\n *ngIf=\"templateEditorActive\"\n [ngTemplateOutlet]=\"column.cellEditCmp ? column.cellEditCmp.template : column.fieldType === 'date' ? dateEditTempate : textEditTempate\"\n [ngTemplateOutletContext]=\"{\n rowIndex: rowIndex,\n colIndex: colIndex,\n column: column,\n rowItem: rowItem,\n $implicit: this,\n row: rowComponent\n }\"\n >\n </ng-template>\n</ng-container>\n\n<ng-template #cellViewTemp>\n <ng-container *ngIf=\"column.nestedColumn\">\n <span *ngIf=\"tableLevel > 0\" class=\"cell-placeholder\" [ngStyle]=\"{ width: tableLevel * column.nestedColumnIndent + 'px' }\"></span>\n <span [style.visibility]=\"rowItem.children ? 'visible' : 'hidden'\" class=\"childtable-toggler\" (click)=\"toggleChildTable(rowItem)\">\n <ng-container *ngIf=\"rowItem['$isChildTableOpen']\">\n <svg\n *ngIf=\"!column.extraOptions?.iconFoldTable\"\n width=\"16px\"\n height=\"16px\"\n viewBox=\"0 0 16 16\"\n version=\"1.1\"\n xmlns=\"http://www.w3.org/2000/svg\"\n xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n class=\"svg-icon svg-icon-close\"\n >\n <g stroke=\"none\" stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\">\n <rect x=\"0.5\" y=\"0.5\" width=\"15\" height=\"15\" rx=\"2\"></rect>\n <rect x=\"4\" y=\"7\" width=\"8\" height=\"2\"></rect>\n </g>\n </svg>\n\n <span *ngIf=\"column.extraOptions?.iconFoldTable\" [innerHTML]=\"column.extraOptions?.iconFoldTable | safe : 'html'\"></span>\n </ng-container>\n <ng-container *ngIf=\"!rowItem['$isChildTableOpen']\">\n <svg\n *ngIf=\"!column.extraOptions?.iconUnFoldTable\"\n width=\"16px\"\n height=\"16px\"\n viewBox=\"0 0 16 16\"\n version=\"1.1\"\n xmlns=\"http://www.w3.org/2000/svg\"\n xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n class=\"svg-icon svg-icon-open-folder\"\n >\n <g stroke=\"none\" stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\">\n <rect x=\"0.5\" y=\"0.5\" width=\"15\" height=\"15\" rx=\"2\"></rect>\n <path\n d=\"M8.75,4 L8.75,7.25 L12,7.25 L12,8.75 L8.749,8.75 L8.75,12 L7.25,12 L7.249,8.75 L4,8.75 L4,7.25 L7.25,7.25 L7.25,4 L8.75,4 Z\"\n ></path>\n </g>\n </svg>\n <span *ngIf=\"column.extraOptions?.iconUnFoldTable\" [innerHTML]=\"column.extraOptions?.iconUnFoldTable | safe : 'html'\"></span>\n </ng-container>\n </span>\n </ng-container>\n <ng-template\n [ngTemplateOutlet]=\"column.cellCmp ? column.cellCmp.template : textViewTemplate\"\n [ngTemplateOutletContext]=\"{\n rowIndex: rowIndex,\n colIndex: colIndex,\n column: column,\n rowItem: rowItem,\n cellItem: rowItem[column.field],\n cellValue: rowItem | disPlayCellValuePipe : rowIndex : column : lang,\n $implicit: this,\n row: rowComponent,\n tableLevel: tableLevel\n }\"\n >\n </ng-template>\n</ng-template>\n\n<ng-template let-cellValue=\"cellValue\" #textViewTemplate>\n <span [attr.title]=\"cellValue\">{{ cellValue }}</span>\n</ng-template>\n\n<ng-template let-rowIndex=\"rowIndex\" let-colIndex=\"colIndex\" let-rowItem=\"rowItem\" let-column=\"column\" let-cell=\"\" #textEditTempate>\n <form class=\"form-inline edit-padding-fix\">\n <div class=\"form-group full-width\">\n <div class=\"input-group full-width\">\n <input\n [dAutoFocus]=\"cell && cell.isCellEdit\"\n [type]=\"column.fieldType\"\n class=\"devui-form-control\"\n [name]=\"column.field\"\n [(ngModel)]=\"rowItem[column.field]\"\n [attr.maxlength]=\"column.extraOptions?.maxlength\"\n [attr.minlength]=\"column.extraOptions?.minlength\"\n [attr.pattern]=\"column.extraOptions?.pattern\"\n (keydown.enter)=\"cell && cell.finishCellEdit($event)\"\n [placeholder]=\"column.placeholder || ''\"\n />\n </div>\n </div>\n </form>\n</ng-template>\n\n<ng-template let-rowIndex=\"rowIndex\" let-colIndex=\"colIndex\" let-rowItem=\"rowItem\" let-column=\"column\" let-cell=\"\" #dateEditTempate>\n <form class=\"form-inline edit-padding-fix\">\n <div class=\"devui-form-group\">\n <div class=\"devui-input-group devui-dropdown-origin\">\n <input\n class=\"devui-form-control search\"\n [name]=\"column.field\"\n [(ngModel)]=\"rowItem[column.field]\"\n dDatepicker\n appendToBody\n [dateFormat]=\"column.extraOptions?.dateFormat\"\n #datePicker=\"datepicker\"\n (ngModelChange)=\"!column.extraOptions?.showTime && cell && cell.finishCellEdit()\"\n [maxDate]=\"column.extraOptions?.maxDate\"\n [minDate]=\"column.extraOptions?.minDate\"\n [cssClass]=\"column.extraOptions?.cssClass\"\n [dateConfig]=\"column.extraOptions?.dateConfig\"\n [locale]=\"column.extraOptions?.locale\"\n [customViewTemplate]=\"column.extraOptions?.customViewTemplate\"\n [disabled]=\"column.extraOptions?.disabled\"\n [placeholder]=\"column.placeholder || ''\"\n [autoOpen]=\"true\"\n [showTime]=\"column.extraOptions?.showTime\"\n />\n <div class=\"devui-input-group-addon\" (click)=\"datePicker.toggle()\">\n <i class=\"icon icon-calendar\"></i>\n </div>\n </div>\n </div>\n </form>\n</ng-template>\n" }] }], ctorParameters: () => [{ type: undefined, decorators: [{ type: Inject, args: [DATA_TABLE] }] }, { type: undefined, decorators: [{ type: Inject, args: [DATA_TABLE_ROW] }] }, { type: i1.I18nService }, { type: i0.ChangeDetectorRef }, { type: i0.ComponentFactoryResolver }, { type: i0.ElementRef }, { type: i0.NgZone }], propDecorators: { editorHost: [{ type: ViewChild, args: [EditorDirective] }], rowIndex: [{ type: Input }], colIndex: [{ type: Input }], column: [{ type: Input }], rowItem: [{ type: Input }], editModel: [{ type: Input }], isEditRow: [{ type: Input }], timeout: [{ type: Input }], tableLevel: [{ type: Input }], isEditable: [{ type: HostBinding, args: ['class.editable-cell'] }] } }); class DataTableRowComponent { constructor(dt, changeDetectorRef, rowRef, ngZone) { this.dt = dt; this.changeDetectorRef = changeDetectorRef; this.rowRef = rowRef; this.ngZone = ngZone; this.fontWeight = 'normal'; this.verticalAlign = 'middle'; this.detailChange = new EventEmitter(); this.forceUpdateEvent = new EventEmitter(); this.clickCount = 0; // 记录点击次数 } ngOnInit() { this.ngZone.runOutsideAngular(() => { this.rowRef.nativeElement.addEventListener('mouseup', this.onRowClick.bind(this)); this.rowRef.nativeElement.addEventListener('dblclick', this.onRowDBClick.bind(this)); if (this.generalRowHoveredData) { this.rowRef.nativeElement.addEventListener('mouseenter', this.onRowMouseEnter.bind(this)); this.rowRef.nativeElement.addEventListener('mouseleave', this.onRowMouseLeave.bind(this)); } }); } forceUpdate() { this.changeDetectorRef.markForCheck(); this.forceUpdateEvent.emit(ForceUpdateReason.RowUpdate); } onRowClick($event) { this.clickCount++; if (this.clickCount === 1) { this.timeoutId = setTimeout(() => { if (this.clickCount === 1) { this.dt.onRowClick({ rowIndex: this.rowIndex, nestedIndex: this.nestedIndex, rowItem: this.rowItem, rowComponent: this, event: $event, }); } this.clickCount = 0; clearTimeout(this.timeoutId); }, this.timeout); } } onRowDBClick($event) { this.dt.onRowDBClick({ rowIndex: this.rowIndex, nestedIndex: this.nestedIndex, rowItem: this.rowItem, rowComponent: this }); } onRowMouseEnter($event) { this.ngZone.run(() => { this.rowItem.$hovered = true; }); } onRowMouseLeave($event) { this.ngZone.run(() => { this.rowItem.$hovered = false; }); } onRowCheckChange($event, rowIndex, nestedIndex, rowItem) { rowItem.$checked = $event; rowItem.$halfChecked = false; this.dt.setRowCheckStatus({ rowItem, rowIndex, nestedIndex, checked: $event }); } toggle() { if (this.rowItem.$isDetailOpen === undefined) { this.rowItem.$isDetailOpen = !!this.rowItem.$expandConfig?.expand; } this.rowItem.$isDetailOpen = !this.rowItem.$isDetailOpen; if (this.rowItem.$expandConfig) { this.rowItem.$expandConfig.expand = !this.rowItem.$expandConfig.expand; } this.detailChange.emit({ state: this.rowItem.$isDetailOpen, index: this.rowIndex }); this.dt.onDetailToggle({ state: this.rowItem.$isDetailOpen, index: this.rowIndex }); } trackByFn(index, item) { return index; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DataTableRowComponent, deps: [{ token: DATA_TABLE }, { token: i0.ChangeDetectorRef }, { token: i0.ElementRef }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: DataTableRowComponent, selector: "d-data-table-row, [dDataTableRow]", inputs: { rowItem: "rowItem", selectable: "selectable", checkable: "checkable", showExpandToggle: "showExpandToggle", rowIndex: "rowIndex", allChecked: "allChecked", columns: "columns", editModel: "editModel", editRowItem: "editRowItem", resizeable: "resizeable", timeout: "timeout", tableLevel: "tableLevel", nestedIndex: "nestedIndex", generalRowHoveredData: "generalRowHoveredData" }, outputs: { detailChange: "detailChange" }, host: { properties: { "style.font-weight": "this.fontWeight", "style.vertical-align": "this.verticalAlign" } }, providers: [ { provide: DATA_TABLE_ROW, useExisting: forwardRef(() => DataTableRowComponent), }, ], ngImport: i0, template: "<td *ngIf=\"showExpandToggle\" class=\"devui-detail-cell\" [ngClass]=\"{ 'devui-sticky-left-cell': columns[0]?.fixedLeft }\" [style.left]=\"0\">\n <div *ngIf=\"rowItem.detail || rowItem?.$expandConfig\" (click)=\"toggle()\">\n <span class=\"expand-icon-wrapper\" *ngIf=\"!rowItem['$isDetailOpen'] && !rowItem.$expandConfig?.expand\">\n <svg\n width=\"16px\"\n height=\"16px\"\n viewBox=\"0 0 16 16\"\n version=\"1.1\"\n xmlns=\"http://www.w3.org/2000/svg\"\n xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n >\n <g id=\"\u64CD\u4F5C/collapse2\" stroke=\"none\" stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\">\n <polygon\n id=\"\u8DEF\u5F84\"\n fill=\"#293040\"\n fill-rule=\"nonzero\"\n transform=\"translate(8.146447, 8.000000) scale(-1, 1) translate(-8.146447, -8.000000) \"\n points=\"11.7071068 12.2928932 10.2928932 13.7071068 4.58578644 8 10.2928932 2.29289322 11.7071068 3.70710678 7.41421356 8\"\n ></polygon>\n </g>\n </svg>\n </span>\n <span class=\"expand-icon-wrapper\" *ngIf=\"(rowItem && rowItem['$isDetailOpen']) || rowItem.$expandConfig?.expand\">\n <svg\n width=\"16px\"\n height=\"16px\"\n viewBox=\"0 0 16 16\"\n version=\"1.1\"\n xmlns=\"http://www.w3.org/2000/svg\"\n xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n >\n <g id=\"\u64CD\u4F5C/chevron_down\" stroke=\"none\" stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\">\n <polygon id=\"chevron_down\" fill=\"#293040\" points=\"4.5 5 8 8.76923077 11.5 5 13 6.61538462 8 12 3 6.61538462\"></polygon>\n </g>\n </svg>\n </span>\n </div>\n</td>\n<td\n *ngIf=\"checkable\"\n class=\"devui-checkable-cell\"\n [ngClass]=\"{ 'devui-sticky-left-cell': columns[0]?.fixedLeft }\"\n [style.left]=\"showExpandToggle && columns[0]?.fixedLeft ? '36px' : '0'\"\n>\n <d-checkbox\n [ngModelOptions]=\"{ standalone: true }\"\n (ngModelChange)=\"onRowCheckChange($event, rowIndex, nestedIndex, rowItem)\"\n [ngModel]=\"rowItem.$checked\"\n [halfchecked]=\"rowItem.$halfChecked\"\n [disabled]=\"rowItem.$checkDisabled || rowItem.$disabled\"\n dTooltip\n [content]=\"rowItem.$checkBoxTips\"\n [position]=\"['top', 'right', 'bottom', 'left']\"\n [showAnimation]=\"true\"\n (mouseup)=\"$event.stopPropagation()\"\n >\n </d-checkbox>\n</td>\n<ng-container *ngFor=\"let column of columns; let colIndex = index; trackBy: trackByFn\">\n <td\n