igniteui-angular-sovn
Version:
Ignite UI for Angular is a dependency-free Angular toolkit for building modern web apps
1,728 lines (1,646 loc) • 81 kB
text/typescript
import { Subject } from 'rxjs';
import {
AfterContentInit,
ChangeDetectorRef,
ChangeDetectionStrategy,
Component,
ContentChild,
ContentChildren,
Input,
QueryList,
TemplateRef,
Output,
EventEmitter,
OnDestroy,
Inject,
Optional,
Self,
} from '@angular/core';
import { notifyChanges } from '../watch-changes';
import { WatchColumnChanges } from '../watch-changes';
import { GridColumnDataType } from '../../data-operations/data-util';
import {
IgxFilteringOperand,
IgxBooleanFilteringOperand,
IgxNumberFilteringOperand,
IgxDateFilteringOperand,
IgxStringFilteringOperand,
IgxDateTimeFilteringOperand,
IgxTimeFilteringOperand
} from '../../data-operations/filtering-condition';
import { ISortingStrategy, DefaultSortingStrategy } from '../../data-operations/sorting-strategy';
import { DisplayDensity } from '../../core/density';
import { IgxRowDirective } from '../row.directive';
import { FilteringExpressionsTree } from '../../data-operations/filtering-expressions-tree';
import { CellType, ColumnType, GridType, IgxCellTemplateContext, IgxColumnTemplateContext, IgxSummaryTemplateContext, IGX_GRID_BASE } from '../common/grid.interface';
import { IgxGridHeaderComponent } from '../headers/grid-header.component';
import { IgxGridFilteringCellComponent } from '../filtering/base/grid-filtering-cell.component';
import { IgxGridHeaderGroupComponent } from '../headers/grid-header-group.component';
import {
IgxSummaryOperand, IgxNumberSummaryOperand, IgxDateSummaryOperand,
IgxSummaryResult, IgxTimeSummaryOperand
} from '../summaries/grid-summary';
import {
IgxCellTemplateDirective,
IgxCellHeaderTemplateDirective,
IgxCellEditorTemplateDirective,
IgxCollapsibleIndicatorTemplateDirective,
IgxFilterCellTemplateDirective,
IgxSummaryTemplateDirective,
IgxCellValidationErrorDirective
} from './templates.directive';
import { MRLResizeColumnInfo, MRLColumnSizeInfo, IColumnPipeArgs } from './interfaces';
import { DropPosition } from '../moving/moving.service';
import { IColumnVisibilityChangingEventArgs, IPinColumnCancellableEventArgs, IPinColumnEventArgs } from '../common/events';
import { isConstructor, PlatformUtil } from '../../core/utils';
import { IgxGridCell } from '../grid-public-cell';
import { NG_VALIDATORS, Validator } from '@angular/forms';
const DEFAULT_DATE_FORMAT = 'mediumDate';
const DEFAULT_TIME_FORMAT = 'mediumTime';
const DEFAULT_DATE_TIME_FORMAT = 'medium';
const DEFAULT_DIGITS_INFO = '1.0-3';
/**
* **Ignite UI for Angular Column** -
* [Documentation](https://www.infragistics.com/products/ignite-ui-angular/angular/components/grid/grid#columns-configuration)
*
* The Ignite UI Column is used within an `igx-grid` element to define what data the column will show. Features such as sorting,
* filtering & editing are enabled at the column level. You can also provide a template containing custom content inside
* the column using `ng-template` which will be used for all cells within the column.
*/
@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
selector: 'igx-column',
template: ``,
standalone: true
})
export class IgxColumnComponent implements AfterContentInit, OnDestroy, ColumnType {
/**
* Sets/gets the `field` value.
* ```typescript
* let columnField = this.column.field;
* ```
* ```html
* <igx-column [field] = "'ID'"></igx-column>
* ```
*
* @memberof IgxColumnComponent
*/
@Input()
public set field(value: string) {
this._field = value;
this.hasNestedPath = value?.includes('.');
}
public get field(): string {
return this._field;
}
/**
* @hidden @internal
*/
public validators: Validator[] = [];
/**
* Sets/gets the `header` value.
* ```typescript
* let columnHeader = this.column.header;
* ```
* ```html
* <igx-column [header] = "'ID'"></igx-column>
* ```
*
* @memberof IgxColumnComponent
*/
@notifyChanges()
@WatchColumnChanges()
@Input()
public header = '';
/**
* Sets/gets the `title` value.
* ```typescript
* let title = this.column.title;
* ```
* ```html
* <igx-column [title] = "'Some column tooltip'"></igx-column>
* ```
*
* @memberof IgxColumnComponent
*/
@notifyChanges()
@WatchColumnChanges()
@Input()
public title = '';
/**
* Sets/gets whether the column is sortable.
* Default value is `false`.
* ```typescript
* let isSortable = this.column.sortable;
* ```
* ```html
* <igx-column [sortable] = "true"></igx-column>
* ```
*
* @memberof IgxColumnComponent
*/
@WatchColumnChanges()
@Input()
public sortable = false;
/**
* Returns if the column is selectable.
* ```typescript
* let columnSelectable = this.column.selectable;
* ```
*
* @memberof IgxColumnComponent
*/
@WatchColumnChanges()
@Input()
public get selectable(): boolean {
return this._selectable;
}
/**
* Sets if the column is selectable.
* Default value is `true`.
* ```html
* <igx-column [selectable] = "false"></igx-column>
* ```
*
* @memberof IgxColumnComponent
*/
public set selectable(value: boolean) {
this._selectable = value;
}
/**
* Sets/gets whether the column is groupable.
* Default value is `false`.
* ```typescript
* let isGroupable = this.column.groupable;
* ```
* ```html
* <igx-column [groupable] = "true"></igx-column>
* ```
*
* @memberof IgxColumnComponent
*/
@notifyChanges(true)
@WatchColumnChanges()
@Input()
public get groupable(): boolean {
return this._groupable;
}
public set groupable(value: boolean) {
this._groupable = value;
this.grid.groupablePipeTrigger++;
}
/**
* Gets whether the column is editable.
* Default value is `false`.
* ```typescript
* let isEditable = this.column.editable;
* ```
*
* @memberof IgxColumnComponent
*/
@WatchColumnChanges()
@Input()
public get editable(): boolean {
// Updating the primary key when grid has transactions (incl. row edit)
// should not be allowed, as that can corrupt transaction state.
const rowEditable = this.grid && this.grid.rowEditable;
const hasTransactions = this.grid && this.grid.transactions.enabled;
if (this.isPrimaryColumn && (rowEditable || hasTransactions)) {
return false;
}
if (this._editable !== undefined) {
return this._editable;
} else {
return rowEditable;
}
}
/**
* Sets whether the column is editable.
* ```typescript
* this.column.editable = true;
* ```
* ```html
* <igx-column [editable] = "true"></igx-column>
* ```
*
* @memberof IgxColumnComponent
*/
public set editable(editable: boolean) {
this._editable = editable;
}
/**
* Sets/gets whether the column is filterable.
* Default value is `true`.
* ```typescript
* let isFilterable = this.column.filterable;
* ```
* ```html
* <igx-column [filterable] = "false"></igx-column>
* ```
*
* @memberof IgxColumnComponent
*/
@notifyChanges()
@WatchColumnChanges()
@Input()
public filterable = true;
/**
* Sets/gets whether the column is resizable.
* Default value is `false`.
* ```typescript
* let isResizable = this.column.resizable;
* ```
* ```html
* <igx-column [resizable] = "true"></igx-column>
* ```
*
* @memberof IgxColumnComponent
*/
@WatchColumnChanges()
@Input()
public resizable = false;
/**
* Sets/gets whether the column header is included in autosize logic.
* Useful when template for a column header is sized based on parent, for example a default `div`.
* Default value is `false`.
* ```typescript
* let isResizable = this.column.resizable;
* ```
* ```html
* <igx-column [resizable] = "true"></igx-column>
* ```
*
* @memberof IgxColumnComponent
*/
@WatchColumnChanges()
@Input()
public autosizeHeader = true;
/**
* Gets a value indicating whether the summary for the column is enabled.
* ```typescript
* let hasSummary = this.column.hasSummary;
* ```
*
* @memberof IgxColumnComponent
*/
@notifyChanges(true)
@WatchColumnChanges()
@Input()
public get hasSummary() {
return this._hasSummary;
}
/**
* Sets a value indicating whether the summary for the column is enabled.
* Default value is `false`.
* ```html
* <igx-column [hasSummary] = "true"></igx-column>
* ```
*
* @memberof IgxColumnComponent
*/
public set hasSummary(value) {
this._hasSummary = value;
if (this.grid) {
this.grid.summaryService.resetSummaryHeight();
}
}
/**
* Gets whether the column is hidden.
* ```typescript
* let isHidden = this.column.hidden;
* ```
*
* @memberof IgxColumnComponent
*/
@notifyChanges(true)
@WatchColumnChanges()
@Input()
public get hidden(): boolean {
return this._hidden;
}
/**
* Sets the column hidden property.
* Default value is `false`.
* ```html
* <igx-column [hidden] = "true"></igx-column>
* ```
*
* Two-way data binding.
* ```html
* <igx-column [(hidden)] = "model.isHidden"></igx-column>
* ```
*
* @memberof IgxColumnComponent
*/
public set hidden(value: boolean) {
if (this._hidden !== value) {
this._hidden = value;
this.hiddenChange.emit(this._hidden);
if (this.columnLayoutChild && this.parent.hidden !== value) {
this.parent.hidden = value;
return;
}
if (this.grid) {
this.grid.crudService.endEdit(false);
this.grid.summaryService.resetSummaryHeight();
this.grid.filteringService.refreshExpressions();
this.grid.filteringService.hideFilteringRowOnColumnVisibilityChange(this);
this.grid.notifyChanges();
}
}
}
/**
* Returns if the column is selected.
* ```typescript
* let isSelected = this.column.selected;
* ```
*
* @memberof IgxColumnComponent
*/
public get selected(): boolean {
return this.grid.selectionService.isColumnSelected(this.field);
}
/**
* Select/deselect a column.
* Default value is `false`.
* ```typescript
* this.column.selected = true;
* ```
*
* @memberof IgxColumnComponent
*/
public set selected(value: boolean) {
if (this.selectable && value !== this.selected) {
if (value) {
this.grid.selectionService.selectColumnsWithNoEvent([this.field]);
} else {
this.grid.selectionService.deselectColumnsWithNoEvent([this.field]);
}
this.grid.notifyChanges();
}
}
/**
* @hidden
*/
@Output()
public hiddenChange = new EventEmitter<boolean>();
/** @hidden */
@Output()
public expandedChange = new EventEmitter<boolean>();
/** @hidden */
@Output()
public collapsibleChange = new EventEmitter<boolean>();
/** @hidden */
@Output()
public visibleWhenCollapsedChange = new EventEmitter<boolean>();
/** @hidden */
@Output()
public columnChange = new EventEmitter<void>();
/**
* Gets whether the hiding is disabled.
* ```typescript
* let isHidingDisabled = this.column.disableHiding;
* ```
*
* @memberof IgxColumnComponent
*/
@notifyChanges()
@WatchColumnChanges()
@Input()
public disableHiding = false;
/**
* Gets whether the pinning is disabled.
* ```typescript
* let isPinningDisabled = this.column.disablePinning;
* ```
*
* @memberof IgxColumnComponent
*/
@notifyChanges()
@WatchColumnChanges()
@Input()
public disablePinning = false;
/**
* @deprecated in version 13.1.0. Use `IgxGridComponent.moving` instead.
*
* Sets/gets whether the column is movable.
* Default value is `false`.
*
* ```typescript
* let isMovable = this.column.movable;
* ```
* ```html
* <igx-column [movable] = "true"></igx-column>
* ```
*
* @memberof IgxColumnComponent
*/
@Input()
public movable = false;
/**
* Gets the `width` of the column.
* ```typescript
* let columnWidth = this.column.width;
* ```
*
* @memberof IgxColumnComponent
*/
@notifyChanges(true)
@WatchColumnChanges()
@Input()
public get width(): string {
const isAutoWidth = this._width && typeof this._width === 'string' && this._width === 'auto';
if (isAutoWidth) {
if (!this.autoSize) {
return 'fit-content';
} else {
return this.autoSize + 'px';
}
}
return this.widthSetByUser ? this._width : this.defaultWidth;
}
/**
* Sets the `width` of the column.
* ```html
* <igx-column [width] = "'25%'"></igx-column>
* ```
*
* Two-way data binding.
* ```html
* <igx-column [(width)]="model.columns[0].width"></igx-column>
* ```
*
* @memberof IgxColumnComponent
*/
public set width(value: string) {
if (value) {
this._calcWidth = null;
this.calcPixelWidth = NaN;
this.widthSetByUser = true;
// width could be passed as number from the template
// host bindings are not px affixed so we need to ensure we affix simple number strings
if (typeof (value) === 'number' || value.match(/^[0-9]*$/)) {
value = value + 'px';
}
if (value === 'fit-content') {
value = 'auto';
}
this._width = value;
if (this.grid) {
this.cacheCalcWidth();
}
this.widthChange.emit(this._width);
}
}
/** @hidden @internal **/
public autoSize: number;
/**
* Sets/gets the maximum `width` of the column.
* ```typescript
* let columnMaxWidth = this.column.width;
* ```
* ```html
* <igx-column [maxWidth] = "'150px'"></igx-column>
* ```
*
* @memberof IgxColumnComponent
*/
@WatchColumnChanges()
@Input()
public maxWidth: string;
/**
* Sets/gets the class selector of the column header.
* ```typescript
* let columnHeaderClass = this.column.headerClasses;
* ```
* ```html
* <igx-column [headerClasses] = "'column-header'"></igx-column>
* ```
*
* @memberof IgxColumnComponent
*/
@notifyChanges()
@WatchColumnChanges()
@Input()
public headerClasses = '';
/**
* Sets conditional style properties on the column header.
* Similar to `ngStyle` it accepts an object literal where the keys are
* the style properties and the value is the expression to be evaluated.
* ```typescript
* styles = {
* background: 'royalblue',
* color: (column) => column.pinned ? 'red': 'inherit'
* }
* ```
* ```html
* <igx-column [headerStyles]="styles"></igx-column>
* ```
*
* @memberof IgxColumnComponent
*/
@notifyChanges()
@WatchColumnChanges()
@Input()
public headerStyles = null;
/**
* Sets/gets the class selector of the column group header.
* ```typescript
* let columnHeaderClass = this.column.headerGroupClasses;
* ```
* ```html
* <igx-column [headerGroupClasses] = "'column-group-header'"></igx-column>
* ```
*
* @memberof IgxColumnComponent
*/
@notifyChanges()
@WatchColumnChanges()
@Input()
public headerGroupClasses = '';
/**
* Sets conditional style properties on the column header group wrapper.
* Similar to `ngStyle` it accepts an object literal where the keys are
* the style properties and the value is the expression to be evaluated.
* ```typescript
* styles = {
* background: 'royalblue',
* color: (column) => column.pinned ? 'red': 'inherit'
* }
* ```
* ```html
* <igx-column [headerGroupStyles]="styles"></igx-column>
* ```
*
* @memberof IgxColumnComponent
*/
@notifyChanges()
@WatchColumnChanges()
@Input()
public headerGroupStyles = null;
/**
* Sets a conditional class selector of the column cells.
* Accepts an object literal, containing key-value pairs,
* where the key is the name of the CSS class, while the
* value is either a callback function that returns a boolean,
* or boolean, like so:
* ```typescript
* callback = (rowData, columnKey, cellValue, rowIndex) => { return rowData[columnKey] > 6; }
* cellClasses = { 'className' : this.callback };
* ```
* ```html
* <igx-column [cellClasses] = "cellClasses"></igx-column>
* <igx-column [cellClasses] = "{'class1' : true }"></igx-column>
* ```
*
* @memberof IgxColumnComponent
*/
@notifyChanges()
@WatchColumnChanges()
@Input()
public cellClasses: any;
/**
* Sets conditional style properties on the column cells.
* Similar to `ngStyle` it accepts an object literal where the keys are
* the style properties and the value is the expression to be evaluated.
* As with `cellClasses` it accepts a callback function.
* ```typescript
* styles = {
* background: 'royalblue',
* color: (rowData, columnKey, cellValue, rowIndex) => value.startsWith('Important') ? 'red': 'inherit'
* }
* ```
* ```html
* <igx-column [cellStyles]="styles"></igx-column>
* ```
*
* @memberof IgxColumnComponent
*/
@notifyChanges()
@WatchColumnChanges()
@Input()
public cellStyles = null;
/**
* Applies display format to cell values in the column. Does not modify the underlying data.
*
* @remark
* Note: As the formatter is used in places like the Excel style filtering dialog, in certain
* scenarios (remote filtering for example), the row data argument can be `undefined`.
*
*
* In this example, we check to see if the column name is Salary, and then provide a method as the column formatter
* to format the value into a currency string.
*
* @example
* ```typescript
* columnInit(column: IgxColumnComponent) {
* if (column.field == "Salary") {
* column.formatter = (salary => this.format(salary));
* }
* }
*
* format(value: number) : string {
* return formatCurrency(value, "en-us", "$");
* }
* ```
*
* @example
* ```typescript
* const column = this.grid.getColumnByName('Address');
* const addressFormatter = (address: string, rowData: any) => data.privacyEnabled ? 'unknown' : address;
* column.formatter = addressFormatter;
* ```
*
* @memberof IgxColumnComponent
*/
@notifyChanges()
@WatchColumnChanges()
@Input()
public formatter: (value: any, rowData?: any) => any;
/**
* The summaryFormatter is used to format the display of the column summaries.
*
* In this example, we check to see if the column name is OrderDate, and then provide a method as the summaryFormatter
* to change the locale for the dates to 'fr-FR'. The summaries with the count key are skipped so they are displayed as numbers.
*
* ```typescript
* columnInit(column: IgxColumnComponent) {
* if (column.field == "OrderDate") {
* column.summaryFormatter = this.summaryFormat;
* }
* }
*
* summaryFormat(summary: IgxSummaryResult, summaryOperand: IgxSummaryOperand): string {
* const result = summary.summaryResult;
* if(summaryResult.key !== 'count' && result !== null && result !== undefined) {
* const pipe = new DatePipe('fr-FR');
* return pipe.transform(result,'mediumDate');
* }
* return result;
* }
* ```
*
* @memberof IgxColumnComponent
*/
@notifyChanges()
@WatchColumnChanges()
@Input()
public summaryFormatter: (summary: IgxSummaryResult, summaryOperand: IgxSummaryOperand) => any;
/**
* Sets/gets whether the column filtering should be case sensitive.
* Default value is `true`.
* ```typescript
* let filteringIgnoreCase = this.column.filteringIgnoreCase;
* ```
* ```html
* <igx-column [filteringIgnoreCase] = "false"></igx-column>
* ```
*
* @memberof IgxColumnComponent
*/
@WatchColumnChanges()
@Input()
public filteringIgnoreCase = true;
/**
* Sets/gets whether the column sorting should be case sensitive.
* Default value is `true`.
* ```typescript
* let sortingIgnoreCase = this.column.sortingIgnoreCase;
* ```
* ```html
* <igx-column [sortingIgnoreCase] = "false"></igx-column>
* ```
*
* @memberof IgxColumnComponent
*/
@WatchColumnChanges()
@Input()
public sortingIgnoreCase = true;
/**
* Sets/gets whether the column is `searchable`.
* Default value is `true`.
* ```typescript
* let isSearchable = this.column.searchable';
* ```
* ```html
* <igx-column [searchable] = "false"></igx-column>
* ```
*
* @memberof IgxColumnComponent
*/
@notifyChanges()
@WatchColumnChanges()
@Input()
public searchable = true;
/**
* Sets/gets the data type of the column values.
* Default value is `string`.
* ```typescript
* let columnDataType = this.column.dataType;
* ```
* ```html
* <igx-column [dataType] = "'number'"></igx-column>
* ```
*
* @memberof IgxColumnComponent
*/
@Input()
public dataType: GridColumnDataType = GridColumnDataType.String;
/** @hidden */
@Input()
public collapsibleIndicatorTemplate: TemplateRef<IgxColumnTemplateContext>;
/**
* Row index where the current field should end.
* The amount of rows between rowStart and rowEnd will determine the amount of spanning rows to that field
* ```html
* <igx-column-layout>
* <igx-column [rowEnd]="2" [rowStart]="1" [colStart]="1"></igx-column>
* </igx-column-layout>
* ```
*
* @memberof IgxColumnComponent
*/
@Input()
public rowEnd: number;
/**
* Column index where the current field should end.
* The amount of columns between colStart and colEnd will determine the amount of spanning columns to that field
* ```html
* <igx-column-layout>
* <igx-column [colEnd]="3" [rowStart]="1" [colStart]="1"></igx-column>
* </igx-column-layout>
* ```
*
* @memberof IgxColumnComponent
*/
@Input()
public colEnd: number;
/**
* Row index from which the field is starting.
* ```html
* <igx-column-layout>
* <igx-column [rowStart]="1" [colStart]="1"></igx-column>
* </igx-column-layout>
* ```
*
* @memberof IgxColumnComponent
*/
@Input()
public rowStart: number;
/**
* Column index from which the field is starting.
* ```html
* <igx-column-layout>
* <igx-column [colStart]="1" [rowStart]="1"></igx-column>
* </igx-column-layout>
* ```
*
* @memberof IgxColumnComponent
*/
@Input()
public colStart: number;
/**
* Sets/gets custom properties provided in additional template context.
*
* ```html
* <igx-column [additionalTemplateContext]="contextObject">
* <ng-template igxCell let-cell="cell" let-props="additionalTemplateContext">
* {{ props }}
* </ng-template>
* </igx-column>
* ```
*
* @memberof IgxColumnComponent
*/
@Input()
public additionalTemplateContext: any;
/**
* @hidden
*/
@Output()
public widthChange = new EventEmitter<string>();
/**
* @hidden
*/
@Output()
public pinnedChange = new EventEmitter<boolean>();
/**
* @hidden
*/
@ContentChild(IgxFilterCellTemplateDirective, { read: IgxFilterCellTemplateDirective })
public filterCellTemplateDirective: IgxFilterCellTemplateDirective;
/**
* @hidden
*/
@ContentChild(IgxSummaryTemplateDirective, { read: IgxSummaryTemplateDirective })
protected summaryTemplateDirective: IgxSummaryTemplateDirective;
/**
* @hidden
* @see {@link bodyTemplate}
*/
@ContentChild(IgxCellTemplateDirective, { read: IgxCellTemplateDirective })
protected cellTemplate: IgxCellTemplateDirective;
/**
* @hidden
*/
@ContentChild(IgxCellValidationErrorDirective, { read: IgxCellValidationErrorDirective })
protected cellValidationErrorTemplate: IgxCellValidationErrorDirective;
/**
* @hidden
*/
@ContentChildren(IgxCellHeaderTemplateDirective, { read: IgxCellHeaderTemplateDirective, descendants: false })
protected headTemplate: QueryList<IgxCellHeaderTemplateDirective>;
/**
* @hidden
*/
@ContentChild(IgxCellEditorTemplateDirective, { read: IgxCellEditorTemplateDirective })
protected editorTemplate: IgxCellEditorTemplateDirective;
/**
* @hidden
*/
@ContentChild(IgxCollapsibleIndicatorTemplateDirective, { read: IgxCollapsibleIndicatorTemplateDirective, static: false })
protected collapseIndicatorTemplate: IgxCollapsibleIndicatorTemplateDirective;
/**
* @hidden
*/
public get calcWidth(): any {
return this.getCalcWidth();
}
/** @hidden @internal **/
public calcPixelWidth: number;
/**
* @hidden
*/
public get maxWidthPx() {
const gridAvailableSize = this.grid.calcWidth;
const isPercentageWidth = this.maxWidth && typeof this.maxWidth === 'string' && this.maxWidth.indexOf('%') !== -1;
return isPercentageWidth ? parseFloat(this.maxWidth) / 100 * gridAvailableSize : parseFloat(this.maxWidth);
}
/**
* @hidden
*/
public get maxWidthPercent() {
const gridAvailableSize = this.grid.calcWidth;
const isPercentageWidth = this.maxWidth && typeof this.maxWidth === 'string' && this.maxWidth.indexOf('%') !== -1;
return isPercentageWidth ? parseFloat(this.maxWidth) : parseFloat(this.maxWidth) / gridAvailableSize * 100;
}
/**
* @hidden
*/
public get minWidthPx() {
const gridAvailableSize = this.grid.calcWidth;
const isPercentageWidth = this.minWidth && typeof this.minWidth === 'string' && this.minWidth.indexOf('%') !== -1;
return isPercentageWidth ? parseFloat(this.minWidth) / 100 * gridAvailableSize : parseFloat(this.minWidth);
}
/**
* @hidden
*/
public get minWidthPercent() {
const gridAvailableSize = this.grid.calcWidth;
const isPercentageWidth = this.minWidth && typeof this.minWidth === 'string' && this.minWidth.indexOf('%') !== -1;
return isPercentageWidth ? parseFloat(this.minWidth) : parseFloat(this.minWidth) / gridAvailableSize * 100;
}
/**
* Sets/gets the minimum `width` of the column.
* Default value is `88`;
* ```typescript
* let columnMinWidth = this.column.minWidth;
* ```
* ```html
* <igx-column [minWidth] = "'100px'"></igx-column>
* ```
*
* @memberof IgxColumnComponent
*/
@notifyChanges()
@WatchColumnChanges()
@Input()
public set minWidth(value: string) {
const minVal = parseFloat(value);
if (Number.isNaN(minVal)) {
return;
}
this._defaultMinWidth = value;
}
public get minWidth(): string {
return !this._defaultMinWidth ? this.defaultMinWidth : this._defaultMinWidth;
}
/**
* Gets the column index.
* ```typescript
* let columnIndex = this.column.index;
* ```
*
* @memberof IgxColumnComponent
*/
public get index(): number {
return (this.grid as any)._columns.indexOf(this);
}
/**
* Gets whether the column is `pinned`.
* ```typescript
* let isPinned = this.column.pinned;
* ```
*
* @memberof IgxColumnComponent
*/
@WatchColumnChanges()
@Input()
public get pinned(): boolean {
return this._pinned;
}
/**
* Sets whether the column is pinned.
* Default value is `false`.
* ```html
* <igx-column [pinned] = "true"></igx-column>
* ```
*
* Two-way data binding.
* ```html
* <igx-column [(pinned)] = "model.columns[0].isPinned"></igx-column>
* ```
*
* @memberof IgxColumnComponent
*/
public set pinned(value: boolean) {
if (this._pinned !== value) {
const isAutoWidth = this.width && typeof this.width === 'string' && this.width === 'fit-content';
if (this.grid && this.width && (isAutoWidth || !isNaN(parseInt(this.width, 10)))) {
if (value) {
this.pin();
} else {
this.unpin();
}
return;
}
/* No grid/width available at initialization. `initPinning` in the grid
will re-init the group (if present)
*/
this._pinned = value;
this.pinnedChange.emit(this._pinned);
}
}
/**
* Gets the column `summaries`.
* ```typescript
* let columnSummaries = this.column.summaries;
* ```
*
* @memberof IgxColumnComponent
*/
@notifyChanges(true)
@WatchColumnChanges()
@Input()
public get summaries(): any {
return this._summaries;
}
/**
* Sets the column `summaries`.
* ```typescript
* this.column.summaries = IgxNumberSummaryOperand;
* ```
*
* @memberof IgxColumnComponent
*/
public set summaries(classRef: any) {
if (isConstructor(classRef)) {
this._summaries = new classRef();
}
if (this.grid) {
this.grid.summaryService.removeSummariesCachePerColumn(this.field);
this.grid.summaryPipeTrigger++;
this.grid.summaryService.resetSummaryHeight();
}
}
/**
* Gets the column `filters`.
* ```typescript
* let columnFilters = this.column.filters'
* ```
*
* @memberof IgxColumnComponent
*/
@Input()
public get filters(): IgxFilteringOperand {
return this._filters;
}
/**
* Sets the column `filters`.
* ```typescript
* this.column.filters = IgxBooleanFilteringOperand.instance().
* ```
*
* @memberof IgxColumnComponent
*/
public set filters(instance: IgxFilteringOperand) {
this._filters = instance;
}
/**
* Gets the column `sortStrategy`.
* ```typescript
* let sortStrategy = this.column.sortStrategy
* ```
*
* @memberof IgxColumnComponent
*/
@Input()
public get sortStrategy(): ISortingStrategy {
return this._sortStrategy;
}
/**
* Sets the column `sortStrategy`.
* ```typescript
* this.column.sortStrategy = new CustomSortingStrategy().
* class CustomSortingStrategy extends SortingStrategy {...}
* ```
*
* @memberof IgxColumnComponent
*/
public set sortStrategy(classRef: ISortingStrategy) {
this._sortStrategy = classRef;
}
/**
* Gets the function that compares values for grouping.
* ```typescript
* let groupingComparer = this.column.groupingComparer'
* ```
*
* @memberof IgxColumnComponent
*/
@Input()
public get groupingComparer(): (a: any, b: any, currRec?: any, groupRec?: any) => number {
return this._groupingComparer;
}
/**
* Sets a custom function to compare values for grouping.
* Subsequent values in the sorted data that the function returns 0 for are grouped.
* ```typescript
* this.column.groupingComparer = (a: any, b: any, currRec?: any, groupRec?: any) => { return a === b ? 0 : -1; }
* ```
*
* @memberof IgxColumnComponent
*/
public set groupingComparer(funcRef: (a: any, b: any, currRec?: any, groupRec?: any) => number) {
this._groupingComparer = funcRef;
}
/**
* Gets the default minimum `width` of the column.
* ```typescript
* let defaultMinWidth = this.column.defaultMinWidth;
* ```
*
* @memberof IgxColumnComponent
*/
public get defaultMinWidth(): string {
if (!this.grid) {
return '80';
}
switch (this.grid.displayDensity) {
case DisplayDensity.cosy:
return '64';
case DisplayDensity.compact:
return '56';
default:
return '80';
}
}
/**
* Returns a reference to the `summaryTemplate`.
* ```typescript
* let summaryTemplate = this.column.summaryTemplate;
* ```
*
* @memberof IgxColumnComponent
*/
@notifyChanges()
@WatchColumnChanges()
@Input()
public get summaryTemplate(): TemplateRef<IgxSummaryTemplateContext> {
return this._summaryTemplate;
}
/**
* Sets the summary template.
* ```html
* <ng-template #summaryTemplate igxSummary let-summaryResults>
* <p>{{ summaryResults[0].label }}: {{ summaryResults[0].summaryResult }}</p>
* <p>{{ summaryResults[1].label }}: {{ summaryResults[1].summaryResult }}</p>
* </ng-template>
* ```
* ```typescript
* @ViewChild("'summaryTemplate'", {read: TemplateRef })
* public summaryTemplate: TemplateRef<any>;
* this.column.summaryTemplate = this.summaryTemplate;
* ```
*
* @memberof IgxColumnComponent
*/
public set summaryTemplate(template: TemplateRef<IgxSummaryTemplateContext>) {
this._summaryTemplate = template;
}
/**
* Returns a reference to the `bodyTemplate`.
* ```typescript
* let bodyTemplate = this.column.bodyTemplate;
* ```
*
* @memberof IgxColumnComponent
*/
@notifyChanges()
@WatchColumnChanges()
@Input('cellTemplate')
public get bodyTemplate(): TemplateRef<IgxCellTemplateContext> {
return this._bodyTemplate;
}
/**
* Sets the body template.
* ```html
* <ng-template #bodyTemplate igxCell let-val>
* <div style = "background-color: yellowgreen" (click) = "changeColor(val)">
* <span> {{val}} </span>
* </div>
* </ng-template>
* ```
* ```typescript
* @ViewChild("'bodyTemplate'", {read: TemplateRef })
* public bodyTemplate: TemplateRef<any>;
* this.column.bodyTemplate = this.bodyTemplate;
* ```
*
* @memberof IgxColumnComponent
*/
public set bodyTemplate(template: TemplateRef<IgxCellTemplateContext>) {
this._bodyTemplate = template;
}
/**
* Returns a reference to the header template.
* ```typescript
* let headerTemplate = this.column.headerTemplate;
* ```
*
* @memberof IgxColumnComponent
*/
@notifyChanges()
@WatchColumnChanges()
@Input()
public get headerTemplate(): TemplateRef<IgxColumnTemplateContext> {
return this._headerTemplate;
}
/**
* Sets the header template.
* Note that the column header height is fixed and any content bigger than it will be cut off.
* ```html
* <ng-template #headerTemplate>
* <div style = "background-color:black" (click) = "changeColor(val)">
* <span style="color:red" >{{column.field}}</span>
* </div>
* </ng-template>
* ```
* ```typescript
* @ViewChild("'headerTemplate'", {read: TemplateRef })
* public headerTemplate: TemplateRef<any>;
* this.column.headerTemplate = this.headerTemplate;
* ```
*
* @memberof IgxColumnComponent
*/
public set headerTemplate(template: TemplateRef<IgxColumnTemplateContext>) {
this._headerTemplate = template;
}
/**
* Returns a reference to the inline editor template.
* ```typescript
* let inlineEditorTemplate = this.column.inlineEditorTemplate;
* ```
*
* @memberof IgxColumnComponent
*/
@notifyChanges()
@WatchColumnChanges()
@Input('cellEditorTemplate')
public get inlineEditorTemplate(): TemplateRef<IgxCellTemplateContext> {
return this._inlineEditorTemplate;
}
/**
* Sets the inline editor template.
* ```html
* <ng-template #inlineEditorTemplate igxCellEditor let-cell="cell">
* <input type="string" [(ngModel)]="cell.value"/>
* </ng-template>
* ```
* ```typescript
* @ViewChild("'inlineEditorTemplate'", {read: TemplateRef })
* public inlineEditorTemplate: TemplateRef<any>;
* this.column.inlineEditorTemplate = this.inlineEditorTemplate;
* ```
*
* @memberof IgxColumnComponent
*/
public set inlineEditorTemplate(template: TemplateRef<IgxCellTemplateContext>) {
this._inlineEditorTemplate = template;
}
/**
* Returns a reference to the validation error template.
* ```typescript
* let errorTemplate = this.column.errorTemplate;
* ```
*/
@notifyChanges()
@WatchColumnChanges()
@Input('errorTemplate')
public get errorTemplate(): TemplateRef<IgxCellTemplateContext> {
return this._errorTemplate;
}
/**
* Sets the error template.
* ```html
* <ng-template igxCellValidationError let-cell="cell" #errorTemplate >
* <div *ngIf="cell.validation.errors?.['forbiddenName']">
* This name is forbidden.
* </div>
* </ng-template>
* ```
* ```typescript
* @ViewChild("'errorTemplate'", {read: TemplateRef })
* public errorTemplate: TemplateRef<any>;
* this.column.errorTemplate = this.errorTemplate;
* ```
*/
public set errorTemplate(template: TemplateRef<IgxCellTemplateContext>) {
this._errorTemplate = template;
}
/**
* Returns a reference to the `filterCellTemplate`.
* ```typescript
* let filterCellTemplate = this.column.filterCellTemplate;
* ```
*
* @memberof IgxColumnComponent
*/
@notifyChanges()
@WatchColumnChanges()
@Input('filterCellTemplate')
public get filterCellTemplate(): TemplateRef<IgxColumnTemplateContext> {
return this._filterCellTemplate;
}
/**
* Sets the quick filter template.
* ```html
* <ng-template #filterCellTemplate IgxFilterCellTemplate let-column="column">
* <input (input)="onInput()">
* </ng-template>
* ```
* ```typescript
* @ViewChild("'filterCellTemplate'", {read: TemplateRef })
* public filterCellTemplate: TemplateRef<any>;
* this.column.filterCellTemplate = this.filterCellTemplate;
* ```
*
* @memberof IgxColumnComponent
*/
public set filterCellTemplate(template: TemplateRef<IgxColumnTemplateContext>) {
this._filterCellTemplate = template;
}
/**
* Gets the cells of the column.
* ```typescript
* let columnCells = this.column.cells;
* ```
*
*/
public get cells(): CellType[] {
return this.grid.dataView
.map((rec, index) => {
if (!this.grid.isGroupByRecord(rec) && !this.grid.isSummaryRow(rec)) {
this.grid.pagingMode === 1 && this.grid.page !== 0 ? index = index + this.grid.perPage * this.grid.page : index = this.grid.dataRowList.first.index + index;
const cell = new IgxGridCell(this.grid as any, index, this.field);
return cell;
}
}).filter(cell => cell);
}
/**
* @hidden @internal
*/
public get _cells(): CellType[] {
return this.grid.rowList.filter((row) => row instanceof IgxRowDirective)
.map((row) => {
if (row._cells) {
return row._cells.filter((cell) => cell.columnIndex === this.index);
}
}).reduce((a, b) => a.concat(b), []);
}
/**
* Gets the column visible index.
* If the column is not visible, returns `-1`.
* ```typescript
* let visibleColumnIndex = this.column.visibleIndex;
* ```
*
* @memberof IgxColumnComponent
*/
public get visibleIndex(): number {
if (!isNaN(this._vIndex)) {
return this._vIndex;
}
const unpinnedColumns = this.grid.unpinnedColumns.filter(c => !c.columnGroup);
const pinnedColumns = this.grid.pinnedColumns.filter(c => !c.columnGroup);
// eslint-disable-next-line @typescript-eslint/no-this-alias
let col = this;
let vIndex = -1;
if (this.columnGroup) {
col = this.allChildren.filter(c => !c.columnGroup && !c.hidden)[0] as any;
}
if (this.columnLayoutChild) {
return this.parent.childrenVisibleIndexes.find(x => x.column === this).index;
}
if (!this.pinned) {
const indexInCollection = unpinnedColumns.indexOf(col);
vIndex = indexInCollection === -1 ?
-1 :
(this.grid.isPinningToStart ?
pinnedColumns.length + indexInCollection :
indexInCollection);
} else {
const indexInCollection = pinnedColumns.indexOf(col);
vIndex = this.grid.isPinningToStart ?
indexInCollection :
unpinnedColumns.length + indexInCollection;
}
this._vIndex = vIndex;
return vIndex;
}
/**
* Returns a boolean indicating if the column is a `ColumnGroup`.
* ```typescript
* let columnGroup = this.column.columnGroup;
* ```
*
* @memberof IgxColumnComponent
*/
public get columnGroup() {
return false;
}
/**
* Returns a boolean indicating if the column is a `ColumnLayout` for multi-row layout.
* ```typescript
* let columnGroup = this.column.columnGroup;
* ```
*
* @memberof IgxColumnComponent
*/
public get columnLayout() {
return false;
}
/**
* Returns a boolean indicating if the column is a child of a `ColumnLayout` for multi-row layout.
* ```typescript
* let columnLayoutChild = this.column.columnLayoutChild;
* ```
*
* @memberof IgxColumnComponent
*/
public get columnLayoutChild() {
return this.parent && this.parent.columnLayout;
}
/** @hidden @internal **/
public get allChildren(): IgxColumnComponent[] {
return [];
}
/**
* Returns the level of the column in a column group.
* Returns `0` if the column doesn't have a `parent`.
* ```typescript
* let columnLevel = this.column.level;
* ```
*
* @memberof IgxColumnComponent
*/
public get level() {
let ptr = this.parent;
let lvl = 0;
while (ptr) {
lvl++;
ptr = ptr.parent;
}
return lvl;
}
/** @hidden @internal **/
public get isLastPinned(): boolean {
return this.grid.isPinningToStart &&
this.grid.pinnedColumns[this.grid.pinnedColumns.length - 1] === this;
}
/** @hidden @internal **/
public get isFirstPinned(): boolean {
const pinnedCols = this.grid.pinnedColumns.filter(x => !x.columnGroup);
return !this.grid.isPinningToStart && pinnedCols[0] === this;
}
/** @hidden @internal **/
public get rightPinnedOffset(): string {
return this.pinned && !this.grid.isPinningToStart ?
- this.grid.pinnedWidth - this.grid.headerFeaturesWidth + 'px' :
null;
}
public get gridRowSpan(): number {
return this.rowEnd && this.rowStart ? this.rowEnd - this.rowStart : 1;
}
public get gridColumnSpan(): number {
return this.colEnd && this.colStart ? this.colEnd - this.colStart : 1;
}
/**
* Indicates whether the column will be visible when its parent is collapsed.
* ```html
* <igx-column-group>
* <igx-column [visibleWhenCollapsed]="true"></igx-column>
* </igx-column-group>
* ```
*
* @memberof IgxColumnComponent
*/
@notifyChanges(true)
@Input()
public set visibleWhenCollapsed(value: boolean) {
this._visibleWhenCollapsed = value;
this.visibleWhenCollapsedChange.emit(this._visibleWhenCollapsed);
if (this.parent) {
this.parent.setExpandCollapseState();
}
}
public get visibleWhenCollapsed(): boolean {
return this._visibleWhenCollapsed;
}
/**
* @remarks
* Pass optional parameters for DatePipe and/or DecimalPipe to format the display value for date and numeric columns.
* Accepts an `IColumnPipeArgs` object with any of the `format`, `timezone` and `digitsInfo` properties.
* For more details see https://angular.io/api/common/DatePipe and https://angular.io/api/common/DecimalPipe
* @example
* ```typescript
* const pipeArgs: IColumnPipeArgs = {
* format: 'longDate',
* timezone: 'UTC',
* digitsInfo: '1.1-2'
* }
* ```
* ```html
* <igx-column dataType="date" [pipeArgs]="pipeArgs"></igx-column>
* <igx-column dataType="number" [pipeArgs]="pipeArgs"></igx-column>
* ```
* @memberof IgxColumnComponent
*/
@notifyChanges()
@WatchColumnChanges()
@Input()
public set pipeArgs(value: IColumnPipeArgs) {
this._columnPipeArgs = Object.assign(this._columnPipeArgs, value);
this.grid.summaryService.clearSummaryCache();
this.grid.pipeTrigger++;
}
public get pipeArgs(): IColumnPipeArgs {
return this._columnPipeArgs;
}
/**
* @hidden
* @internal
*/
public get collapsible() {
return false;
}
public set collapsible(_value: boolean) { }
/**
* @hidden
* @internal
*/
public get expanded() {
return true;
}
public set expanded(_value: boolean) { }
/**
* @hidden
*/
public defaultWidth: string;
/**
* @hidden
*/
public widthSetByUser: boolean;
/**
* @hidden
*/
public hasNestedPath: boolean;
/**
* @hidden
* @internal
*/
public defaultTimeFormat = 'hh:mm:ss tt';
/**
* @hidden
* @internal
*/
public defaultDateTimeFormat = 'dd/MM/yyyy HH:mm:ss tt';
/**
* Returns the filteringExpressionsTree of the column.
* ```typescript
* let tree = this.column.filteringExpressionsTree;
* ```
*
* @memberof IgxColumnComponent
*/
public get filteringExpressionsTree(): FilteringExpressionsTree {
return this.grid.filteringExpressionsTree.find(this.field) as FilteringExpressionsTree;
}
/**
* Sets/gets the parent column.
* ```typescript
* let parentColumn = this.column.parent;
* ```
* ```typescript
* this.column.parent = higherLevelColumn;
* ```
*
* @memberof IgxColumnComponent
*/
public parent = null;
/**
* Sets/gets the children columns.
* ```typescript
* let columnChildren = this.column.children;
* ```
* ```typescript
* this.column.children = childrenColumns;
* ```
*
* @memberof IgxColumnComponent
*/
public children: QueryList<IgxColumnComponent>;
/**
* @hidden
*/
public destroy$ = new Subject<any>();
/**
* @hidden
*/
protected _applySelectableClass = false;
protected _vIndex = NaN;
/**
* @hidden
*/
protected _pinned = false;
/**
* @hidden
*/
protected _bodyTemplate: TemplateRef<IgxCellTemplateContext>;
/**
* @hidden
*/
protected _errorTemplate: TemplateRef<IgxCellTemplateContext>;
/**
* @hidden
*/
protected _headerTemplate: TemplateRef<IgxColumnTemplateContext>;
/**
* @hidden
*/
protected _summaryTemplate: TemplateRef<IgxSummaryTemplateContext>;
/**
* @hidden
*/
protected _inlineEditorTemplate: TemplateRef<IgxCellTemplateContext>;
/**
* @hidden
*/
protected _filterCellTemplate: TemplateRef<IgxColumnTemplateContext>;
/**
* @hidden
*/
protected _summaries = null;
/**
* @hidden
*/
protected _filters = null;
/**
* @hidden
*/
protected _sortStrategy: ISortingStrategy = DefaultSortingStrategy.instance();
/**
* @hidden
*/
protected _groupingComparer: (a: any, b: any, currRec?: any, groupRec?: any) => number;
/**
* @hidden
*/
protected _hidden = false;
/**
* @hidden
*/
protected _index: number;
/**
* @hidden
*/
protected _disablePinning = false;
/**
* @hidden
*/
protected _width: string;
/**
* @hidden
*/
protected _defaultMinWi