UNPKG

@catull/igniteui-angular

Version:

Ignite UI for Angular is a dependency-free Angular toolkit for building modern web apps

1,212 lines 153 kB
var IgxGridComponent_1; import { __decorate, __metadata } from "tslib"; import { Component, ChangeDetectionStrategy, Input, Output, EventEmitter, ContentChild, ViewChildren, QueryList, ViewChild, ElementRef, TemplateRef, HostBinding, forwardRef, HostListener } from '@angular/core'; import { GridBaseAPIService } from '../api.service'; import { IgxGridBaseDirective } from '../grid-base.directive'; import { IgxGridNavigationService } from '../grid-navigation.service'; import { IgxGridAPIService } from './grid-api.service'; import { cloneArray } from '../../core/utils'; import { IgxGroupByRowTemplateDirective, IgxGridDetailTemplateDirective } from './grid.directives'; import { IgxGridGroupByRowComponent } from './groupby-row.component'; import { takeUntil, first } from 'rxjs/operators'; import { IgxFilteringService } from '../filtering/grid-filtering.service'; import { IgxColumnResizingService } from '../resizing/resizing.service'; import { IgxGridSummaryService } from '../summaries/grid-summary.service'; import { IgxGridSelectionService, IgxGridCRUDService } from '../selection/selection.service'; import { IgxForOfSyncService, IgxForOfScrollSyncService } from '../../directives/for-of/for_of.sync.service'; import { IgxGridMRLNavigationService } from '../grid-mrl-navigation.service'; import { IgxRowIslandAPIService } from '../hierarchical-grid/row-island-api.service'; import { FilterMode } from '../common/enums'; let NEXT_ID = 0; /** * **Ignite UI for Angular Grid** - * [Documentation](https://www.infragistics.com/products/ignite-ui-angular/angular/components/grid.html) * * The Ignite UI Grid is used for presenting and manipulating tabular data in the simplest way possible. Once data * has been bound, it can be manipulated through filtering, sorting & editing operations. * * Example: * ```html * <igx-grid [data]="employeeData" autoGenerate="false"> * <igx-column field="first" header="First Name"></igx-column> * <igx-column field="last" header="Last Name"></igx-column> * <igx-column field="role" header="Role"></igx-column> * </igx-grid> * ``` */ let IgxGridComponent = IgxGridComponent_1 = class IgxGridComponent extends IgxGridBaseDirective { constructor() { super(...arguments); this._id = `igx-grid-${NEXT_ID++}`; /** * @hidden */ this._groupingExpressions = []; /** * @hidden */ this._groupingExpandState = []; this._hideGroupedColumns = false; this._dropAreaMessage = null; this._filteredData = null; this.childDetailTemplates = new Map(); /** *@hidden */ this.groupingExpressionsChange = new EventEmitter(); /** *@hidden */ this.groupingExpansionStateChange = new EventEmitter(); /** * An @Input property that determines whether created groups are rendered expanded or collapsed. * The default rendered state is expanded. * ```html * <igx-grid #grid [data]="Data" [groupsExpanded]="false" [autoGenerate]="true"></igx-grid> * ``` * @memberof IgxGridComponent */ this.groupsExpanded = true; /** * A hierarchical representation of the group by records. * ```typescript * let groupRecords = this.grid.groupsRecords; * ``` * @memberof IgxGridComponent */ this.groupsRecords = []; /** * Emitted when a new `IgxColumnComponent` gets grouped/ungrouped, or multiple columns get * grouped/ungrouped at once by using the Group By API. * The `onGroupingDone` event would be raised only once if several columns get grouped at once by calling * the `groupBy()` or `clearGrouping()` API methods and passing an array as an argument. * The event arguments provide the `expressions`, `groupedColumns` and `ungroupedColumns` properties, which contain * the `ISortingExpression` and the `IgxColumnComponent` related to the grouping/ungrouping operation. * Please note that `groupedColumns` and `ungroupedColumns` show only the **newly** changed columns (affected by the **last** * grouping/ungrouping operation), not all columns which are currently grouped/ungrouped. * columns. * ```typescript * groupingDone(event: IGroupingDoneEventArgs){ * const expressions = event.expressions; * //the newly grouped columns * const groupedColumns = event.groupedColumns; * //the newly ungrouped columns * const ungroupedColumns = event.ungroupedColumns; * } * ``` * ```html * <igx-grid #grid [data]="localData" (onGroupingDone)="groupingDone($event)" [autoGenerate]="true"></igx-grid> * ``` * @memberof IgxGridComponent */ this.onGroupingDone = new EventEmitter(); this.detailTemplate = null; this._expansionStates = new Map(); /** *@hidden */ this.expansionStatesChange = new EventEmitter(); /** *@hidden */ this._focusIn = new EventEmitter(); } /** * An @Input property that sets the value of the `id` attribute. If not provided it will be automatically generated. * ```html * <igx-grid [id]="'igx-grid-1'" [data]="Data" [autoGenerate]="true"></igx-grid> * ``` * @memberof IgxGridComponent */ get id() { return this._id; } set id(value) { this._id = value; } /** * An @Input property that lets you fill the `IgxGridComponent` with an array of data. * ```html * <igx-grid [data]="Data" [autoGenerate]="true"></igx-grid> * ``` * @memberof IgxGridComponent */ get data() { return this._data; } set data(value) { this._data = value || []; this.summaryService.clearSummaryCache(); if (this.shouldGenerate) { this.setupColumns(); } this.cdr.markForCheck(); } /** * Returns an array of objects containing the filtered data in the `IgxGridComponent`. * ```typescript * let filteredData = this.grid.filteredData; * ``` * @memberof IgxGridComponent */ get filteredData() { return this._filteredData; } /** * Sets an array of objects containing the filtered data in the `IgxGridComponent`. * ```typescript * this.grid.filteredData = [{ * ID: 1, * Name: "A" * }]; * ``` * @memberof IgxGridComponent */ set filteredData(value) { this._filteredData = value; } /** * Returns the state of the grid virtualization, including the start index and how many records are rendered. * ```typescript * const gridVirtState = this.grid1.virtualizationState; * ``` * @memberof IgxGridComponent */ get virtualizationState() { return this.verticalScrollContainer.state; } /** * @hidden */ set virtualizationState(state) { this.verticalScrollContainer.state = state; } /** * Sets the total number of records in the data source. * This property is required for remote grid virtualization to function when it is bound to remote data. * ```typescript * this.grid1.totalItemCount = 55; * ``` * @memberof IgxGridComponent */ set totalItemCount(count) { this.verticalScrollContainer.totalItemCount = count; this.cdr.detectChanges(); } /** * Returns the total number of records in the data source. * Works only with remote grid virtualization. * ```typescript * const itemCount = this.grid1.totalItemCount; * ``` * @memberof IgxGridComponent */ get totalItemCount() { return this.verticalScrollContainer.totalItemCount; } get _gridAPI() { return this.gridAPI; } /** * Returns the group by state of the `IgxGridComponent`. * ```typescript * let groupByState = this.grid.groupingExpressions; * ``` * @memberof IgxGridComponent */ get groupingExpressions() { return this._groupingExpressions; } /** * Sets the group by state of the `IgxGridComponent` and emits the `onGroupingDone` * event with the appropriate arguments. * ```typescript * this.grid.groupingExpressions = [{ * fieldName: "ID", * dir: SortingDirection.Asc, * ignoreCase: false * }]; * ``` * * Two-way data binding. * ```html * <igx-grid #grid [data]="Data" [autoGenerate]="true" [(groupingExpressions)]="model.groupingExpressions"></igx-grid> * ``` * @memberof IgxGridComponent */ set groupingExpressions(value) { if (value && value.length > 10) { throw Error('Maximum amount of grouped columns is 10.'); } const oldExpressions = this.groupingExpressions; const newExpressions = value; this._groupingExpressions = cloneArray(value); this.groupingExpressionsChange.emit(this._groupingExpressions); this.chipsGoupingExpressions = cloneArray(value); if (this._gridAPI.grid) { /* grouping should work in conjunction with sorting and without overriding separate sorting expressions */ this._applyGrouping(); this._gridAPI.arrange_sorting_expressions(); this.notifyChanges(); } else { // setter called before grid is registered in grid API service this.sortingExpressions.unshift.apply(this.sortingExpressions, this._groupingExpressions); } if (!this._init && JSON.stringify(oldExpressions) !== JSON.stringify(newExpressions) && this.columnList) { const groupedCols = []; const ungroupedCols = []; const groupedColsArr = newExpressions.filter((obj) => { return !oldExpressions.some((obj2) => { return obj.fieldName === obj2.fieldName; }); }); groupedColsArr.forEach((elem) => { groupedCols.push(this.getColumnByName(elem.fieldName)); }, this); const ungroupedColsArr = oldExpressions.filter((obj) => { return !newExpressions.some((obj2) => { return obj.fieldName === obj2.fieldName; }); }); ungroupedColsArr.forEach((elem) => { ungroupedCols.push(this.getColumnByName(elem.fieldName)); }, this); this.notifyChanges(); const groupingDoneArgs = { expressions: newExpressions, groupedColumns: groupedCols, ungroupedColumns: ungroupedCols }; this.onGroupingDone.emit(groupingDoneArgs); } } /** * Returns a list of expansion states for group rows. * Includes only states that differ from the default one (controlled through groupsExpanded and states that the user has changed. * Contains the expansion state (expanded: boolean) and the unique identifier for the group row (Array). * ```typescript * const groupExpState = this.grid.groupingExpansionState; * ``` * @memberof IgxGridComponent */ get groupingExpansionState() { return this._groupingExpandState; } /** * Sets a list of expansion states for group rows. * ```typescript * this.grid.groupingExpansionState = [{ * expanded: false, * hierarchy: [{ fieldName: 'ID', value: 1 }] * }]; * // You can use DataUtil.getHierarchy(groupRow) to get the group `IgxGridRowComponent` hierarchy. * ``` * * Two-way data binding. * ```html * <igx-grid #grid [data]="Data" [autoGenerate]="true" [(groupingExpansionState)]="model.groupingExpansionState"></igx-grid> * ``` * @memberof IgxGridComponent */ set groupingExpansionState(value) { if (value !== this._groupingExpandState) { this.groupingExpansionStateChange.emit(value); } this._groupingExpandState = value; if (this.gridAPI.grid) { this.cdr.detectChanges(); } } /** * An @Input property that sets whether the grouped columns should be hidden as well. * The default value is "false" * ```html * <igx-grid #grid [data]="localData" [hideGroupedColumns]="true" [autoGenerate]="true"></igx-grid> * ``` * @memberof IgxGridComponent */ get hideGroupedColumns() { return this._hideGroupedColumns; } set hideGroupedColumns(value) { if (value) { this.groupingDiffer = this.differs.find(this.groupingExpressions).create(); } else { this.groupingDiffer = null; } if (this.columnList && this.groupingExpressions) { this._setGroupColsVisibility(value); } this._hideGroupedColumns = value; } /** * An @Input property that sets the message displayed inside the GroupBy drop area where columns can be dragged on. * Note: The grid needs to have at least one groupable column in order the GroupBy area to be displayed. * ```html * <igx-grid dropAreaMessage="Drop here to group!"> * <igx-column [groupable]="true" field="ID"></igx-column> * </igx-grid> * ``` * @memberof IgxGridComponent */ set dropAreaMessage(value) { this._dropAreaMessage = value; this.notifyChanges(); } /** * An accessor that returns the message displayed inside the GroupBy drop area where columns can be dragged on. */ get dropAreaMessage() { return this._dropAreaMessage || this.resourceStrings.igx_grid_groupByArea_message; } /** * A list of all group rows. * ```typescript * const groupList = this.grid.groupsRowList; * ``` * @memberof IgxGridComponent */ get groupsRowList() { const res = new QueryList(); if (!this._groupsRowList) { return res; } const rList = this._groupsRowList.filter((item) => { return item.element.nativeElement.parentElement !== null; }).sort((item1, item2) => item1.index - item2.index); res.reset(rList); return res; } /** * Returns a list of key-value pairs [row ID, expansion state]. Includes only states that differ from the default one. * ```typescript * const expansionStates = this.grid.expansionStates; * ``` * @memberof IgxGridComponent */ get expansionStates() { return this._expansionStates; } onFocusIn() { this._focusIn.emit(); } /** * Sets a list of key-value pairs [row ID, expansion state]. * ```typescript * const states = new Map<any, boolean>(); * states.set(1, true); * this.grid.expansionStates = states; * ``` * * Two-way data binding. * ```html * <igx-grid #grid [data]="data" [(expansionStates)]="model.expansionStates"> * <ng-template igxGridDetail let-dataItem> * <div *ngIf="dataItem.Category"> * <header>{{dataItem.Category?.CategoryName}}</header> * <span>{{dataItem.Category?.Description}}</span> * </div> * </ng-template> * </igx-grid> * ``` * @memberof IgxGridComponent */ set expansionStates(value) { this._expansionStates = new Map(value); this.expansionStatesChange.emit(this._expansionStates); if (this.gridAPI.grid) { this.cdr.detectChanges(); this._focusActiveCell(); } } /** * Expands all master rows. * ```typescript * this.grid.expandAll(); * ``` * @memberof IgxGridComponent */ expandAll() { const expandedStates = this.expansionStates; this.data.forEach((rec) => { expandedStates.set(this.primaryKey ? rec[this.primaryKey] : rec, true); }); this.expansionStates = expandedStates; } /** * Collapses all master rows. * ```typescript * this.grid.collapseAll(); * ``` * @memberof IgxGridComponent */ collapseAll() { this.expansionStates = new Map(); } /** * Expands the master row by its id. ID is either the primaryKey value or the data record instance. * ```typescript * this.grid.expand(rowID); * ``` * @memberof IgxGridComponent */ expand(rowID) { const expandedStates = this.expansionStates; expandedStates.set(rowID, true); this.expansionStates = expandedStates; } /** * Collapses the master row by its id. ID is either the primaryKey value or the data record instance. * ```typescript * this.grid.collapse(rowID); * ``` * @memberof IgxGridComponent */ collapse(rowID) { const expandedStates = this.expansionStates; expandedStates.set(rowID, false); this.expansionStates = expandedStates; } /** * Toggles the master row by its id. ID is either the primaryKey value or the data record instance. * ```typescript * this.grid.toggle(rowID); * ``` * @memberof IgxGridComponent */ toggleRow(rowID) { const expandedStates = this.expansionStates; const state = expandedStates.get(rowID); expandedStates.set(rowID, !state); this.expansionStates = expandedStates; } getDetailsContext(rowData, index) { return { $implicit: rowData, index: index }; } preventContainerScroll(evt) { if (evt.target.scrollTop !== 0 && this.hasDetails) { const activeElem = document.activeElement; this.verticalScrollContainer.addScrollTop(evt.target.scrollTop); evt.target.scrollTop = 0; this.verticalScrollContainer.onChunkLoad.pipe(first()).subscribe(() => { const active = this.selectionService.activeElement; const currRow = this.navigation.getRowByIndex(active.row, ''); // check if the focused element was a child of the details view if (this.isDetailRecord(active.row) && currRow && currRow.contains(activeElem)) { // Some browsers (like Edge/IE) lose focus after scrolling even when the element was in the DOM. activeElem.focus({ preventScroll: true }); return; } const nextCellTarget = this.navigation.getCellElementByVisibleIndex(active.row, active.column); const nextRowTarget = this.navigation.getRowByIndex(active.row + 1, ''); if (nextCellTarget) { nextCellTarget.focus({ preventScroll: true }); } else if (nextRowTarget) { nextRowTarget.focus({ preventScroll: true }); } }); } } /** * @hidden */ trackChanges(index, rec) { if (rec.detailsData !== undefined) { return rec.detailsData; } return rec; } detailsViewFocused(container, rowIndex) { this.selectionService.activeElement = { row: rowIndex, column: this.selectionService.activeElement ? this.selectionService.activeElement.column : 0 }; } detailsKeyboardHandler(event, rowIndex, container) { const colIndex = this.selectionService.activeElement ? this.selectionService.activeElement.column : 0; const shift = event.shiftKey; const ctrl = event.ctrlKey; const key = event.key.toLowerCase(); const target = event.target; if (key === 'tab') { event.stopPropagation(); const lastColIndex = this.unpinnedColumns[this.unpinnedColumns.length - 1].visibleIndex; if (shift && target === container) { // shift + tab from details to data row event.preventDefault(); this.navigateTo(rowIndex - 1, lastColIndex, (args) => args.target.nativeElement.focus()); } else if (!shift) { // when the next element is focused via tab check if it is an element outside the details view // if so we have exited the details view and focus should move to the first cell in the next row this._focusIn.pipe(first()).subscribe(() => { if (!container.contains(document.activeElement)) { this.navigation.performTab(container, { row: rowIndex, column: lastColIndex }); } }); } } else if (key === 'arrowup' && !ctrl && target === container) { this.navigation.navigateUp(container, { row: rowIndex, column: colIndex }); } else if (key === 'arrowup' && ctrl && target === container) { this.navigation.navigateTop(colIndex); } else if (key === 'arrowdown' && !ctrl && target === container) { this.navigation.navigateDown(container, { row: rowIndex, column: colIndex }); } else if (key === 'arrowdown' && ctrl && target === container) { this.navigation.navigateBottom(colIndex); } } get hasDetails() { return !!this.gridDetailsTemplate; } /** * @hidden */ getRowTemplate(rowData) { if (this.isGroupByRecord(rowData)) { return this.defaultGroupTemplate; } else if (this.isSummaryRow(rowData)) { return this.summaryTemplate; } else if (this.hasDetails && this.isDetailRecord(rowData)) { return this.detailTemplateContainer; } else { return this.recordTemplate; } } isDetailRecord(record) { return record.detailsData !== undefined; } /** * @hidden */ get groupAreaHostClass() { return this.getComponentDensityClass('igx-drop-area'); } /** * Returns the template reference of the `IgxGridComponent`'s group row. * ``` * const groupRowTemplate = this.grid.groupRowTemplate; * ``` * @memberof IgxGridComponent */ get groupRowTemplate() { return this._groupRowTemplate; } /** * Sets the template reference of the `IgxGridComponent`'s group `IgxGridRowComponent`. * ```typescript * this.grid.groupRowTemplate = myRowTemplate. * ``` * @memberof IgxGridComponent */ set groupRowTemplate(template) { this._groupRowTemplate = template; this.notifyChanges(); } /** * Returns the template reference of the `IgxGridComponent`'s group area. * ```typescript * const groupAreaTemplate = this.grid.groupAreaTemplate; * ``` * @memberof IgxGridComponent */ get groupAreaTemplate() { return this._groupAreaTemplate; } /** * Sets the template reference of the `IgxGridComponent`'s group area. * ```typescript * this.grid.groupAreaTemplate = myAreaTemplate. * ``` * @memberof IgxGridComponent */ set groupAreaTemplate(template) { this._groupAreaTemplate = template; this.notifyChanges(); } /** * Groups by a new `IgxColumnComponent` based on the provided expression, or modifies an existing one. * Also allows for multiple columns to be grouped at once if an array of `ISortingExpression` is passed. * The onGroupingDone event would get raised only **once** if this method gets called multiple times with the same arguments. * ```typescript * this.grid.groupBy({ fieldName: name, dir: SortingDirection.Asc, ignoreCase: false }); * this.grid.groupBy([ { fieldName: name1, dir: SortingDirection.Asc, ignoreCase: false }, { fieldName: name2, dir: SortingDirection.Desc, ignoreCase: true }, { fieldName: name3, dir: SortingDirection.Desc, ignoreCase: false } ]); * ``` * @memberof IgxGridComponent */ groupBy(expression) { if (this.checkIfNoColumnField(expression)) { return; } this.endEdit(true); if (expression instanceof Array) { this._gridAPI.groupBy_multiple(expression); } else { this._gridAPI.groupBy(expression); } this.notifyChanges(true); } /** * Clears all grouping in the grid, if no parameter is passed. * If a parameter is provided, clears grouping for a particular column or an array of columns. * ```typescript * this.grid.clearGrouping(); //clears all grouping * this.grid.clearGrouping("ID"); //ungroups a single column * this.grid.clearGrouping(["ID", "Column1", "Column2"]); //ungroups multiple columns * ``` * */ clearGrouping(name) { this._gridAPI.clear_groupby(name); this.notifyChanges(true); } /** * Returns if a group is expanded or not. * ```typescript * public groupRow: IGroupByRecord; * const expandedGroup = this.grid.isExpandedGroup(this.groupRow); * ``` * @memberof IgxGridComponent */ isExpandedGroup(group) { const state = this._getStateForGroupRow(group); return state ? state.expanded : this.groupsExpanded; } /** * Toggles the expansion state of a group. * ```typescript * public groupRow: IGroupByRecord; * const toggleExpGroup = this.grid.toggleGroup(this.groupRow); * ``` * @memberof IgxGridComponent */ toggleGroup(groupRow) { this._toggleGroup(groupRow); this.notifyChanges(); } /** * Expands the specified group and all of its parent groups. * ```typescript * public groupRow: IGroupByRecord; * this.grid.fullyExpandGroup(this.groupRow); * ``` * @memberof IgxGridComponent */ fullyExpandGroup(groupRow) { this._fullyExpandGroup(groupRow); this.notifyChanges(); } /** * @hidden */ isGroupByRecord(record) { // return record.records instance of GroupedRecords fails under Webpack return record.records && record.records.length; } /** * Toggles the expansion state of all group rows recursively. * ```typescript * this.grid.toggleAllGroupRows; * ``` * @memberof IgxGridComponent */ toggleAllGroupRows() { this.groupingExpansionState = []; this.groupsExpanded = !this.groupsExpanded; this.notifyChanges(); } /** * Returns if the `IgxGridComponent` has groupable columns. * ```typescript * const groupableGrid = this.grid.hasGroupableColumns; * ``` * @memberof IgxGridComponent */ get hasGroupableColumns() { return this.columnList.some((col) => col.groupable && !col.columnGroup); } _setGroupColsVisibility(value) { if (this.columnList && !this.hasColumnLayouts) { this.groupingExpressions.forEach((expr) => { const col = this.getColumnByName(expr.fieldName); col.hidden = value; }); } } /** * Returns if the grid's group by drop area is visible. * ```typescript * const dropVisible = this.grid.dropAreaVisible; * ``` * @memberof IgxGridComponent */ get dropAreaVisible() { return (this.draggedColumn && this.draggedColumn.groupable) || !this.chipsGoupingExpressions.length; } /** * @hidden */ _getStateForGroupRow(groupRow) { return this._gridAPI.groupBy_get_expanded_for_group(groupRow); } /** * @hidden */ _toggleGroup(groupRow) { this._gridAPI.groupBy_toggle_group(groupRow); } /** * @hidden */ _fullyExpandGroup(groupRow) { this._gridAPI.groupBy_fully_expand_group(groupRow); } /** * @hidden */ _applyGrouping() { this._gridAPI.sort_multiple(this._groupingExpressions); } /** * @hidden */ isColumnGrouped(fieldName) { return this.groupingExpressions.find(exp => exp.fieldName === fieldName) ? true : false; } /** * @hidden */ getContext(rowData, rowIndex) { if (this.isDetailRecord(rowData)) { const cachedData = this.childDetailTemplates.get(rowData.detailsData); const rowID = this.primaryKey ? rowData.detailsData[this.primaryKey] : this.data.indexOf(rowData.detailsData); if (cachedData) { const view = cachedData.view; const tmlpOutlet = cachedData.owner; return { $implicit: rowData.detailsData, moveView: view, owner: tmlpOutlet, index: this.dataView.indexOf(rowData), templateID: 'detailRow-' + rowID }; } else { // child rows contain unique grids, hence should have unique templates return { $implicit: rowData.detailsData, templateID: 'detailRow-' + rowID, index: this.dataView.indexOf(rowData) }; } } return { $implicit: rowData, index: rowIndex, templateID: this.isGroupByRecord(rowData) ? 'groupRow' : this.isSummaryRow(rowData) ? 'summaryRow' : 'dataRow' }; } /** * @hidden */ viewCreatedHandler(args) { if (args.context.templateID.indexOf('detailRow') !== -1) { this.childDetailTemplates.set(args.context.$implicit, args); } } /** * @hidden */ viewMovedHandler(args) { if (args.context.templateID.indexOf('detailRow') !== -1) { // view was moved, update owner in cache const key = args.context.$implicit; const cachedData = this.childDetailTemplates.get(key); cachedData.owner = args.owner; } } /** * @hidden */ get template() { if (this.filteredData && this.filteredData.length === 0) { return this.emptyGridTemplate ? this.emptyGridTemplate : this.emptyFilteredGridTemplate; } if (this.isLoading && (!this.data || this.dataLength === 0)) { return this.loadingGridTemplate ? this.loadingGridTemplate : this.loadingGridDefaultTemplate; } if (this.dataLength === 0) { return this.emptyGridTemplate ? this.emptyGridTemplate : this.emptyGridDefaultTemplate; } } /** * @hidden */ onChipRemoved(event) { this.clearGrouping(event.owner.id); } /** * @hidden */ chipsOrderChanged(event) { const newGrouping = []; for (let i = 0; i < event.chipsArray.length; i++) { const expr = this.groupingExpressions.filter((item) => { return item.fieldName === event.chipsArray[i].id; })[0]; if (!this.getColumnByName(expr.fieldName).groupable) { // disallow changing order if there are columns with groupable: false return; } newGrouping.push(expr); } this.groupingExpansionState = []; this.chipsGoupingExpressions = newGrouping; if (event.originalEvent instanceof KeyboardEvent) { // When reordered using keyboard navigation, we don't have `onMoveEnd` event. this.groupingExpressions = this.chipsGoupingExpressions; } this.notifyChanges(); } /** * @hidden */ chipsMovingEnded() { this.groupingExpressions = this.chipsGoupingExpressions; this.notifyChanges(); } /** * @hidden */ onChipClicked(event) { const sortingExpr = this.sortingExpressions; const columnExpr = sortingExpr.find((expr) => expr.fieldName === event.owner.id); columnExpr.dir = 3 - columnExpr.dir; this.sort(columnExpr); this.notifyChanges(); } /** * @hidden */ onChipKeyDown(event) { if (event.originalEvent.key === ' ' || event.originalEvent.key === 'Spacebar' || event.originalEvent.key === 'Enter') { const sortingExpr = this.sortingExpressions; const columnExpr = sortingExpr.find((expr) => expr.fieldName === event.owner.id); columnExpr.dir = 3 - columnExpr.dir; this.sort(columnExpr); this.notifyChanges(); } } /** * @hidden */ get defaultTargetBodyHeight() { const allItems = this.totalItemCount || this.dataLength; return this.renderedRowHeight * Math.min(this._defaultTargetRecordNumber, this.paging ? Math.min(allItems, this.perPage) : allItems); } /** * @hidden */ getGroupAreaHeight() { return this.groupArea ? this.groupArea.nativeElement.offsetHeight : 0; } /** * @hidden * Gets the combined width of the columns that are specific to the enabled grid features. They are fixed. * TODO: Remove for Angular 8. Calling parent class getter using super is not supported for now. */ getFeatureColumnsWidth() { let width = super.getFeatureColumnsWidth(); if (this.groupingExpressions.length && this.headerGroupContainer) { width += this.headerGroupContainer.nativeElement.offsetWidth; } return width; } /** * @hidden */ scrollTo(row, column) { if (this.groupingExpressions && this.groupingExpressions.length && typeof (row) !== 'number') { const rowIndex = this.groupingResult.indexOf(row); const groupByRecord = this.groupingMetadata[rowIndex]; if (groupByRecord) { this._fullyExpandGroup(groupByRecord); } } super.scrollTo(row, column, this.groupingFlatResult); } /** * @hidden */ get dropAreaTemplateResolved() { if (this.dropAreaTemplate) { return this.dropAreaTemplate; } else { return this.defaultDropAreaTemplate; } } /** * @hidden */ getGroupByChipTitle(expression) { const column = this.getColumnByName(expression.fieldName); return (column && column.header) || expression.fieldName; } /** * @hidden */ get iconTemplate() { if (this.groupsExpanded) { return this.headerExpandIndicatorTemplate || this.defaultExpandedTemplate; } else { return this.headerCollapseIndicatorTemplate || this.defaultCollapsedTemplate; } } /** * @hidden */ getColumnGroupable(fieldName) { const column = this.getColumnByName(fieldName); return column && column.groupable; } /** * @hidden */ ngAfterContentInit() { super.ngAfterContentInit(); if (this.allowFiltering && this.hasColumnLayouts) { this.filterMode = FilterMode.excelStyleFilter; } if (this.groupTemplate) { this._groupRowTemplate = this.groupTemplate.template; } if (this.hideGroupedColumns && this.columnList && this.groupingExpressions) { this._setGroupColsVisibility(this.hideGroupedColumns); } this._setupNavigationService(); } ngAfterViewInit() { super.ngAfterViewInit(); this.verticalScrollContainer.onBeforeViewDestroyed.pipe(takeUntil(this.destroy$)).subscribe((view) => { const rowData = view.context.$implicit; if (this.isDetailRecord(rowData)) { const cachedData = this.childDetailTemplates.get(rowData.detailsData); if (cachedData) { const tmlpOutlet = cachedData.owner; tmlpOutlet._viewContainerRef.detach(0); } } }); } ngOnInit() { super.ngOnInit(); this.onGroupingDone.pipe(takeUntil(this.destroy$)).subscribe((args) => { this.endEdit(true); this.summaryService.updateSummaryCache(args); }); } ngDoCheck() { if (this.groupingDiffer && this.columnList && !this.hasColumnLayouts) { const changes = this.groupingDiffer.diff(this.groupingExpressions); if (changes && this.columnList) { changes.forEachAddedItem((rec) => { const col = this.getColumnByName(rec.item.fieldName); col.hidden = true; }); changes.forEachRemovedItem((rec) => { const col = this.getColumnByName(rec.item.fieldName); col.hidden = false; }); } } super.ngDoCheck(); } /** * @inheritdoc */ getSelectedData(formatters = false, headers = false) { if (this.groupingExpressions.length) { const source = []; const process = (record) => { if (record.expression || record.summaries) { source.push(null); return; } source.push(record); }; this.dataView.forEach(process); return this.extractDataFromSelection(source, formatters, headers); } else { return super.getSelectedData(formatters, headers); } } _setupNavigationService() { if (this.hasColumnLayouts) { this.navigation = new IgxGridMRLNavigationService(); this.navigation.grid = this; } } checkIfNoColumnField(expression) { if (expression instanceof Array) { for (const singleExpression of expression) { if (!singleExpression.fieldName) { return true; } } return false; } return !expression.fieldName; } }; __decorate([ HostBinding('attr.id'), Input(), __metadata("design:type", String), __metadata("design:paramtypes", [String]) ], IgxGridComponent.prototype, "id", null); __decorate([ Input(), __metadata("design:type", Array), __metadata("design:paramtypes", [Array]) ], IgxGridComponent.prototype, "data", null); __decorate([ Input(), __metadata("design:type", Array), __metadata("design:paramtypes", [Array]) ], IgxGridComponent.prototype, "groupingExpressions", null); __decorate([ Output(), __metadata("design:type", Object) ], IgxGridComponent.prototype, "groupingExpressionsChange", void 0); __decorate([ Input(), __metadata("design:type", Object), __metadata("design:paramtypes", [Object]) ], IgxGridComponent.prototype, "groupingExpansionState", null); __decorate([ Output(), __metadata("design:type", Object) ], IgxGridComponent.prototype, "groupingExpansionStateChange", void 0); __decorate([ Input(), __metadata("design:type", Object) ], IgxGridComponent.prototype, "groupsExpanded", void 0); __decorate([ Input(), __metadata("design:type", Boolean), __metadata("design:paramtypes", [Boolean]) ], IgxGridComponent.prototype, "hideGroupedColumns", null); __decorate([ Input(), __metadata("design:type", String), __metadata("design:paramtypes", [String]) ], IgxGridComponent.prototype, "dropAreaMessage", null); __decorate([ Input(), __metadata("design:type", TemplateRef) ], IgxGridComponent.prototype, "dropAreaTemplate", void 0); __decorate([ Output(), __metadata("design:type", Object) ], IgxGridComponent.prototype, "onGroupingDone", void 0); __decorate([ ContentChild(IgxGroupByRowTemplateDirective, { read: IgxGroupByRowTemplateDirective }), __metadata("design:type", IgxGroupByRowTemplateDirective) ], IgxGridComponent.prototype, "groupTemplate", void 0); __decorate([ ContentChild(IgxGridDetailTemplateDirective, { read: IgxGridDetailTemplateDirective, static: false }), __metadata("design:type", IgxGridDetailTemplateDirective) ], IgxGridComponent.prototype, "gridDetailsTemplate", void 0); __decorate([ ViewChildren(IgxGridGroupByRowComponent, { read: IgxGridGroupByRowComponent }), __metadata("design:type", QueryList) ], IgxGridComponent.prototype, "_groupsRowList", void 0); __decorate([ ViewChild('defaultDropArea', { read: TemplateRef, static: true }), __metadata("design:type", TemplateRef) ], IgxGridComponent.prototype, "defaultDropAreaTemplate", void 0); __decorate([ ViewChild('groupArea'), __metadata("design:type", ElementRef) ], IgxGridComponent.prototype, "groupArea", void 0); __decorate([ ViewChild('record_template', { read: TemplateRef, static: true }), __metadata("design:type", TemplateRef) ], IgxGridComponent.prototype, "recordTemplate", void 0); __decorate([ ViewChild('detail_template_container', { read: TemplateRef, static: true }), __metadata("design:type", TemplateRef) ], IgxGridComponent.prototype, "detailTemplateContainer", void 0); __decorate([ ContentChild(IgxGridDetailTemplateDirective, { read: TemplateRef, static: false }), __metadata("design:type", TemplateRef) ], IgxGridComponent.prototype, "detailTemplate", void 0); __decorate([ ViewChild('group_template', { read: TemplateRef, static: true }), __metadata("design:type", TemplateRef) ], IgxGridComponent.prototype, "defaultGroupTemplate", void 0); __decorate([ ViewChild('summary_template', { read: TemplateRef, static: true }), __metadata("design:type", TemplateRef) ], IgxGridComponent.prototype, "summaryTemplate", void 0); __decorate([ Input(), __metadata("design:type", Object), __metadata("design:paramtypes", [Object]) ], IgxGridComponent.prototype, "expansionStates", null); __decorate([ Output(), __metadata("design:type", Object) ], IgxGridComponent.prototype, "expansionStatesChange", void 0); __decorate([ Output(), __metadata("design:type", Object) ], IgxGridComponent.prototype, "_focusIn", void 0); __decorate([ HostListener('focusin'), __metadata("design:type", Function), __metadata("design:paramtypes", []), __metadata("design:returntype", void 0) ], IgxGridComponent.prototype, "onFocusIn", null); IgxGridComponent = IgxGridComponent_1 = __decorate([ Component({ changeDetection: ChangeDetectionStrategy.OnPush, preserveWhitespaces: false, providers: [ IgxGridNavigationService, IgxGridSummaryService, IgxGridSelectionService, IgxGridCRUDService, { provide: GridBaseAPIService, useClass: IgxGridAPIService }, { provide: IgxGridBaseDirective, useExisting: forwardRef(() => IgxGridComponent_1) }, IgxFilteringService, IgxColumnResizingService, IgxForOfSyncService, IgxForOfScrollSyncService, IgxRowIslandAPIService ], selector: 'igx-grid', template: "<igx-grid-toolbar [style.flex-basis.px]=\"outerWidth\" role=\"rowgroup\" *ngIf=\"showToolbar\" [gridID]=\"id\"\n [displayDensity]=\"displayDensity\" #toolbar>\n</igx-grid-toolbar>\n\n<div [style.flex-basis.px]='outerWidth' class=\"igx-grid__grouparea\"\n *ngIf=\"groupingExpressions.length > 0 || hasGroupableColumns\" #groupArea>\n <igx-chips-area (onReorder)=\"chipsOrderChanged($event)\" (onMoveEnd)=\"chipsMovingEnded()\">\n <ng-container *ngFor=\"let expr of chipsGoupingExpressions; let last = last;\">\n <igx-chip [id]=\"expr.fieldName\" [attr.title]=\"getGroupByChipTitle(expr)\"\n [removable]=\"getColumnGroupable(expr.fieldName)\"\n [draggable]=\"getColumnGroupable(expr.fieldName)\" [displayDensity]=\"displayDensity\"\n (onKeyDown)=\"onChipKeyDown($event)\" (onRemove)=\"onChipRemoved($event)\"\n (onClick)=\"getColumnGroupable(expr.fieldName) ? onChipClicked($event): null\"\n [disabled]='!getColumnGroupable(expr.fieldName)'>\n <span>{{ getGroupByChipTitle(expr) }}</span>\n <igx-icon igxSuffix>{{ expr.dir == 1 ? 'arrow_upward' : 'arrow_downward' }}</igx-icon>\n </igx-chip>\n <span class=\"igx-grid__grouparea-connector\">\n <igx-icon [style.visibility]=\"(!last || dropAreaVisible) ? 'visible' : 'hidden'\">arrow_forward\n </igx-icon>\n </span>\n </ng-container>\n <div igxGroupAreaDrop [style.visibility]=\"dropAreaVisible ? 'visible' : 'hidden'\" [class]=\"groupAreaHostClass\"\n [attr.gridId]='this.id'>\n <ng-container *ngTemplateOutlet=\"dropAreaTemplateResolved\"></ng-container>\n </div>\n </igx-chips-area>\n</div>\n\n<div class=\"igx-grid__thead\">\n <div class=\"igx-grid__thead-wrapper\" [class.igx-grid__tr--mrl]='hasColumnLayouts' role=\"rowgroup\"\n [style.width.px]='calcWidth' #theadRow>\n <div class=\"igx-grid__tr\" role=\"row\" [style.width.px]='calcWidth'>\n <span *ngIf=\"hasMovableColumns && draggedColumn && pinnedColumns.length <= 0\"\n [igxColumnMovingDrop]=\"headerContainer\" [attr.droppable]=\"true\" id=\"left\"\n class=\"igx-grid__scroll-on-drag-left\" [style.left.px]=\"featureColumnsWidth\"></span>\n <span *ngIf=\"hasMovableColumns && draggedColumn && pinnedColumns.length > 0\"\n [igxColumnMovingDrop]=\"headerContainer\" [attr.droppable]=\"true\" id=\"left\"\n class=\"igx-grid__scroll-on-drag-pinned\" [style.left.px]=\"pinnedWidth\"></span>\n\n <ng-container *ngIf=\"groupingExpressions.length > 0\">\n <div class=\"igx-grid__header-indentation igx-grid__row-indentation--level-{{groupingExpressions.length}}\"\n [ngClass]=\"{\n 'igx-grid__header-indentation--no-border': isRowSelectable || rowDraggable\n }\" #headerGroupContainer (click)=\"toggleAllGroupRows()\">\n\n <ng-container *ngTemplateOutlet=\"iconTemplate; context: { $implicit: this }\"></ng-container>\n </div>\n </ng-container>\n <ng-container *ngIf=\"rowDraggable\">\n <div class=\"igx-grid__drag-indicator\" [ngClass]=\"{\n 'igx-grid__drag-indicator--header': !isRowSelectable\n }\" #headerDragContainer>\n <div style=\"visibility: hidden;\">\n <ng-container\n *ngTemplateOutlet=\"this.dragIndicatorIconTemplate ? this.dragIndicatorIconTemplate : dragIndicatorIconBase\">\n </ng-container>\n </div>\n </div>\n </ng-container>\n <ng-container *ngIf=\"showRowSelectors\">\n <div class=\"igx-grid__cbx-selection\" (click)=\"onHeaderSelectorClick($event)\" #headerSelectorContainer [ngClass]=\"{\n 'igx-grid__cbx-selection--push': filteringService.isFilterRowVisible\n }\">\n <ng-template #headSelector\n *ngTemplateOutlet=\"\n this.headSelectorTemplate ? this.headSelectorTemplate : headSelectorBaseTemplate;\n context: { $implicit: {\n selectedCount: this.selectionService.filteredSelectedRowIds.length,\n totalCount: this.totalRowsCountAfterFilter }}\">\n </ng-template>\n </div>\n </ng-container>\n <ng-container *ngIf=\"pinnedColumns.length > 0\">\n <ng-template ngFor let-col [ngForOf]=\"pinnedColumns | igxTopLevel\">\n <igx-grid-header-group [column]=\"col\" [gridID]=\"id\" [style.min-width]=\"getHeaderGroupWidth(col)\"\n [style.flex-basis]=\"getHeaderGroupWidth(col)\"></igx-grid-header-group>\n </ng-template>\n </ng-container>\n <ng-template igxGridFor let-col [igxGridForOf]=\"unpinnedColumns | igxTopLevel\"\n [igxForScrollOrientation]=\"'horizontal'\" [igxForScrollContainer]=\"parentVirtDir\"\n [igxForContainerSize]='unpinnedWidth' [igxForTrackBy]='trackColumnChanges'\n [igxForSizePropName]='\"calcPixelWidth\"' #hContainer>\n <igx-grid-header-group [column]=\"col\" [gridID]=\"id\" [style.min-width]=\"getHeaderGroupWidth(col)\"\n [style.flex-basis]=\"getHeaderGroupWidth(col)\"></igx-grid-header-group>\n </ng-template>\n </div>\n <igx-grid-filtering-row #filteringRow [style.width.px]='calcWidth' *ngIf=\"filteringService.isFilterRowVisible\"\n [column]=\"filteringService.filteredColumn\"></igx-grid-filtering-row>\n </div>\n <span *ngIf=\"hasMovableColumns && draggedColumn\" [igxColumnMovingDrop]=\"headerContainer\" [attr.droppable]=\"true\"\n id=\"right\" class=\"igx-grid__scroll-on-drag-right\"></span>\n <div class=\"igx-grid__thead-thumb\" [hidden]='!hasVerticalSroll()' [style.width.px]=\"scrollWidth\"></div>\n</div>\n\n<div igxGridBody (keydown.control.c)=\"copyHandlerIE()\" (copy)=\"copyHandler($event)\" class=\"igx-grid__tbody\">\n <div class=\"igx-grid__tbody-content\" role=\"rowgroup\" (onDragStop)=\"selectionService.dragMode = $event\" (scroll)='preventContainerScroll($event)'\n (onDragScroll)=\"dragScroll($event)\" [igxGridDragSelect]=\"selectio