/** * @license * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ import { ChangeDetectionStrategy, Component, Inject, InjectionToken, Input, OnDestroy, OnInit, Optional, ViewChild, ViewEncapsulation, isDevMode, } from '@angular/core'; import {CdkCellDef, CdkColumnDef, CdkHeaderCellDef} from './cell'; import {CdkTable} from './table'; import { getTableTextColumnMissingParentTableError, getTableTextColumnMissingNameError, } from './table-errors'; /** Configurable options for `CdkTextColumn`. */ export interface TextColumnOptions { /** * Default function that provides the header text based on the column name if a header * text is not provided. */ defaultHeaderTextTransform?: (name: string) => string; /** Default data accessor to use if one is not provided. */ defaultDataAccessor?: (data: T, name: string) => string; } /** Injection token that can be used to specify the text column options. */ export const TEXT_COLUMN_OPTIONS = new InjectionToken>('text-column-options'); /** * Column that simply shows text content for the header and row cells. Assumes that the table * is using the native table implementation (``). * * By default, the name of this column will be the header text and data property accessor. * The header text can be overridden with the `headerText` input. Cell values can be overridden with * the `dataAccessor` input. Change the text justification to the start or end using the `justify` * input. */ @Component({ moduleId: module.id, selector: 'cdk-text-column', template: ` `, encapsulation: ViewEncapsulation.None, // Change detection is intentionally not set to OnPush. This component's template will be provided // to the table to be inserted into its view. This is problematic when change detection runs since // the bindings in this template will be evaluated _after_ the table's view is evaluated, which // mean's the template in the table's view will not have the updated value (and in fact will cause // an ExpressionChangedAfterItHasBeenCheckedError). // tslint:disable-next-line:validate-decorators changeDetection: ChangeDetectionStrategy.Default, }) export class CdkTextColumn implements OnDestroy, OnInit { /** Column name that should be used to reference this column. */ @Input() get name(): string { return this._name; } set name(name: string) { this._name = name; // With Ivy, inputs can be initialized before static query results are // available. In that case, we defer the synchronization until "ngOnInit" fires. this._syncColumnDefName(); } _name: string; /** * Text label that should be used for the column header. If this property is not * set, the header text will default to the column name with its first letter capitalized. */ @Input() headerText: string; /** * Accessor function to retrieve the data rendered for each cell. If this * property is not set, the data cells will render the value found in the data's property matching * the column's name. For example, if the column is named `id`, then the rendered value will be * value defined by the data's `id` property. */ @Input() dataAccessor: (data: T, name: string) => string; /** Alignment of the cell values. */ @Input() justify: 'start'|'end' = 'start'; /** @docs-private */ @ViewChild(CdkColumnDef, {static: true}) columnDef: CdkColumnDef; /** * The column cell is provided to the column during `ngOnInit` with a static query. * Normally, this will be retrieved by the column using `ContentChild`, but that assumes the * column definition was provided in the same view as the table, which is not the case with this * component. * @docs-private */ @ViewChild(CdkCellDef, {static: true}) cell: CdkCellDef; /** * The column headerCell is provided to the column during `ngOnInit` with a static query. * Normally, this will be retrieved by the column using `ContentChild`, but that assumes the * column definition was provided in the same view as the table, which is not the case with this * component. * @docs-private */ @ViewChild(CdkHeaderCellDef, {static: true}) headerCell: CdkHeaderCellDef; constructor( @Optional() private _table: CdkTable, @Optional() @Inject(TEXT_COLUMN_OPTIONS) private _options: TextColumnOptions) { this._options = _options || {}; } ngOnInit() { this._syncColumnDefName(); if (this.headerText === undefined) { this.headerText = this._createDefaultHeaderText(); } if (!this.dataAccessor) { this.dataAccessor = this._options.defaultDataAccessor || ((data: T, name: string) => (data as any)[name]); } if (this._table) { // Provide the cell and headerCell directly to the table with the static `ViewChild` query, // since the columnDef will not pick up its content by the time the table finishes checking // its content and initializing the rows. this.columnDef.cell = this.cell; this.columnDef.headerCell = this.headerCell; this._table.addColumnDef(this.columnDef); } else { throw getTableTextColumnMissingParentTableError(); } } ngOnDestroy() { if (this._table) { this._table.removeColumnDef(this.columnDef); } } /** * Creates a default header text. Use the options' header text transformation function if one * has been provided. Otherwise simply capitalize the column name. */ _createDefaultHeaderText() { const name = this.name; if (isDevMode() && !name) { throw getTableTextColumnMissingNameError(); } if (this._options && this._options.defaultHeaderTextTransform) { return this._options.defaultHeaderTextTransform(name); } return name[0].toUpperCase() + name.slice(1); } /** Synchronizes the column definition name with the text column name. */ private _syncColumnDefName() { if (this.columnDef) { this.columnDef.name = this.name; } } }
{{headerText}} {{dataAccessor(data, name)}}