diff --git a/components/datatable/datatable.ts b/components/datatable/datatable.ts index 8f06f3b055e..d6b1fb78b1f 100644 --- a/components/datatable/datatable.ts +++ b/components/datatable/datatable.ts @@ -1,5 +1,5 @@ import {NgModule,Component,ElementRef,AfterContentInit,AfterViewInit,AfterViewChecked,OnInit,OnDestroy,DoCheck,Input,ViewContainerRef, - Output,SimpleChange,EventEmitter,ContentChild,ContentChildren,Renderer,IterableDiffers,QueryList,TemplateRef,ChangeDetectorRef} from '@angular/core'; + Output,SimpleChange,EventEmitter,ContentChild,ContentChildren,Renderer,IterableDiffers,QueryList,TemplateRef,ChangeDetectorRef} from '@angular/core'; import {CommonModule} from '@angular/common'; import {FormsModule} from '@angular/forms' import {SharedModule} from '../common/shared'; @@ -26,11 +26,11 @@ import {Subscription} from 'rxjs/Subscription'; ` }) export class DTRadioButton { - + @Input() checked: boolean; @Output() onClick: EventEmitter = new EventEmitter(); - + handleClick(event) { this.onClick.emit(event); } @@ -52,13 +52,13 @@ export class DTRadioButton { ` }) export class DTCheckbox { - + @Input() checked: boolean; - + @Input() disabled: boolean; @Output() onChange: EventEmitter = new EventEmitter(); - + handleClick(event) { if(!this.disabled) { this.onChange.emit({originalEvent: event, checked: !this.checked}); @@ -72,13 +72,13 @@ export class DTCheckbox { template: `` }) export class RowExpansionLoader { - + @Input() template: TemplateRef; - + @Input() rowData: any; - + constructor(protected viewContainer: ViewContainerRef) {} - + ngOnInit() { let view = this.viewContainer.createEmbeddedView(this.template, { '\$implicit': this.rowData @@ -95,6 +95,7 @@ export class RowExpansionLoader {
@@ -186,18 +187,22 @@ export class RowExpansionLoader {
- - + +
- - {{col.header}} - - -
+ + {{col.header}} + + + + + +
@@ -210,7 +215,7 @@ export class RowExpansionLoader { - {{col.header}} {{resolveFieldData(rowData,col.field)}} @@ -231,10 +236,14 @@ export class RowExpansionLoader { + + +
- @@ -256,7 +265,7 @@ export class DataTable implements AfterViewChecked,AfterViewInit,AfterContentIni @Input() rowsPerPageOptions: number[]; @Input() responsive: boolean; - + @Input() stacked: boolean; @Input() selectionMode: string; @@ -266,7 +275,7 @@ export class DataTable implements AfterViewChecked,AfterViewInit,AfterContentIni @Output() selectionChange: EventEmitter = new EventEmitter(); @Input() editable: boolean; - + @Output() onRowClick: EventEmitter = new EventEmitter(); @Output() onRowSelect: EventEmitter = new EventEmitter(); @@ -274,9 +283,9 @@ export class DataTable implements AfterViewChecked,AfterViewInit,AfterContentIni @Output() onRowUnselect: EventEmitter = new EventEmitter(); @Output() onRowDblclick: EventEmitter = new EventEmitter(); - + @Output() onHeaderCheckboxToggle: EventEmitter = new EventEmitter(); - + @Output() onContextMenuSelect: EventEmitter = new EventEmitter(); @Input() filterDelay: number = 300; @@ -318,17 +327,17 @@ export class DataTable implements AfterViewChecked,AfterViewInit,AfterContentIni @Input() sortOrder: number = 1; @Input() multiSortMeta: SortMeta[]; - + @Input() contextMenu: any; - + @Input() csvSeparator: string = ','; - + @Input() emptyMessage: string = 'No records found'; - + @Input() paginatorPosition: string = 'bottom'; - + @Input() expandedRows: any[]; - + @Output() onEditInit: EventEmitter = new EventEmitter(); @Output() onEditComplete: EventEmitter = new EventEmitter(); @@ -336,29 +345,29 @@ export class DataTable implements AfterViewChecked,AfterViewInit,AfterContentIni @Output() onEdit: EventEmitter = new EventEmitter(); @Output() onEditCancel: EventEmitter = new EventEmitter(); - + @Output() onPage: EventEmitter = new EventEmitter(); - + @Output() onSort: EventEmitter = new EventEmitter(); - + @Output() onFilter: EventEmitter = new EventEmitter(); @ContentChild(Header) header; @ContentChild(Footer) footer; - + @Input() expandableRows: boolean; - + @Input() tabindex: number = 1; - + @Output() onRowExpand: EventEmitter = new EventEmitter(); - + @Output() onRowCollapse: EventEmitter = new EventEmitter(); - + @ContentChild(TemplateRef) rowExpansionTemplate: TemplateRef; - + @ContentChildren(Column) cols: QueryList; - + protected dataToRender: any[]; protected first: number = 0; @@ -374,43 +383,43 @@ export class DataTable implements AfterViewChecked,AfterViewInit,AfterContentIni protected columns: Column[]; protected columnsUpdated: boolean = false; - + protected stopSortPropagation: boolean; - + protected sortColumn: Column; - + protected percentageScrollHeight: boolean; - + protected scrollBody: any; - - protected scrollHeader: any - + + protected scrollHeader: any; + protected scrollHeaderBox: any; - + protected bodyScrollListener: any; - + protected headerScrollListener: any; - + protected resizeScrollListener: any; - + protected columnResizing: boolean; - + protected lastPageX: number; - + protected documentColumnResizeListener: any; - + protected documentColumnResizeEndListener: any; - + protected resizerHelper: any; - + protected resizeColumn: any; - + protected reorderIndicatorUp: any; - + protected reorderIndicatorDown: any; - + protected draggedColumn: any; - + protected tbody: any; differ: any; @@ -418,11 +427,11 @@ export class DataTable implements AfterViewChecked,AfterViewInit,AfterContentIni globalFilterFunction: any; preventBlurOnEdit: boolean; - + columnsSubscription: Subscription; - constructor(protected el: ElementRef, protected domHandler: DomHandler, differs: IterableDiffers, - protected renderer: Renderer, private changeDetector: ChangeDetectorRef) { + constructor(protected el: ElementRef, protected domHandler: DomHandler, differs: IterableDiffers, + protected renderer: Renderer, private changeDetector: ChangeDetectorRef) { this.differ = differs.find([]).create(null); } @@ -438,10 +447,10 @@ export class DataTable implements AfterViewChecked,AfterViewInit,AfterContentIni }); } } - + ngAfterContentInit() { this.initColumns(); - + this.columnsSubscription = this.cols.changes.subscribe(_ => { this.initColumns(); this.changeDetector.markForCheck(); @@ -483,26 +492,62 @@ export class DataTable implements AfterViewChecked,AfterViewInit,AfterContentIni if(this.paginator) { this.updatePaginator(); } - + if(this.stopSortPropagation) { this.stopSortPropagation = false; } - else if(!this.lazy && (this.sortField||this.multiSortMeta)) { + else if(!this.lazy && (this.sortField||this.multiSortMeta)) { if(this.sortMode == 'single') this.sortSingle(); else if(this.sortMode == 'multiple') this.sortMultiple(); } - + this.updateDataToRender(this.filteredValue||this.value); } } - + initColumns(): void { this.columns = this.cols.toArray(); this.columnsUpdated = true; } + public adjustScrollableColumnWidths(): void { + let scrollableHeaderColumns: Array = this.getScrollableHeaderColumns(); + for (let i = 0;i < scrollableHeaderColumns.length;i++) { + this.adjustScrollableColumnWidth(scrollableHeaderColumns[i]); + } + } + + private getScrollableHeaderColumns(): Array { + if (this.scrollable && this.scrollHeaderBox) { + return this.domHandler.find(this.scrollHeaderBox, 'th'); + } + return []; + } + + private adjustScrollableColumnWidth(headerElement): void { + if (!this.scrollable || !this.resizableColumns) { + return; + } + + let scrollableResizeColumns = this.findScrollableResizeColumns(headerElement); + for (let i = 0;i < scrollableResizeColumns.length;i++) { + scrollableResizeColumns[i].style.width = headerElement.style.width; + } + } + + private findScrollableResizeColumns(headerElement): Array { + let headerIndex = this.domHandler.index(headerElement); + let scrollableRows = this.domHandler.find(this.tbody, 'tr.ui-widget-content'); + let scrollableColumns: Array = []; + for (let i = 0;i < scrollableRows.length;i++) { + let scrollableRowColumns = this.domHandler.find(scrollableRows[i], 'td'); + scrollableColumns.push(scrollableRowColumns[headerIndex]); + } + return scrollableColumns; + } + resolveFieldData(data: any, field: string): any { if(data && field) { if(field.indexOf('.') == -1) { @@ -541,7 +586,7 @@ export class DataTable implements AfterViewChecked,AfterViewInit,AfterContentIni this.onLazyLoad.emit(this.createLazyLoadMetadata()); else this.updateDataToRender(this.filteredValue||this.value); - + this.onPage.emit({ first: this.first, rows: this.rows @@ -564,7 +609,7 @@ export class DataTable implements AfterViewChecked,AfterViewInit,AfterContentIni this.dataToRender = datasource; } } - + onHeaderKeydown(event, column: Column) { if(event.keyCode == 13) { this.sort(event, column); @@ -598,7 +643,7 @@ export class DataTable implements AfterViewChecked,AfterViewInit,AfterContentIni this.sortSingle(); } } - + this.onSort.emit({ field: this.sortField, order: this.sortOrder, @@ -628,14 +673,14 @@ export class DataTable implements AfterViewChecked,AfterViewInit,AfterContentIni return (this.sortOrder * result); }); } - + this.first = 0; if(this.hasFilter()) { this.filter(); } } - + //prevent resort at ngDoCheck this.stopSortPropagation = true; } @@ -650,7 +695,7 @@ export class DataTable implements AfterViewChecked,AfterViewInit,AfterContentIni this.filter(); } } - + //prevent resort at ngDoCheck this.stopSortPropagation = true; } @@ -731,17 +776,17 @@ export class DataTable implements AfterViewChecked,AfterViewInit,AfterContentIni handleRowClick(event, rowData) { this.onRowClick.next({originalEvent: event, data: rowData}); - + if(!this.selectionMode) { return; } - + let targetNode = event.target.nodeName; - if(targetNode == 'INPUT' || targetNode == 'BUTTON' || targetNode == 'A' + if(targetNode == 'INPUT' || targetNode == 'BUTTON' || targetNode == 'A' || (this.domHandler.hasClass(event.target, 'ui-c'))) { return; } - + if(this.isSelected(rowData)) { if(this.isSingleSelectionMode()) { this.selection = null; @@ -751,7 +796,7 @@ export class DataTable implements AfterViewChecked,AfterViewInit,AfterContentIni this.selection.splice(this.findIndexInSelection(rowData), 1); this.selectionChange.emit(this.selection); } - + this.onRowUnselect.emit({originalEvent: event, data: rowData, type: 'row'}); } else { @@ -768,7 +813,7 @@ export class DataTable implements AfterViewChecked,AfterViewInit,AfterContentIni this.onRowSelect.emit({originalEvent: event, data: rowData, type: 'row'}); } } - + selectRowWithRadio(rowData:any) { if(this.selection != rowData) { this.selection = rowData; @@ -776,40 +821,40 @@ export class DataTable implements AfterViewChecked,AfterViewInit,AfterContentIni this.onRowSelect.emit({originalEvent: event, data: rowData, type: 'radiobutton'}); } } - + toggleRowWithCheckbox(event,rowData) { let selectionIndex = this.findIndexInSelection(rowData); this.selection = this.selection||[]; - + if(selectionIndex != -1) { this.selection.splice(selectionIndex, 1); this.onRowUnselect.emit({originalEvent: event, data: rowData, type: 'checkbox'}); } - + else { this.selection.push(rowData); this.onRowSelect.emit({originalEvent: event, data: rowData, type: 'checkbox'}); } - + this.selectionChange.emit(this.selection); } - + toggleRowsWithCheckbox(event) { if(event.checked) this.selection = this.dataToRender.slice(0); else this.selection = []; - + this.selectionChange.emit(this.selection); - + this.onHeaderCheckboxToggle.emit({originalEvent: event, checked: event.checked}); } - + onRowRightClick(event, rowData) { if(this.contextMenu) { let selectionIndex = this.findIndexInSelection(rowData); let selected = selectionIndex != -1; - + if(!selected) { if(this.isSingleSelectionMode()) { this.selection = rowData; @@ -822,7 +867,7 @@ export class DataTable implements AfterViewChecked,AfterViewInit,AfterContentIni } } - this.contextMenu.show(event); + this.contextMenu.show(event); this.onContextMenuSelect.emit({originalEvent: event, data: rowData}); } } @@ -856,7 +901,7 @@ export class DataTable implements AfterViewChecked,AfterViewInit,AfterContentIni isSelected(rowData) { return ((rowData && this.domHandler.equals(rowData, this.selection)) || this.findIndexInSelection(rowData) != -1); } - + get allSelected() { let val = true; if(this.dataToRender && this.selection && (this.dataToRender.length == this.selection.length)) { @@ -887,7 +932,7 @@ export class DataTable implements AfterViewChecked,AfterViewInit,AfterContentIni filter() { this.first = 0; - + if(this.lazy) { this.onLazyLoad.emit(this.createLazyLoadMetadata()); } @@ -900,14 +945,14 @@ export class DataTable implements AfterViewChecked,AfterViewInit,AfterContentIni for(let j = 0; j < this.columns.length; j++) { let col = this.columns[j], - filterMeta = this.filters[col.field]; + filterMeta = this.filters[col.field]; //local if(filterMeta) { let filterValue = filterMeta.value, - filterField = col.field, - filterMatchMode = filterMeta.matchMode||'startsWith', - dataFieldValue = this.resolveFieldData(this.value[i], filterField); + filterField = col.field, + filterMatchMode = filterMeta.matchMode||'startsWith', + dataFieldValue = this.resolveFieldData(this.value[i], filterField); let filterConstraint = this.filterConstraints[filterMatchMode]; @@ -946,7 +991,7 @@ export class DataTable implements AfterViewChecked,AfterViewInit,AfterContentIni this.updateDataToRender(this.filteredValue||this.value); } - + this.onFilter.emit({ filters: this.filters }); @@ -1057,25 +1102,20 @@ export class DataTable implements AfterViewChecked,AfterViewInit,AfterContentIni } findCell(element) { - let cell = element; - while(cell.tagName != 'TD') { - cell = cell.parentElement; - } - - return cell; + return this.domHandler.closestTag(element, 'TD', true); } initResizableColumns() { this.tbody = this.domHandler.findSingle(this.el.nativeElement, 'tbody.ui-datatable-data'); this.resizerHelper = this.domHandler.findSingle(this.el.nativeElement, 'div.ui-column-resizer-helper'); this.fixColumnWidths(); - + this.documentColumnResizeListener = this.renderer.listenGlobal('body', 'mousemove', (event) => { if(this.columnResizing) { this.onColumnResize(event); } }); - + this.documentColumnResizeEndListener = this.renderer.listenGlobal('body', 'mouseup', (event) => { if(this.columnResizing) { this.columnResizing = false; @@ -1083,13 +1123,13 @@ export class DataTable implements AfterViewChecked,AfterViewInit,AfterContentIni } }); } - + initColumnResize(event) { this.resizeColumn = event.target.parentElement; this.columnResizing = true; this.lastPageX = event.pageX; } - + onColumnResize(event) { let container = this.el.nativeElement.children[0]; this.domHandler.addClass(container, 'ui-unselectable-text'); @@ -1098,71 +1138,81 @@ export class DataTable implements AfterViewChecked,AfterViewInit,AfterContentIni if(event.pageX > container.offsetLeft && event.pageX < (container.offsetLeft + container.offsetWidth)) { this.resizerHelper.style.left = event.pageX + 'px'; } - + this.resizerHelper.style.display = 'block'; } - + onColumnResizeEnd(event) { let delta = this.resizerHelper.offsetLeft - this.lastPageX; let columnWidth = this.resizeColumn.offsetWidth; let newColumnWidth = columnWidth + delta; let minWidth = this.resizeColumn.style.minWidth||15; - + if(columnWidth + delta > parseInt(minWidth)) { if(this.columnResizeMode === 'fit') { let nextColumn = this.resizeColumn.nextElementSibling; let nextColumnWidth = nextColumn.offsetWidth - delta; - + if(newColumnWidth > 15 && nextColumnWidth > 15) { this.resizeColumn.style.width = newColumnWidth + 'px'; - if(nextColumn) { - nextColumn.style.width = nextColumnWidth + 'px'; - } + this.adjustScrollableColumnWidth(this.resizeColumn); + + nextColumn.style.width = nextColumnWidth + 'px'; + this.adjustScrollableColumnWidth(nextColumn); } } else if(this.columnResizeMode === 'expand') { this.tbody.parentElement.style.width = this.tbody.parentElement.offsetWidth + delta + 'px'; this.resizeColumn.style.width = newColumnWidth + 'px'; - } - + if (this.scrollable && this.resizableColumns) { + let scrollableTable = this.domHandler.closestTag(this.resizeColumn, 'TABLE'); + scrollableTable.style.width = scrollableTable.offsetWidth + delta + 'px'; + this.adjustScrollableColumnWidth(this.resizeColumn); + } + } + this.onColResize.emit({ element: this.resizeColumn, delta: delta }); } - + this.resizerHelper.style.display = 'none'; this.resizeColumn = null; this.domHandler.removeClass(this.el.nativeElement.children[0], 'ui-unselectable-text'); } - + fixColumnWidths() { let columns = this.domHandler.find(this.el.nativeElement, 'th.ui-resizable-column'); for(let col of columns) { - col.style.width = 'auto'; - } - - for(let col of columns) { - col.style.width = col.offsetWidth + 'px'; + if (!this.domHandler.hasClass(col, 'col-icon')) { + col.style.width = 'auto'; + } } + + this.adjustScrollableColumnWidths(); } - + onColumnDragStart(event) { + if (this.columnResizing) { + event.preventDefault(); + return; + } this.draggedColumn = this.findParentHeader(event.target); event.dataTransfer.setData('a', 'b'); // Firefox requires this to make dragging possible } - + onColumnDragover(event) { if(this.reorderableColumns && this.draggedColumn) { event.preventDefault(); let dropHeader = this.findParentHeader(event.target); - + if(this.draggedColumn != dropHeader) { let targetPosition = dropHeader.getBoundingClientRect(); let targetLeft = targetPosition.left + document.body.scrollLeft; let targetTop = targetPosition.top + document.body.scrollTop; let columnCenter = targetLeft + dropHeader.offsetWidth / 2; - + this.reorderIndicatorUp.style.top = (targetTop - 16) + 'px'; this.reorderIndicatorDown.style.top = targetTop + dropHeader.offsetHeight + 'px'; @@ -1174,7 +1224,7 @@ export class DataTable implements AfterViewChecked,AfterViewInit,AfterContentIni this.reorderIndicatorUp.style.left = (targetLeft - 8) + 'px'; this.reorderIndicatorDown.style.left = (targetLeft - 8)+ 'px'; } - + this.reorderIndicatorUp.style.display = 'block'; this.reorderIndicatorDown.style.display = 'block'; } @@ -1183,7 +1233,7 @@ export class DataTable implements AfterViewChecked,AfterViewInit,AfterContentIni } } } - + onColumnDragleave(event) { if(this.reorderableColumns && this.draggedColumn) { event.preventDefault(); @@ -1191,7 +1241,7 @@ export class DataTable implements AfterViewChecked,AfterViewInit,AfterContentIni this.reorderIndicatorDown.style.display = 'none'; } } - + onColumnDrop(event) { event.preventDefault(); let dragIndex = this.domHandler.index(this.draggedColumn); @@ -1206,7 +1256,7 @@ export class DataTable implements AfterViewChecked,AfterViewInit,AfterContentIni columns: this.columns }); } - + this.reorderIndicatorUp.style.display = 'none'; this.reorderIndicatorDown.style.display = 'none'; this.draggedColumn = null; @@ -1216,18 +1266,9 @@ export class DataTable implements AfterViewChecked,AfterViewInit,AfterContentIni this.reorderIndicatorUp = this.domHandler.findSingle(this.el.nativeElement.children[0], 'span.ui-datatable-reorder-indicator-up'); this.reorderIndicatorDown = this.domHandler.findSingle(this.el.nativeElement.children[0], 'span.ui-datatable-reorder-indicator-down'); } - + findParentHeader(element) { - if(element.nodeName == 'TH') { - return element; - } - else { - let parent = element.parentElement; - while(parent.nodeName != 'TH') { - parent = parent.parentElement; - } - return parent; - } + return this.domHandler.closestTag(element, 'TH', true); } initScrolling() { @@ -1235,31 +1276,31 @@ export class DataTable implements AfterViewChecked,AfterViewInit,AfterContentIni this.scrollHeaderBox = this.domHandler.findSingle(this.el.nativeElement, '.ui-datatable-scrollable-header-box'); this.scrollBody = this.domHandler.findSingle(this.el.nativeElement, '.ui-datatable-scrollable-body'); this.percentageScrollHeight = this.scrollHeight && (this.scrollHeight.indexOf('%') !== -1); - + if(this.scrollHeight) { if(this.percentageScrollHeight) this.scrollBody.style.maxHeight = this.domHandler.getOuterHeight(this.el.nativeElement.parentElement) * (parseInt(this.scrollHeight) / 100) + 'px'; else this.scrollBody.style.maxHeight = this.scrollHeight; - + this.scrollHeaderBox.style.marginRight = this.calculateScrollbarWidth() + 'px'; } - + this.bodyScrollListener = this.renderer.listen(this.scrollBody, 'scroll', () => { this.scrollHeaderBox.style.marginLeft = -1 * this.scrollBody.scrollLeft + 'px'; }); - + this.headerScrollListener = this.renderer.listen(this.scrollHeader, 'scroll', () => { this.scrollHeader.scrollLeft = 0; }); - + if(this.percentageScrollHeight) { this.resizeScrollListener = this.renderer.listenGlobal('window', 'resize', () => { this.scrollBody.style.maxHeight = this.domHandler.getOuterHeight(this.el.nativeElement.parentElement) * (parseInt(this.scrollHeight) / 100) + 'px'; }); } } - + calculateScrollbarWidth(): number { let scrollDiv = document.createElement("div"); scrollDiv.className = "ui-scrollbar-measure"; @@ -1267,7 +1308,7 @@ export class DataTable implements AfterViewChecked,AfterViewInit,AfterContentIni let scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth; document.body.removeChild(scrollDiv); - + return scrollbarWidth; } @@ -1302,14 +1343,14 @@ export class DataTable implements AfterViewChecked,AfterViewInit,AfterContentIni multiSortMeta: this.multiSortMeta }; } - + toggleRow(row: any) { if(!this.expandedRows) { this.expandedRows = []; } - + let expandedRowIndex = this.findExpandedRowIndex(row); - + if(expandedRowIndex != -1) { this.expandedRows.splice(expandedRowIndex, 1); this.onRowCollapse.emit(row); @@ -1319,7 +1360,7 @@ export class DataTable implements AfterViewChecked,AfterViewInit,AfterContentIni this.onRowExpand.emit(row); } } - + findExpandedRowIndex(row: any): number { let index = -1 if(this.expandedRows) { @@ -1332,15 +1373,15 @@ export class DataTable implements AfterViewChecked,AfterViewInit,AfterContentIni } return index; } - + isRowExpanded(row) { return this.findExpandedRowIndex(row) != -1; } - + public reset() { this.sortField = null; this.sortOrder = 1; - + this.filteredValue = null; this.filters = {}; @@ -1358,36 +1399,36 @@ export class DataTable implements AfterViewChecked,AfterViewInit,AfterContentIni visibleColumns() { return this.columns.filter(c => !c.hidden); } - + public exportCSV() { let data = this.value, - csv = "data:text/csv;charset=utf-8,"; - + csv = "data:text/csv;charset=utf-8,"; + //headers for(let i = 0; i < this.columns.length; i++) { if(this.columns[i].field) { csv += this.columns[i].field; - + if(i < (this.columns.length - 1)) { csv += this.csvSeparator; } } } - - //body + + //body this.value.forEach((record, i) => { csv += '\n'; for(let i = 0; i < this.columns.length; i++) { if(this.columns[i].field) { csv += this.resolveFieldData(record, this.columns[i].field); - + if(i < (this.columns.length - 1)) { csv += this.csvSeparator; } } } }); - + window.open(encodeURI(csv)); } @@ -1396,21 +1437,21 @@ export class DataTable implements AfterViewChecked,AfterViewInit,AfterContentIni if(this.globalFilterFunction) { this.globalFilterFunction(); } - + if(this.scrollable) { this.bodyScrollListener(); this.headerScrollListener(); - + if(this.percentageScrollHeight) { this.resizeScrollListener(); } } - + if(this.resizableColumns) { this.documentColumnResizeListener(); this.documentColumnResizeEndListener(); } - + if(this.columnsSubscription) { this.columnsSubscription.unsubscribe(); } diff --git a/components/dom/domhandler.ts b/components/dom/domhandler.ts index 482ede3c638..44c2079011c 100644 --- a/components/dom/domhandler.ts +++ b/components/dom/domhandler.ts @@ -56,6 +56,26 @@ export class DomHandler { return element.querySelector(selector); } + public closestClass(element: any, className: string, includeSelf: boolean = false) { + return this.closest(element, (parent) => { + return !this.hasClass(parent, className); + }, includeSelf); + } + + public closestTag(element: any, nodeName: string, includeSelf: boolean = false) { + return this.closest(element, (parent) => { + return parent.nodeName != nodeName; + }, includeSelf); + } + + private closest(element: any, continuePredicate: Function, includeSelf: boolean) { + let parent = includeSelf ? element : element.parentElement; + while(continuePredicate(parent)) { + parent = parent.parentElement; + } + return parent; + } + public index(element: any): number { let children = element.parentNode.childNodes; let num = 0; diff --git a/showcase/app.routes.ts b/showcase/app.routes.ts index b119c4670ab..96d6287c0b2 100644 --- a/showcase/app.routes.ts +++ b/showcase/app.routes.ts @@ -74,6 +74,7 @@ import {DataScrollerDemo} from "./demo/datascroller/datascrollerdemo"; import {DataScrollerInlineDemo} from "./demo/datascroller/datascrollerinlinedemo"; import {DataScrollerLoaderDemo} from "./demo/datascroller/datascrollerloaderdemo"; import {DataScrollerInfiniteDemo} from "./demo/datascroller/datascrollerinfinitedemo"; +import {DataTableIntegrationDemo} from "./demo/datatable/datatableintegrationdemo"; import {TreeDemo} from "./demo/tree/treedemo"; import {TreeTableDemo} from "./demo/treetable/treetabledemo"; import {TerminalDemo} from "./demo/terminal/terminaldemo"; @@ -185,6 +186,7 @@ export const routes: Routes = [ {path: 'toolbar', component: ToolbarDemo}, {path: 'validation', component: ValidationDemo}, {path: 'datatableexport', component: DataTableExportDemo}, + {path: 'datatableintegration', component: DataTableIntegrationDemo}, {path: 'tabmenu', component: TabMenuDemo}, {path: 'tooltip', component: TooltipDemo}, {path: 'menumodel', component: MenuModelApi}, diff --git a/showcase/application.ts b/showcase/application.ts index 6d762425ec3..882faf1bc41 100644 --- a/showcase/application.ts +++ b/showcase/application.ts @@ -85,6 +85,7 @@ import {DataScrollerInlineDemo} from "./demo/datascroller/datascrollerinlinedemo import {DataScrollerLoaderDemo} from "./demo/datascroller/datascrollerloaderdemo"; import {DataScrollerInfiniteDemo} from "./demo/datascroller/datascrollerinfinitedemo"; import {DataScrollerSubMenu} from "./demo/datascroller/datascrollersubmenu"; +import {DataTableIntegrationDemo} from "./demo/datatable/datatableintegrationdemo"; import {TreeDemo} from "./demo/tree/treedemo"; import {TreeTableDemo} from "./demo/treetable/treetabledemo"; import {TerminalDemo} from "./demo/terminal/terminaldemo"; @@ -326,6 +327,7 @@ export class AppComponent { DataTableColTogglerDemo, DataTableRowExpansionDemo, DataTableSubmenu, + DataTableIntegrationDemo, CodeHighlighterDemo, OrderListDemo, PickListDemo, diff --git a/showcase/demo/datatable/datatableintegrationdemo.html b/showcase/demo/datatable/datatableintegrationdemo.html new file mode 100644 index 00000000000..0072086630a --- /dev/null +++ b/showcase/demo/datatable/datatableintegrationdemo.html @@ -0,0 +1,164 @@ + + +
+
+ DataTable + Demo presents combined usage of properties: resizableColumns, reorderableColumns, + scrollable, sortable, columnResizeMode, scrollHeight, scrollWidth, header, footer, filter, paginator, selectionMode, + expandableRows with inner table and column toggler. +
+
+ +
+

Fit Mode

+ +
+
+ +
+
+
Footer
+ + + +
+ +

Expand Mode

+ +
+
+ +
+
+
Footer
+ + + +
+
+ +
+ + +
+
+export class DataTableIntegrationDemo implements OnInit {
+
+    cars1: Car[];
+
+    cols1: any[];
+
+    columnOptions1: SelectItem[];
+
+    cars2: Car[];
+
+    cols2: any[];
+
+    cars3: Car[];
+
+    columnOptions2: SelectItem[];
+
+    constructor(private carService: CarService) {
+    }
+
+    ngOnInit(): void {
+        this.cars1 = this.carService.getCarsMedium();
+        this.cols1 = [
+            {field: 'vin', header: 'Vin'},
+            {field: 'year', header: 'Year'},
+            {field: 'brand', header: 'Brand'},
+            {field: 'color', header: 'Color'}
+        ];
+        this.columnOptions1 = [];
+        this.initTable(this.cars1, this.cols1, this.columnOptions1);
+
+        this.cars2 = this.carService.getCarsMedium();
+        this.cols2 = [
+            {field: 'vin', header: 'Vin'},
+            {field: 'year', header: 'Year'},
+            {field: 'brand', header: 'Brand'},
+            {field: 'color', header: 'Color'}
+        ];
+        this.columnOptions2 = [];
+        this.initTable(this.cars2, this.cols2, this.columnOptions2);
+
+        this.cars3 = this.carService.getCarsSmall();
+    }
+
+    private initTable(cars: Car[], cols: any[], columnOptions: SelectItem[]): void {
+        for(let i = 0; i < cols.length; i++) {
+            columnOptions.push({label: cols[i].header, value: cols[i]});
+        }
+    }
+}
+
+
+
+ + +
+
+<h3 class="first">Fit Mode</h3>
+<p-dataTable [value]="cars1" resizableColumns="true" reorderableColumns="true" scrollable="true" scrollHeight="200px"
+	[paginator]="true" [pageLinks]="3" [rowsPerPageOptions]="[5,10,20]" rows="10" selectionMode="single" expandableRows="true">
+	<header>
+		<div style="text-align:left">
+			<p-multiSelect [options]="columnOptions1" [(ngModel)]="cols1"></p-multiSelect>
+		</div>
+	</header>
+	<footer>Footer</footer>
+	<template let-car>
+		<p-dataTable [value]="cars3">
+			<p-column field="vin" header="Vin"></p-column>
+			<p-column field="year" header="Year"></p-column>
+			<p-column field="brand" header="Brand"></p-column>
+			<p-column field="color" header="Color"></p-column>
+		</p-dataTable>
+	</template>
+	<p-column expander="true" styleClass="col-icon"></p-column>
+	<p-column *ngFor="let col of cols1 let i = index" [field]="col.field" [header]="col.header" sortable="true" [filter]="i % 2"></p-column>
+</p-dataTable>
+
+<h3>Expand Mode</h3>
+<p-dataTable [value]="cars2" resizableColumns="true" reorderableColumns="true" scrollable="true" columnResizeMode="expand"
+	scrollHeight="200px" scrollWidth="75%" [paginator]="true" [pageLinks]="3" [rowsPerPageOptions]="[5,10,20]" rows="10" selectionMode="multiple"
+	expandableRows="true">
+	<header>
+		<div style="text-align:left">
+			<p-multiSelect [options]="columnOptions2" [(ngModel)]="cols2"></p-multiSelect>
+		</div>
+	</header>
+	<footer>Footer</footer>
+	<template let-car>
+		<p-dataTable [value]="cars3">
+			<p-column field="vin" header="Vin"></p-column>
+			<p-column field="year" header="Year"></p-column>
+			<p-column field="brand" header="Brand"></p-column>
+			<p-column field="color" header="Color"></p-column>
+		</p-dataTable>
+	</template>
+	<p-column expander="true" styleClass="col-icon"></p-column>
+	<p-column *ngFor="let col of cols2; let i = index" [field]="col.field" [header]="col.header" sortable="true" [filter]="i % 2"></p-column>
+</p-dataTable>
+
+
+
+
+
\ No newline at end of file diff --git a/showcase/demo/datatable/datatableintegrationdemo.ts b/showcase/demo/datatable/datatableintegrationdemo.ts new file mode 100644 index 00000000000..ce30b89210e --- /dev/null +++ b/showcase/demo/datatable/datatableintegrationdemo.ts @@ -0,0 +1,57 @@ +import {Component, OnInit} from '@angular/core'; +import {Car} from '../domain/car'; +import {CarService} from '../service/carservice'; +import {SelectItem} from "../../../components/common/api"; + +@Component({ + templateUrl: 'showcase/demo/datatable/datatableintegrationdemo.html' +}) +export class DataTableIntegrationDemo implements OnInit { + + cars1: Car[]; + + cols1: any[]; + + columnOptions1: SelectItem[]; + + cars2: Car[]; + + cols2: any[]; + + cars3: Car[]; + + columnOptions2: SelectItem[]; + + constructor(private carService: CarService) { + } + + ngOnInit(): void { + this.carService.getCarsMedium().then(cars => this.cars1 = cars); + this.cols1 = [ + {field: 'vin', header: 'Vin'}, + {field: 'year', header: 'Year'}, + {field: 'brand', header: 'Brand'}, + {field: 'color', header: 'Color'} + ]; + this.columnOptions1 = []; + this.initTable(this.cars1, this.cols1, this.columnOptions1); + + this.carService.getCarsMedium().then(cars => this.cars2 = cars); + this.cols2 = [ + {field: 'vin', header: 'Vin'}, + {field: 'year', header: 'Year'}, + {field: 'brand', header: 'Brand'}, + {field: 'color', header: 'Color'} + ]; + this.columnOptions2 = []; + this.initTable(this.cars2, this.cols2, this.columnOptions2); + + this.carService.getCarsSmall().then(cars => this.cars3 = cars); + } + + private initTable(cars: Car[], cols: any[], columnOptions: SelectItem[]): void { + for(let i = 0; i < cols.length; i++) { + columnOptions.push({label: cols[i].header, value: cols[i]}); + } + } +}