### File

`src/table/table.component.ts`

### Description

Build your table with this component by extending things that differ from default.

See demo

Instead of the usual write-your-own-html approach you had with `<table>`, carbon table uses model-view-controller approach.

Here, you create a view (with built-in controller) and provide it a model. Changes you make to the model are reflected in the view. Provide same model you use in the table to the `<ibm-pagination>` components. They provide a different view over the same data.

## Basic usage

     * <ibm-table [model]="model"></ibm-table>
     *

     * public model = new TableModel();
     *
     * this.model.data = [
     *     [new TableItem({data: "asdf"}), new TableItem({data: "qwer"})],
     *     [new TableItem({data: "csdf"}), new TableItem({data: "zwer"})],
     *     [new TableItem({data: "bsdf"}), new TableItem({data: "swer"})],
     *     [new TableItem({data: "csdf"}), new TableItem({data: "twer"})]
     * ];
     *

## Customization

If you have custom data in your table, you need a way to display it. You can do that by providing a template to `TableItem`.

     * <ng-template #customTableItemTemplate let-data="data">
     *     <a [routerLink]="data.link">{{data.name}} {{data.surname}}</a>
     * </ng-template>
     *

     * customTableItemTemplate: TemplateRef<any>;
     *
     * this.customModel.data = [
     *     [new TableItem({data: "asdf"}), new TableItem({data: {name: "Lessy", link: "/table"}, template: this.customTableItemTemplate})],
     *     [new TableItem({data: "csdf"}), new TableItem({data: "swer"})],
     *     [new TableItem({data: "bsdf"}), new TableItem({data: {name: "Alice", surname: "Bob"}, template: this.customTableItemTemplate})],
     *     [new TableItem({data: "csdf"}), new TableItem({data: "twer"})],
     * ];
     *

### Sorting and filtering

In case you need custom sorting and/or filtering you should subclass `TableHeaderItem` and override needed functions.

     * class FilterableHeaderItem extends TableHeaderItem {
     *     // custom filter function
     *     filter(item: TableItem): boolean {
     *         if (typeof item.data === "string" && item.data.toLowerCase().indexOf(this.filterData.data.toLowerCase()) >= 0 ||
     *         item.data.name && item.data.name.toLowerCase().indexOf(this.filterData.data.toLowerCase()) >= 0 ||
     *         item.data.surname && item.data.surname.toLowerCase().indexOf(this.filterData.data.toLowerCase()) >= 0) {
     *             return false;
     *         }
     *         return true;
     *     }
     *
     *     set filterCount(n) {}
     *     get filterCount() {
     *         return (this.filterData && this.filterData.data && this.filterData.data.length > 0) ? 1 : 0;
     *     }
     *
     *     // used for custom sorting
     *     compare(one: TableItem, two: TableItem) {
     *         const stringOne = (one.data.name || one.data.surname || one.data).toLowerCase();
     *         const stringTwo = (two.data.name || two.data.surname || two.data).toLowerCase();
     *
     *         if (stringOne > stringTwo) {
     *             return 1;
     *         } else if (stringOne < stringTwo) {
     *             return -1;
     *         } else {
     *             return 0;
     *         }
     *     }
     * }
     *

See `TableHeaderItem` class for more information.

## No data template

When table has no data to show, it can show a message you provide it instead.

     * <ibm-table [model]="model">No data.</ibm-table>
     *

... will show `No data.` message, but you can get creative and provide any template you want to replace table's default `tbody`.

## Use pagination as table footer

     * <ibm-pagination [model]="model" (selectPage)="selectPage($event)"></ibm-pagination>
     *

`selectPage()` function should fetch the data from backend, create new `data`, apply it to `model.data`, and update `model.currentPage`.

If the data your server returns is a two dimensional array of objects, it would look something like this:

     * selectPage(page) {
     *     this.getPage(page).then((data: Array<Array<any>>) => {
     *         // set the data and update page
     *         this.model.data = this.prepareData(data);
     *         this.model.currentPage = page;
     *     });
     * }
     *
     * protected prepareData(data: Array<Array<any>>) {
     *     // create new data from the service data
     *     let newData = [];
     *     data.forEach(dataRow => {
     *         let row = [];
     *         dataRow.forEach(dataElement => {
     *             row.push(new TableItem({
     *                 data: dataElement,
     *                 template: typeof dataElement === "string" ? undefined : this.paginationTableItemTemplate
     *                 // your template can handle all the data types so you don't have to conditionally set it
     *                 // you can also set different templates for different columns based on index
     *             }));
     *         });
     *         newData.push(row);
     *     });
     *     return newData;
     * }
     *

../../iframe.html?id=table--basic

### Implements

`AfterViewInit`

### Example

### Metadata

selector | `ibm-table`

template

    <table
    class="bx--data-table bx--data-table--sort"
    [ngClass]="{
    	'bx--data-table--compact': size === 'sm',
    	'bx--data-table--tall': size === 'lg',
    	'bx--data-table--zebra': striped,
    	'bx--skeleton': skeleton
    }">
    	<thead>
    		<tr>
    			<th
    				class="bx--table-expand"
    				*ngIf="model.hasExpandableRows()"
    				[ibmDataGridFocus]="isDataGrid"
    				[(columnIndex)]="columnIndex"
    				(click)="setExpandIndex($event)">
    			</th>
    			<th
    				class="bx--table-column-checkbox"
    				*ngIf="!skeleton && showSelectionColumn"
    				[ibmDataGridFocus]="isDataGrid"
    				[(columnIndex)]="columnIndex"
    				(click)="setCheckboxIndex()"
    				style="width: 10px;">
    				<ibm-checkbox
    					inline="true"
    					[size]="size !== ('lg' ? 'sm' : 'md')"
    					[(ngModel)]="selectAllCheckbox"
    					[indeterminate]="selectAllCheckboxSomeSelected"
    					[attr.aria-label]="checkboxHeaderLabel.subject | async"
    					(change)="onSelectAllCheckboxChange()">
    				</ibm-checkbox>
    			</th>
    			<ng-container *ngFor="let column of model.header; let i = index">
    				<th
    					[ngClass]='{"thead_action": column.filterTemplate || this.sort.observers.length > 0}'
    					*ngIf="column.visible"
    					[class]="column.className"
    					[ngStyle]="column.style"
    					[ibmDataGridFocus]="isDataGrid"
    					[(columnIndex)]="columnIndex"
    					[draggable]="columnsDraggable"
    					(dragstart)="columnDragStart($event, i)"
    					(dragend)="columnDragEnd($event, i)"
    					(click)="setIndex(i)">
    					<div
    						*ngIf="columnsResizable"
    						class="column-resize-handle"
    						(mousedown)="columnResizeStart($event, column)">
    					</div>
    					<button
    						class="bx--table-sort"
    						*ngIf="!skeleton && this.sort.observers.length > 0 && column.sortable"
    						[attr.aria-label]="(column.sorted && column.ascending ? getSortDescendingLabel() : getSortAscendingLabel()) | async"
    						aria-live="polite"
    						[ngClass]="{
    							'bx--table-sort--active': column.sorted,
    							'bx--table-sort--ascending': column.ascending
    						}"
    						(click)="sort.emit(i)">
    						<span
    							*ngIf="!column.template"
    							[title]="column.data"
    							tabindex="-1">
    							{{column.data}}
    						</span>
    						<ng-template
    							[ngTemplateOutlet]="column.template" [ngTemplateOutletContext]="{data: column.data}">
    						</ng-template>
    						<svg
    							focusable="false"
    							preserveAspectRatio="xMidYMid meet"
    							style="will-change: transform;"
    							xmlns="http://www.w3.org/2000/svg"
    							class="bx--table-sort__icon"
    							width="16"
    							height="16"
    							viewBox="0 0 16 16"
    							aria-hidden="true">
    							<path d="M12.3 9.3l-3.8 3.8V1h-1v12.1L3.7 9.3 3 10l5 5 5-5z"></path>
    						</svg>
    						<svg
    							focusable="false"
    							preserveAspectRatio="xMidYMid meet"
    							style="will-change: transform;"
    							xmlns="http://www.w3.org/2000/svg"
    							class="bx--table-sort__icon-unsorted"
    							width="16"
    							height="16"
    							viewBox="0 0 16 16"
    							aria-hidden="true">
    							<path d="M13.8 10.3L12 12.1V2h-1v10.1l-1.8-1.8-.7.7 3 3 3-3zM4.5 2l-3 3 .7.7L4 3.9V14h1V3.9l1.8 1.8.7-.7z"></path>
    						</svg>
    					</button>
    					<span
    						class="bx--table-header-label"
    						*ngIf="!skeleton && this.sort.observers.length === 0 || (this.sort.observers.length > 0 && !column.sortable)">
    						<span *ngIf="!column.template" [title]="column.data">{{column.data}}</span>
    						<ng-template
    							[ngTemplateOutlet]="column.template" [ngTemplateOutletContext]="{data: column.data}">
    						</ng-template>
    					</span>
    					<button
    						[ngClass]="{'active': column.filterCount > 0}"
    						*ngIf="column.filterTemplate"
    						type="button"
    						aria-expanded="false"
    						aria-haspopup="true"
    						[ibmTooltip]="column.filterTemplate"
    						trigger="click"
    						[title]="filterTitle.subject | async"
    						placement="bottom,top"
    						[data]="column.filterData">
    						<svg
    							xmlns="http://www.w3.org/2000/svg"
    							class="icon--sm"
    							width="16"
    							height="16"
    							viewBox="0 0 16 16">
    							<path d="M0 0v3l6 8v5h4v-5l6-8V0H0zm9 10.7V15H7v-4.3L1.3 3h13.5L9 10.7z"/>
    						</svg>
    						<span *ngIf="column.filterCount > 0">
    							{{column.filterCount}}
    						</span>
    					</button>
    					<div
    					*ngIf="columnsDraggable && isColumnDragging"
    					class="drop-area">
    						<div
    						*ngIf="columnDraggedHoverIndex == i && columnDraggedPosition == 'left'"
    						class="drop-indicator-left"></div>
    						<div
    						class="drop-area-left"
    						(dragenter)="columnDragEnter($event, 'left', i)"
    						(dragleave)="columnDragLeave($event, 'left', i)"
    						(dragover)="columnDragover($event, 'left', i)"
    						(drop)="columnDrop($event, 'left', i)">
    						</div>

    						<div
    						class="drop-area-right"
    						(dragenter)="columnDragEnter($event, 'right', i)"
    						(dragleave)="columnDragLeave($event, 'right', i)"
    						(dragover)="columnDragover($event, 'right', i)"
    						(drop)="columnDrop($event, 'right', i)">
    						</div>
    						<div
    						*ngIf="columnDraggedHoverIndex == i && columnDraggedPosition == 'right'"
    						class="drop-indicator-right"></div>
    					</div>
    				</th>
    			</ng-container>
    			<th *ngIf="!skeleton && stickyHeader" [ngStyle]="{'width': scrollbarWidth + 'px', 'padding': 0, 'border': 0}">
    				<!--
    					Scrollbar pushes body to the left so this header column is added to push
    					the title bar the same amount and keep the header and body columns aligned.
    				-->
    			</th>
    		</tr>
    	</thead>
    	<tbody
    	*ngIf="!noData; else noDataTemplate"
    	[ngStyle]="{'overflow-y': 'scroll'}"
    	(scroll)="onScroll($event)">
    		<ng-container *ngFor="let row of model.data; let i = index">
    			<tr *ngIf="!model.isRowFiltered(i)"
    				(click)="onRowSelect(i)"
    				[attr.data-parent-row]="(model.isRowExpandable(i) ? 'true' : null)"
    				[class]="model.rowsClass[i] ? model.rowsClass[i] : null"
    				[ngClass]="{
    					'bx--data-table--selected': model.rowsSelected[i],
    					'bx--parent-row': model.isRowExpandable(i),
    					'bx--expandable-row': model.rowsExpanded[i],
    					'tbody_row--selectable': enableSingleSelect,
    					'tbody_row--success': !model.rowsSelected[i] && model.rowsContext[i] === 'success',
    					'tbody_row--warning': !model.rowsSelected[i] && model.rowsContext[i] === 'warning',
    					'tbody_row--info': !model.rowsSelected[i] && model.rowsContext[i] === 'info',
    					'tbody_row--error': !model.rowsSelected[i] && model.rowsContext[i] === 'error'
    				}">
    				<td
    				*ngIf="model.hasExpandableRows()"
    				class="bx--table-expand"
    				[ibmDataGridFocus]="isDataGrid"
    				[(columnIndex)]="columnIndex"
    				[attr.data-previous-value]="(model.rowsExpanded[i] ? 'collapsed' : null)"
    				(click)="setExpandIndex($event)">
    					<button
    					*ngIf="!skeleton && model.isRowExpandable(i)"
    					class="bx--table-expand__button"
    					[attr.aria-label]="getExpandButtonAriaLabel() | async"
    					(click)="model.expandRow(i, !model.rowsExpanded[i])">
    						<ibm-icon-chevron-right16 innerClass="bx--table-expand__svg"></ibm-icon-chevron-right16>
    					</button>
    				</td>
    				<td
    					*ngIf="!skeleton && showSelectionColumn"
    					[ibmDataGridFocus]="isDataGrid"
    					[(columnIndex)]="columnIndex"
    					(click)="setCheckboxIndex()">
    					<ibm-checkbox
    						inline="true"
    						[aria-label]="checkboxRowLabel.subject | i18nReplace:getSelectionLabelValue(row) | async"
    						[size]="size !== ('lg' ? 'sm' : 'md')"
    						[(ngModel)]="model.rowsSelected[i]"
    						(change)="onRowCheckboxChange(i)">
    					</ibm-checkbox>
    				</td>
    				<ng-container *ngFor="let item of row; let j = index">
    					<td *ngIf="model.header[j].visible"
    						[class]="model.header[j].className"
    						[ngStyle]="model.header[j].style"
    						[ibmDataGridFocus]="isDataGrid"
    						[(columnIndex)]="columnIndex"
    						(click)="setIndex(j)">
    						<span *ngIf="skeleton && i === 0"></span>
    						<ng-container *ngIf="!skeleton && !item.template">{{item.data}}</ng-container>
    						<ng-template
    							*ngIf="!skeleton"
    							[ngTemplateOutlet]="item.template" [ngTemplateOutletContext]="{data: item.data}">
    						</ng-template>
    					</td>
    				</ng-container>
    			</tr>
    			<tr
    			*ngIf="model.rowsExpanded[i] && !model.isRowFiltered(i)"
    			class="bx--expandable-row"
    			ibmExpandedRowHover
    			[attr.data-child-row]="(model.rowsExpanded[i] ? 'true' : null)">
    				<td
    					[ibmDataGridFocus]="isDataGrid"
    					[(columnIndex)]="columnIndex"
    					[attr.colspan]="row.length + 2"
    					(click)="setExpandIndex($event)">
    					<ng-container *ngIf="!firstExpandedTemplateInRow(row)">{{firstExpandedDataInRow(row)}}</ng-container>
    					<ng-template
    						[ngTemplateOutlet]="firstExpandedTemplateInRow(row)"
    						[ngTemplateOutletContext]="{data: firstExpandedDataInRow(row)}">
    					</ng-template>
    				</td>
    			</tr>
    		</ng-container>
    	</tbody>
    	<ng-template #noDataTemplate><ng-content></ng-content></ng-template>
    	<tfoot>
    		<ng-template
    			[ngTemplateOutlet]="footerTemplate">
    		</ng-template>
    		<tr *ngIf="this.model.isLoading">
    			<td class="table_loading-indicator">
    				<div class="bx--loading bx--loading--small">
    					<svg class="bx--loading__svg" viewBox="-75 -75 150 150">
    						<circle class="bx--loading__stroke" cx="0" cy="0" r="37.5" />
    					</svg>
    				</div>
    			</td>
    		</tr>
    		<tr *ngIf="this.model.isEnd">
    			<td class="table_end-indicator">
    				<h5>{{endOfDataText.subject | async}}</h5>
    				<button (click)="scrollToTop($event)" class="btn--secondary-sm">
    					{{scrollTopText.subject | async}}
    				</button>
    			</td>
    		</tr>
    	</tfoot>
    </table>

### Index

###### Properties

- Protected _expandButtonAriaLabel
- Protected _model
- Protected _sortAscendingLabel
- Protected _sortDescendingLabel
- checkboxHeaderLabel
- checkboxRowLabel
- Public columnDraggedHoverIndex
- Public columnDraggedPosition
- columnIndex
- Protected columnResizeMouseX
- Protected columnResizeWidth
- endOfDataText
- filterTitle
- Public isColumnDragging
- Protected mouseMoveSubscription
- Protected mouseUpSubscription
- scrollTopText
- selectAllCheckbox
- selectAllCheckboxSomeSelected

###### Methods

- columnDragEnd
- columnDragEnter
- columnDragLeave
- columnDragover
- columnDragStart
- columnDrop
- columnResizeEnd
- columnResizeProgress
- columnResizeStart
- firstExpandedDataInRow
- firstExpandedTemplateInRow
- getExpandButtonAriaLabel
- getSelectionLabelValue
- getSortAscendingLabel
- getSortDescendingLabel
- handleTabIndex
- ngAfterViewInit
- onRowCheckboxChange
- onRowSelect
- onScroll
- onSelectAllCheckboxChange
- scrollToTop
- setCheckboxIndex
- setExpandIndex
- setIndex
- Static setTabIndex
- Static skeletonModel
- updateSelectAllCheckbox

###### Inputs

- columnsDraggable
- columnsResizable
- enableRowSelect
- enableSingleSelect
- expandButtonAriaLabel
- footerTemplate
- isDataGrid
- model
- scrollLoadDistance
- selectionLabelColumn
- showSelectionColumn
- size
- skeleton
- sortAscendingLabel
- sortDescendingLabel
- stickyHeader
- striped
- translations

###### Outputs

- deselectAll
- deselectRow
- scrollLoad
- selectAll
- selectRow
- sort

###### Accessors

- model
- enableRowSelect
- expandButtonAriaLabel
- sortDescendingLabel
- sortAscendingLabel
- translations
- noData
- scrollbarWidth

### Constructor

`constructor(elementRef: ElementRef, applicationRef: ApplicationRef, i18n: I18n)`
Defined in src/table/table.component.ts:755

Creates an instance of Table.

Parameters :

Name | Type | Optional
elementRef | `ElementRef` | No
applicationRef | `ApplicationRef` | No
i18n | `I18n` | No

### Inputs

`columnsDraggable` | Set to `true` to enable users to drag and drop columns. Changing the column order in table changes table model. Be aware of it when you add additional data to the model. Default value : `false`
Defined in src/table/table.component.ts:598

`columnsResizable` | Set to `true` to enable users to resize columns. Works for columns with width set in pixels. Default value : `false`
Defined in src/table/table.component.ts:589

`enableRowSelect` | Controls whether to show the selection checkboxes column or not. Type : `boolean`
Defined in src/table/table.component.ts:553

`enableSingleSelect` | Controls whether to enable multiple or single row selection. Default value : `false`
Defined in src/table/table.component.ts:573

`expandButtonAriaLabel` |
Defined in src/table/table.component.ts:601

`footerTemplate` | Set footer template to customize what is displayed in the tfoot section of the table Type : `TemplateRef<any>`
Defined in src/table/table.component.ts:679

`isDataGrid` | Set to `true` for a data grid with keyboard interactions. Default value : `false`
Defined in src/table/table.component.ts:504

`model` | `TableModel` with data the table is to display.
Defined in src/table/table.component.ts:512

`scrollLoadDistance` | Distance (in px) from the bottom that view has to reach before `scrollLoad` event is emitted. Default value : `0`
Defined in src/table/table.component.ts:581

`selectionLabelColumn`

Used to populate the row selection checkbox label with a useful value if set.

Example:

         * <ibm-table [selectionLabelColumn]="0"></ibm-table>
         * <!-- results in aria-label="Select first column value"
         * (where "first column value" is the value of the first column in the row -->
         *

Type : `number`

Defined in src/table/table.component.ts:691

`showSelectionColumn` | Controls whether to show the selection checkboxes column or not. Default value : `true`
Defined in src/table/table.component.ts:566

`size` | Size of the table rows. Type : `"sm" | "md" | "lg"` Default value : `"md"`
Defined in src/table/table.component.ts:496

`skeleton` | Set to `true` for a loading table. Default value : `false`
Defined in src/table/table.component.ts:500

`sortAscendingLabel` |
Defined in src/table/table.component.ts:615

`sortDescendingLabel` |
Defined in src/table/table.component.ts:608

`stickyHeader` | Set to `true` to stick the header to the top of the table Default value : `false`
Defined in src/table/table.component.ts:674

`striped` | Set to `false` to remove table rows (zebra) stripes. Default value : `true`
Defined in src/table/table.component.ts:669

`translations`

Expects an object that contains some or all of:

         * {
         *        "FILTER": "Filter",
         *        "END_OF_DATA": "You've reached the end of your content",
         *        "SCROLL_TOP": "Scroll to top",
         *        "CHECKBOX_HEADER": "Select all rows",
         *        "CHECKBOX_ROW": "Select row"
         * }
         *

Defined in src/table/table.component.ts:635

### Outputs

`deselectAll` | Emits if all rows are deselected. `$event` Type: `EventEmitter`
Defined in src/table/table.component.ts:711

`deselectRow` | Emits if a single row is deselected. `$event` Type: `EventEmitter`
Defined in src/table/table.component.ts:725

`scrollLoad` | Emits when table requires more data to be loaded. `$event` Type: `EventEmitter`
Defined in src/table/table.component.ts:732

`selectAll` | Emits if all rows are selected. `$event` Type: `EventEmitter`
Defined in src/table/table.component.ts:704

`selectRow` | Emits if a single row is selected. `$event` Type: `EventEmitter`
Defined in src/table/table.component.ts:718

`sort` | Emits an index of the column that wants to be sorted. `$event` Type: `EventEmitter`
Defined in src/table/table.component.ts:697

### Methods

columnDragEnd
`columnDragEnd(event, columnIndex)`
Defined in src/table/table.component.ts:894

Parameters :

Name | Optional
event | No
columnIndex | No

Returns : `void`

columnDragEnter
`columnDragEnter(event, position, columnIndex)`
Defined in src/table/table.component.ts:899

Parameters :

Name | Optional
event | No
position | No
columnIndex | No

Returns : `void`

columnDragLeave
`columnDragLeave(event, position, columnIndex)`
Defined in src/table/table.component.ts:904

Parameters :

Name | Optional
event | No
position | No
columnIndex | No

Returns : `void`

columnDragover
`columnDragover(event, position, columnIndex)`
Defined in src/table/table.component.ts:908

Parameters :

Name | Optional
event | No
position | No
columnIndex | No

Returns : `void`

columnDragStart
`columnDragStart(event, columnIndex)`
Defined in src/table/table.component.ts:888

Parameters :

Name | Optional
event | No
columnIndex | No

Returns : `void`

columnDrop
`columnDrop(event, position, columnIndex)`
Defined in src/table/table.component.ts:916

Parameters :

Name | Optional
event | No
position | No
columnIndex | No

Returns : `void`

columnResizeEnd
`columnResizeEnd(event, column)`
Defined in src/table/table.component.ts:788

Parameters :

Name | Optional
event | No
column | No

Returns : `void`

columnResizeProgress
`columnResizeProgress(event, column)`
Defined in src/table/table.component.ts:783

Parameters :

Name | Optional
event | No
column | No

Returns : `void`

columnResizeStart
`columnResizeStart(event, column)`
Defined in src/table/table.component.ts:770

Parameters :

Name | Optional
event | No
column | No

Returns : `void`

firstExpandedDataInRow
`firstExpandedDataInRow(row)`
Defined in src/table/table.component.ts:931

Parameters :

Name | Optional
row | No

Returns : `any`

firstExpandedTemplateInRow
`firstExpandedTemplateInRow(row)`
Defined in src/table/table.component.ts:939

Parameters :

Name | Optional
row | No

Returns : `any`

getExpandButtonAriaLabel
`getExpandButtonAriaLabel()`
Defined in src/table/table.component.ts:1003
Returns : `any`

getSelectionLabelValue
`getSelectionLabelValue(row: TableItem[])`
Defined in src/table/table.component.ts:996

Parameters :

Name | Type | Optional
row | `TableItem[]` | No

Returns : `{ value: any; }`

getSortAscendingLabel
`getSortAscendingLabel()`
Defined in src/table/table.component.ts:1009
Returns : `any`

getSortDescendingLabel
`getSortDescendingLabel()`
Defined in src/table/table.component.ts:1006
Returns : `any`

handleTabIndex
`handleTabIndex()`
Defined in src/table/table.component.ts:957
Returns : `void`

ngAfterViewInit
`ngAfterViewInit()`
Defined in src/table/table.component.ts:764
Returns : `void`

onRowCheckboxChange
`onRowCheckboxChange(index: number)`
Defined in src/table/table.component.ts:848

Triggered when a single row is clicked. Updates the header checkbox state. Emits the `selectRow` or `deselectRow` event.

Parameters :

Name | Type | Optional
index | `number` | No

Returns : `void`

onRowSelect
`onRowSelect(index: number)`
Defined in src/table/table.component.ts:793

Parameters :

Name | Type | Optional
index | `number` | No

Returns : `void`

onScroll
`onScroll(event)`
Defined in src/table/table.component.ts:878

Triggered when the user scrolls on the `<tbody>` element. Emits the `scrollLoad` event.

Parameters :

Name | Optional
event | No

Returns : `void`

onSelectAllCheckboxChange
`onSelectAllCheckboxChange()`
Defined in src/table/table.component.ts:821
Triggered whenever the header checkbox is clicked. Updates all the checkboxes in the table view. Emits the `selectAll` or `deselectAll` event. Returns : `void`

scrollToTop
`scrollToTop(event)`
Defined in src/table/table.component.ts:952

Triggered when the user scrolls on the `<tbody>` element. Emits the `scrollLoad` event.

Parameters :

Name | Optional
event | No

Returns : `void`

setCheckboxIndex
`setCheckboxIndex()`
Defined in src/table/table.component.ts:984
Returns : `void`

setExpandIndex
`setExpandIndex(event)`
Defined in src/table/table.component.ts:992

Parameters :

Name | Optional
event | No

Returns : `void`

setIndex
`setIndex(columnIndex)`
Defined in src/table/table.component.ts:976

Parameters :

Name | Optional
columnIndex | No

Returns : `void`

Static setTabIndex
`setTabIndex(element: HTMLElement, index: "undefined" | "0")`
Defined in src/table/table.component.ts:480

Parameters :

Name | Type | Optional
element | `HTMLElement` | No
index | `"undefined" | "0"` | No

Returns : `void`

Static skeletonModel
`skeletonModel(rowCount: number, columnCount: number)`
Defined in src/table/table.component.ts:461

Creates a skeleton model with a row and column count specified by the user

Example:

         * this.model = Table.skeletonModel(5, 5);
         *

Parameters :

Name | Type | Optional
rowCount | `number` | No
columnCount | `number` | No

Returns : `any`

updateSelectAllCheckbox
`updateSelectAllCheckbox()`
Defined in src/table/table.component.ts:803
Returns : `void`

### Properties

Protected _expandButtonAriaLabel
`_expandButtonAriaLabel: ```
Default value : `this.i18n.getOverridable("TABLE.EXPAND_BUTTON")`
Defined in src/table/table.component.ts:748

Protected _model
`_model: `TableModel``
Type : `TableModel`
Defined in src/table/table.component.ts:746

Protected _sortAscendingLabel
`_sortAscendingLabel: ```
Default value : `this.i18n.getOverridable("TABLE.SORT_ASCENDING")`
Defined in src/table/table.component.ts:750

Protected _sortDescendingLabel
`_sortDescendingLabel: ```
Default value : `this.i18n.getOverridable("TABLE.SORT_DESCENDING")`
Defined in src/table/table.component.ts:749

checkboxHeaderLabel
`checkboxHeaderLabel: ```
Default value : `this.i18n.getOverridable("TABLE.CHECKBOX_HEADER")`
Defined in src/table/table.component.ts:644

checkboxRowLabel
`checkboxRowLabel: ```
Default value : `this.i18n.getOverridable("TABLE.CHECKBOX_ROW")`
Defined in src/table/table.component.ts:645

Public columnDraggedHoverIndex
`columnDraggedHoverIndex: ```
Default value : `-1`
Defined in src/table/table.component.ts:743

Public columnDraggedPosition
`columnDraggedPosition: `string``
Type : `string`
Default value : `""`
Defined in src/table/table.component.ts:744

columnIndex
`columnIndex: `number``
Type : `number`
Default value : `0`
Defined in src/table/table.component.ts:740

Protected columnResizeMouseX
`columnResizeMouseX: `number``
Type : `number`
Defined in src/table/table.component.ts:753

Protected columnResizeWidth
`columnResizeWidth: `number``
Type : `number`
Defined in src/table/table.component.ts:752

endOfDataText
`endOfDataText: ```
Default value : `this.i18n.getOverridable("TABLE.END_OF_DATA")`
Defined in src/table/table.component.ts:646

filterTitle
`filterTitle: ```
Default value : `this.i18n.getOverridable("TABLE.FILTER")`
Defined in src/table/table.component.ts:648

Public isColumnDragging
`isColumnDragging: ```
Default value : `false`
Defined in src/table/table.component.ts:742

Protected mouseMoveSubscription
`mouseMoveSubscription: `Subscription``
Type : `Subscription`
Defined in src/table/table.component.ts:754

Protected mouseUpSubscription
`mouseUpSubscription: `Subscription``
Type : `Subscription`
Defined in src/table/table.component.ts:755

scrollTopText
`scrollTopText: ```
Default value : `this.i18n.getOverridable("TABLE.SCROLL_TOP")`
Defined in src/table/table.component.ts:647

selectAllCheckbox
`selectAllCheckbox: ```
Default value : `false`
Defined in src/table/table.component.ts:655
Controls if all checkboxes are viewed as selected.

selectAllCheckboxSomeSelected
`selectAllCheckboxSomeSelected: ```
Default value : `false`
Defined in src/table/table.component.ts:662
Controls the indeterminate state of the header checkbox.

### Accessors

model
get `model()`
Defined in src/table/table.component.ts:541
set `model(m)`
Defined in src/table/table.component.ts:512

`TableModel` with data the table is to display.

Parameters :

Name | Optional
m | No

Returns : `void`

enableRowSelect
get `enableRowSelect()`
Defined in src/table/table.component.ts:557
set `enableRowSelect(value: boolean)`
Defined in src/table/table.component.ts:553

Controls whether to show the selection checkboxes column or not.

Parameters :

Name | Type | Optional
value | `boolean` | No

Returns : `void`

expandButtonAriaLabel
get `expandButtonAriaLabel()`
Defined in src/table/table.component.ts:604
set `expandButtonAriaLabel(value)`
Defined in src/table/table.component.ts:601

Parameters :

Name | Optional
value | No

Returns : `void`

sortDescendingLabel
get `sortDescendingLabel()`
Defined in src/table/table.component.ts:611
set `sortDescendingLabel(value)`
Defined in src/table/table.component.ts:608

Parameters :

Name | Optional
value | No

Returns : `void`

sortAscendingLabel
get `sortAscendingLabel()`
Defined in src/table/table.component.ts:618
set `sortAscendingLabel(value)`
Defined in src/table/table.component.ts:615

Parameters :

Name | Optional
value | No

Returns : `void`

translations
set `translations(value)`
Defined in src/table/table.component.ts:635

Expects an object that contains some or all of:

         * {
         *        "FILTER": "Filter",
         *        "END_OF_DATA": "You've reached the end of your content",
         *        "SCROLL_TOP": "Scroll to top",
         *        "CHECKBOX_HEADER": "Select all rows",
         *        "CHECKBOX_ROW": "Select row"
         * }
         *

Parameters :

Name | Optional
value | No

Returns : `void`

noData
get `noData()`
Defined in src/table/table.component.ts:734

scrollbarWidth
get `scrollbarWidth()`
Defined in src/table/table.component.ts:927

    import {
    	Component,
    	ApplicationRef,
    	Input,
    	Output,
    	EventEmitter,
    	ElementRef,
    	AfterViewInit,
    	TemplateRef
    } from "@angular/core";
    import { Subscription, fromEvent, Observable } from "rxjs";

    import { TableModel } from "./table.module";
    import { TableHeaderItem } from "./table-header-item.class";
    import { TableItem } from "./table-item.class";
    import { getScrollbarWidth } from "../common/utils";
    import { getFocusElementList, tabbableSelectorIgnoreTabIndex } from "../common/tab.service";
    import { I18n, Overridable } from "./../i18n/i18n.module";
    import { merge } from "./../utils/object";

    export interface TableTranslations {
    	FILTER: string;
    	END_OF_DATA: string;
    	SCROLL_TOP: string;
    	CHECKBOX_HEADER: string;
    	CHECKBOX_ROW: string;
    }

    /**
     * Build your table with this component by extending things that differ from default.
     *
     * [See demo](../../?path=/story/table--basic)
     *
     * Instead of the usual write-your-own-html approach you had with `<table>`,
     * carbon table uses model-view-controller approach.
     *
     * Here, you create a view (with built-in controller) and provide it a model.
     * Changes you make to the model are reflected in the view. Provide same model you use
     * in the table to the `<ibm-pagination>` components.
     * They provide a different view over the same data.
     *
     * ## Basic usage
     *
     * ```html
     * <ibm-table [model]="model"></ibm-table>
     * ```
     *
     * ```typescript
     * public model = new TableModel();
     *
     * this.model.data = [
     * 	[new TableItem({data: "asdf"}), new TableItem({data: "qwer"})],
     * 	[new TableItem({data: "csdf"}), new TableItem({data: "zwer"})],
     * 	[new TableItem({data: "bsdf"}), new TableItem({data: "swer"})],
     * 	[new TableItem({data: "csdf"}), new TableItem({data: "twer"})]
     * ];
     * ```
     *
     * ## Customization
     *
     * If you have custom data in your table, you need a way to display it. You can do that
     * by providing a template to `TableItem`.
     *
     * ```html
     * <ng-template #customTableItemTemplate let-data="data">
     * 	<a [routerLink]="data.link">{{data.name}} {{data.surname}}</a>
     * </ng-template>
     * ```
     *
     * ```typescript
     * customTableItemTemplate: TemplateRef<any>;
     *
     * this.customModel.data = [
     * 	[new TableItem({data: "asdf"}), new TableItem({data: {name: "Lessy", link: "/table"}, template: this.customTableItemTemplate})],
     * 	[new TableItem({data: "csdf"}), new TableItem({data: "swer"})],
     * 	[new TableItem({data: "bsdf"}), new TableItem({data: {name: "Alice", surname: "Bob"}, template: this.customTableItemTemplate})],
     * 	[new TableItem({data: "csdf"}), new TableItem({data: "twer"})],
     * ];
     * ```
     *
     * ### Sorting and filtering
     *
     * In case you need custom sorting and/or filtering you should subclass `TableHeaderItem`
     * and override needed functions.
     *
     * ```typescript
     * class FilterableHeaderItem extends TableHeaderItem {
     * 	// custom filter function
     * 	filter(item: TableItem): boolean {
     * 		if (typeof item.data === "string" && item.data.toLowerCase().indexOf(this.filterData.data.toLowerCase()) >= 0 ||
     * 		item.data.name && item.data.name.toLowerCase().indexOf(this.filterData.data.toLowerCase()) >= 0 ||
     * 		item.data.surname && item.data.surname.toLowerCase().indexOf(this.filterData.data.toLowerCase()) >= 0) {
     * 			return false;
     * 		}
     * 		return true;
     * 	}
     *
     * 	set filterCount(n) {}
     * 	get filterCount() {
     * 		return (this.filterData && this.filterData.data && this.filterData.data.length > 0) ? 1 : 0;
     * 	}
     *
     * 	// used for custom sorting
     * 	compare(one: TableItem, two: TableItem) {
     * 		const stringOne = (one.data.name || one.data.surname || one.data).toLowerCase();
     * 		const stringTwo = (two.data.name || two.data.surname || two.data).toLowerCase();
     *
     * 		if (stringOne > stringTwo) {
     * 			return 1;
     * 		} else if (stringOne < stringTwo) {
     * 			return -1;
     * 		} else {
     * 			return 0;
     * 		}
     * 	}
     * }
     * ```
     *
     * See `TableHeaderItem` class for more information.
     *
     * ## No data template
     *
     * When table has no data to show, it can show a message you provide it instead.
     *
     * ```html
     * <ibm-table [model]="model">No data.</ibm-table>
     * ```
     *
     * ... will show `No data.` message, but you can get creative and provide any template you want
     * to replace table's default `tbody`.
     *
     * ## Use pagination as table footer
     *
     * ```html
     * <ibm-pagination [model]="model" (selectPage)="selectPage($event)"></ibm-pagination>
     * ```
     *
     * `selectPage()` function should fetch the data from backend, create new `data`, apply it to `model.data`,
     * and update `model.currentPage`.
     *
     * If the data your server returns is a two dimensional array of objects, it would look something like this:
     *
     * ```typescript
     * selectPage(page) {
     * 	this.getPage(page).then((data: Array<Array<any>>) => {
     * 		// set the data and update page
     * 		this.model.data = this.prepareData(data);
     * 		this.model.currentPage = page;
     * 	});
     * }
     *
     * protected prepareData(data: Array<Array<any>>) {
     * 	// create new data from the service data
     * 	let newData = [];
     * 	data.forEach(dataRow => {
     * 		let row = [];
     * 		dataRow.forEach(dataElement => {
     * 			row.push(new TableItem({
     * 				data: dataElement,
     * 				template: typeof dataElement === "string" ? undefined : this.paginationTableItemTemplate
     * 				// your template can handle all the data types so you don't have to conditionally set it
     * 				// you can also set different templates for different columns based on index
     * 			}));
     * 		});
     * 		newData.push(row);
     * 	});
     * 	return newData;
     * }
     * ```
     *
     * <example-url>../../iframe.html?id=table--basic</example-url>
     *
     * @export
     * @class Table
     * @implements {AfterContentChecked}
     */
    @Component({
    	selector: "ibm-table",
    	template: `
    	<table
    	class="bx--data-table bx--data-table--sort"
    	[ngClass]="{
    		'bx--data-table--compact': size === 'sm',
    		'bx--data-table--tall': size === 'lg',
    		'bx--data-table--zebra': striped,
    		'bx--skeleton': skeleton
    	}">
    		<thead>
    			<tr>
    				<th
    					class="bx--table-expand"
    					*ngIf="model.hasExpandableRows()"
    					[ibmDataGridFocus]="isDataGrid"
    					[(columnIndex)]="columnIndex"
    					(click)="setExpandIndex($event)">
    				</th>
    				<th
    					class="bx--table-column-checkbox"
    					*ngIf="!skeleton && showSelectionColumn"
    					[ibmDataGridFocus]="isDataGrid"
    					[(columnIndex)]="columnIndex"
    					(click)="setCheckboxIndex()"
    					style="width: 10px;">
    					<ibm-checkbox
    						inline="true"
    						[size]="size !== ('lg' ? 'sm' : 'md')"
    						[(ngModel)]="selectAllCheckbox"
    						[indeterminate]="selectAllCheckboxSomeSelected"
    						[attr.aria-label]="checkboxHeaderLabel.subject | async"
    						(change)="onSelectAllCheckboxChange()">
    					</ibm-checkbox>
    				</th>
    				<ng-container *ngFor="let column of model.header; let i = index">
    					<th
    						[ngClass]='{"thead_action": column.filterTemplate || this.sort.observers.length > 0}'
    						*ngIf="column.visible"
    						[class]="column.className"
    						[ngStyle]="column.style"
    						[ibmDataGridFocus]="isDataGrid"
    						[(columnIndex)]="columnIndex"
    						[draggable]="columnsDraggable"
    						(dragstart)="columnDragStart($event, i)"
    						(dragend)="columnDragEnd($event, i)"
    						(click)="setIndex(i)">
    						<div
    							*ngIf="columnsResizable"
    							class="column-resize-handle"
    							(mousedown)="columnResizeStart($event, column)">
    						</div>
    						<button
    							class="bx--table-sort"
    							*ngIf="!skeleton && this.sort.observers.length > 0 && column.sortable"
    							[attr.aria-label]="(column.sorted && column.ascending ? getSortDescendingLabel() : getSortAscendingLabel()) | async"
    							aria-live="polite"
    							[ngClass]="{
    								'bx--table-sort--active': column.sorted,
    								'bx--table-sort--ascending': column.ascending
    							}"
    							(click)="sort.emit(i)">
    							<span
    								*ngIf="!column.template"
    								[title]="column.data"
    								tabindex="-1">
    								{{column.data}}
    							</span>
    							<ng-template
    								[ngTemplateOutlet]="column.template" [ngTemplateOutletContext]="{data: column.data}">
    							</ng-template>
    							<svg
    								focusable="false"
    								preserveAspectRatio="xMidYMid meet"
    								style="will-change: transform;"
    								xmlns="http://www.w3.org/2000/svg"
    								class="bx--table-sort__icon"
    								width="16"
    								height="16"
    								viewBox="0 0 16 16"
    								aria-hidden="true">
    								<path d="M12.3 9.3l-3.8 3.8V1h-1v12.1L3.7 9.3 3 10l5 5 5-5z"></path>
    							</svg>
    							<svg
    								focusable="false"
    								preserveAspectRatio="xMidYMid meet"
    								style="will-change: transform;"
    								xmlns="http://www.w3.org/2000/svg"
    								class="bx--table-sort__icon-unsorted"
    								width="16"
    								height="16"
    								viewBox="0 0 16 16"
    								aria-hidden="true">
    								<path d="M13.8 10.3L12 12.1V2h-1v10.1l-1.8-1.8-.7.7 3 3 3-3zM4.5 2l-3 3 .7.7L4 3.9V14h1V3.9l1.8 1.8.7-.7z"></path>
    							</svg>
    						</button>
    						<span
    							class="bx--table-header-label"
    							*ngIf="!skeleton && this.sort.observers.length === 0 || (this.sort.observers.length > 0 && !column.sortable)">
    							<span *ngIf="!column.template" [title]="column.data">{{column.data}}</span>
    							<ng-template
    								[ngTemplateOutlet]="column.template" [ngTemplateOutletContext]="{data: column.data}">
    							</ng-template>
    						</span>
    						<button
    							[ngClass]="{'active': column.filterCount > 0}"
    							*ngIf="column.filterTemplate"
    							type="button"
    							aria-expanded="false"
    							aria-haspopup="true"
    							[ibmTooltip]="column.filterTemplate"
    							trigger="click"
    							[title]="filterTitle.subject | async"
    							placement="bottom,top"
    							[data]="column.filterData">
    							<svg
    								xmlns="http://www.w3.org/2000/svg"
    								class="icon--sm"
    								width="16"
    								height="16"
    								viewBox="0 0 16 16">
    								<path d="M0 0v3l6 8v5h4v-5l6-8V0H0zm9 10.7V15H7v-4.3L1.3 3h13.5L9 10.7z"/>
    							</svg>
    							<span *ngIf="column.filterCount > 0">
    								{{column.filterCount}}
    							</span>
    						</button>
    						<div
    						*ngIf="columnsDraggable && isColumnDragging"
    						class="drop-area">
    							<div
    							*ngIf="columnDraggedHoverIndex == i && columnDraggedPosition == 'left'"
    							class="drop-indicator-left"></div>
    							<div
    							class="drop-area-left"
    							(dragenter)="columnDragEnter($event, 'left', i)"
    							(dragleave)="columnDragLeave($event, 'left', i)"
    							(dragover)="columnDragover($event, 'left', i)"
    							(drop)="columnDrop($event, 'left', i)">
    							</div>

    							<div
    							class="drop-area-right"
    							(dragenter)="columnDragEnter($event, 'right', i)"
    							(dragleave)="columnDragLeave($event, 'right', i)"
    							(dragover)="columnDragover($event, 'right', i)"
    							(drop)="columnDrop($event, 'right', i)">
    							</div>
    							<div
    							*ngIf="columnDraggedHoverIndex == i && columnDraggedPosition == 'right'"
    							class="drop-indicator-right"></div>
    						</div>
    					</th>
    				</ng-container>
    				<th *ngIf="!skeleton && stickyHeader" [ngStyle]="{'width': scrollbarWidth + 'px', 'padding': 0, 'border': 0}">
    					<!--
    						Scrollbar pushes body to the left so this header column is added to push
    						the title bar the same amount and keep the header and body columns aligned.
    					-->
    				</th>
    			</tr>
    		</thead>
    		<tbody
    		*ngIf="!noData; else noDataTemplate"
    		[ngStyle]="{'overflow-y': 'scroll'}"
    		(scroll)="onScroll($event)">
    			<ng-container *ngFor="let row of model.data; let i = index">
    				<tr *ngIf="!model.isRowFiltered(i)"
    					(click)="onRowSelect(i)"
    					[attr.data-parent-row]="(model.isRowExpandable(i) ? 'true' : null)"
    					[class]="model.rowsClass[i] ? model.rowsClass[i] : null"
    					[ngClass]="{
    						'bx--data-table--selected': model.rowsSelected[i],
    						'bx--parent-row': model.isRowExpandable(i),
    						'bx--expandable-row': model.rowsExpanded[i],
    						'tbody_row--selectable': enableSingleSelect,
    						'tbody_row--success': !model.rowsSelected[i] && model.rowsContext[i] === 'success',
    						'tbody_row--warning': !model.rowsSelected[i] && model.rowsContext[i] === 'warning',
    						'tbody_row--info': !model.rowsSelected[i] && model.rowsContext[i] === 'info',
    						'tbody_row--error': !model.rowsSelected[i] && model.rowsContext[i] === 'error'
    					}">
    					<td
    					*ngIf="model.hasExpandableRows()"
    					class="bx--table-expand"
    					[ibmDataGridFocus]="isDataGrid"
    					[(columnIndex)]="columnIndex"
    					[attr.data-previous-value]="(model.rowsExpanded[i] ? 'collapsed' : null)"
    					(click)="setExpandIndex($event)">
    						<button
    						*ngIf="!skeleton && model.isRowExpandable(i)"
    						class="bx--table-expand__button"
    						[attr.aria-label]="getExpandButtonAriaLabel() | async"
    						(click)="model.expandRow(i, !model.rowsExpanded[i])">
    							<ibm-icon-chevron-right16 innerClass="bx--table-expand__svg"></ibm-icon-chevron-right16>
    						</button>
    					</td>
    					<td
    						*ngIf="!skeleton && showSelectionColumn"
    						[ibmDataGridFocus]="isDataGrid"
    						[(columnIndex)]="columnIndex"
    						(click)="setCheckboxIndex()">
    						<ibm-checkbox
    							inline="true"
    							[aria-label]="checkboxRowLabel.subject | i18nReplace:getSelectionLabelValue(row) | async"
    							[size]="size !== ('lg' ? 'sm' : 'md')"
    							[(ngModel)]="model.rowsSelected[i]"
    							(change)="onRowCheckboxChange(i)">
    						</ibm-checkbox>
    					</td>
    					<ng-container *ngFor="let item of row; let j = index">
    						<td *ngIf="model.header[j].visible"
    							[class]="model.header[j].className"
    							[ngStyle]="model.header[j].style"
    							[ibmDataGridFocus]="isDataGrid"
    							[(columnIndex)]="columnIndex"
    							(click)="setIndex(j)">
    							<span *ngIf="skeleton && i === 0"></span>
    							<ng-container *ngIf="!skeleton && !item.template">{{item.data}}</ng-container>
    							<ng-template
    								*ngIf="!skeleton"
    								[ngTemplateOutlet]="item.template" [ngTemplateOutletContext]="{data: item.data}">
    							</ng-template>
    						</td>
    					</ng-container>
    				</tr>
    				<tr
    				*ngIf="model.rowsExpanded[i] && !model.isRowFiltered(i)"
    				class="bx--expandable-row"
    				ibmExpandedRowHover
    				[attr.data-child-row]="(model.rowsExpanded[i] ? 'true' : null)">
    					<td
    						[ibmDataGridFocus]="isDataGrid"
    						[(columnIndex)]="columnIndex"
    						[attr.colspan]="row.length + 2"
    						(click)="setExpandIndex($event)">
    						<ng-container *ngIf="!firstExpandedTemplateInRow(row)">{{firstExpandedDataInRow(row)}}</ng-container>
    						<ng-template
    							[ngTemplateOutlet]="firstExpandedTemplateInRow(row)"
    							[ngTemplateOutletContext]="{data: firstExpandedDataInRow(row)}">
    						</ng-template>
    					</td>
    				</tr>
    			</ng-container>
    		</tbody>
    		<ng-template #noDataTemplate><ng-content></ng-content></ng-template>
    		<tfoot>
    			<ng-template
    				[ngTemplateOutlet]="footerTemplate">
    			</ng-template>
    			<tr *ngIf="this.model.isLoading">
    				<td class="table_loading-indicator">
    					<div class="bx--loading bx--loading--small">
    						<svg class="bx--loading__svg" viewBox="-75 -75 150 150">
    							<circle class="bx--loading__stroke" cx="0" cy="0" r="37.5" />
    						</svg>
    					</div>
    				</td>
    			</tr>
    			<tr *ngIf="this.model.isEnd">
    				<td class="table_end-indicator">
    					<h5>{{endOfDataText.subject | async}}</h5>
    					<button (click)="scrollToTop($event)" class="btn--secondary-sm">
    						{{scrollTopText.subject | async}}
    					</button>
    				</td>
    			</tr>
    		</tfoot>
    	</table>
    	`
    })
    export class Table implements AfterViewInit {
    	/**
    	 * Creates a skeleton model with a row and column count specified by the user
    	 *
    	 * Example:
    	 *
    	 * ```typescript
    	 * this.model = Table.skeletonModel(5, 5);
    	 * ```
    	 *
    	 * @param {number} rowCount
    	 * @param {number} columnCount
    	 */
    	static skeletonModel(rowCount: number, columnCount: number) {
    		const model = new TableModel();
    		let header = new Array<TableHeaderItem>();
    		let data = new Array<Array<TableItem>>();
    		let row = new Array<TableItem>();

    		for (let i = 0; i < columnCount; i++) {
    			header.push(new TableHeaderItem());
    			row.push(new TableItem());
    		}
    		for (let i = 0; i < rowCount - 1; i++) {
    			data.push(row);
    		}

    		model.header = header;
    		model.data = data;
    		return model;
    	}

    	static setTabIndex(element: HTMLElement, index: -1 | 0) {
    		const focusElementList = getFocusElementList(element, tabbableSelectorIgnoreTabIndex);
    		if (element.firstElementChild && element.firstElementChild.classList.contains("bx--table-sort")) {
    			focusElementList[1].tabIndex = index;
    		} else if (focusElementList.length > 0) {
    			focusElementList[0].tabIndex = index;
    		} else {
    			element.tabIndex = index;
    		}
    	}

    	/**
    	 * Size of the table rows.
    	 *
    	 * @type {("sm" | "md" | "lg")}
    	 */
    	@Input() size: "sm" | "md" | "lg" = "md";
    	/**
    	 * Set to `true` for a loading table.
    	 */
    	@Input() skeleton = false;
    	/**
    	 * Set to `true` for a data grid with keyboard interactions.
    	 */
    	@Input() isDataGrid = false;

    	/**
    	 * `TableModel` with data the table is to display.
    	 *
    	 * @type {TableModel}
    	 */
    	@Input()
    	set model(m: TableModel) {
    		if (this._model) {
    			this._model.dataChange.unsubscribe();
    			this._model.rowsSelectedChange.unsubscribe();
    		}

    		this._model = m;
    		this._model.rowsSelectedChange.subscribe(() => this.updateSelectAllCheckbox());
    		this._model.dataChange.subscribe(() => {
    			this.updateSelectAllCheckbox();
    			if (this.isDataGrid) {
    				this.handleTabIndex();
    			}
    		});
    		if (this.isDataGrid) {
    			this._model.rowsExpandedChange.subscribe(() => {
    				// Allows the expanded row to have a focus state when it exists in the DOM
    				setTimeout(() => {
    					const expandedRows = this.elementRef.nativeElement.querySelectorAll(".bx--expandable-row:not(.bx--parent-row)");
    					Array.from<any>(expandedRows).forEach(row => {
    						if (row.firstElementChild.tabIndex === undefined || row.firstElementChild.tabIndex !== -1) {
    							row.firstElementChild.tabIndex = -1;
    						}
    					});
    				});
    			});
    		}
    	}

    	get model(): TableModel {
    		return this._model;
    	}

    	/**
    	 * Controls whether to show the selection checkboxes column or not.
    	 *
    	 * @deprecated in the next major carbon-components-angular version in favour of
    	 * `showSelectionColumn` because of new attribute `enableSingleSelect`
    	 *  please use `showSelectionColumn` instead
    	 */
    	@Input()
    	set enableRowSelect(value: boolean) {
    		this.showSelectionColumn = value;
    	}

    	get enableRowSelect () {
    		return this.showSelectionColumn;
    	}

    	/**
    	 * Controls whether to show the selection checkboxes column or not.
    	 *
    	 * @type {boolean}
    	 */
    	@Input() showSelectionColumn = true;

    	/**
    	 * Controls whether to enable multiple or single row selection.
    	 *
    	 * @type {boolean}
    	 */
    	@Input() enableSingleSelect = false;

    	/**
    	 * Distance (in px) from the bottom that view has to reach before
    	 * `scrollLoad` event is emitted.
    	 *
    	 * @type {number}
    	 */
    	@Input() scrollLoadDistance = 0;

    	/**
    	 * Set to `true` to enable users to resize columns.
    	 *
    	 * Works for columns with width set in pixels.
    	 *
    	 */
    	@Input() columnsResizable = false;

    	/**
    	 * Set to `true` to enable users to drag and drop columns.
    	 *
    	 * Changing the column order in table changes table model. Be aware of it when you add additional data
    	 * to the model.
    	 *
    	 */
    	@Input() columnsDraggable = false;

    	@Input()
    	set expandButtonAriaLabel(value: string | Observable<string>) {
    		this._expandButtonAriaLabel.override(value);
    	}
    	get expandButtonAriaLabel() {
    		return this._expandButtonAriaLabel.value;
    	}
    	@Input()
    	set sortDescendingLabel(value: string | Observable<string>) {
    		this._sortDescendingLabel.override(value);
    	}
    	get sortDescendingLabel() {
    		return this._sortDescendingLabel.value;
    	}
    	@Input()
    	set sortAscendingLabel(value: string | Observable<string>) {
    		this._sortAscendingLabel.override(value);
    	}
    	get sortAscendingLabel() {
    		return this._sortAscendingLabel.value;
    	}

    	/**
    	 * Expects an object that contains some or all of:
    	 * ```
    	 * {
    	 *		"FILTER": "Filter",
    	 *		"END_OF_DATA": "You've reached the end of your content",
    	 *		"SCROLL_TOP": "Scroll to top",
    	 *		"CHECKBOX_HEADER": "Select all rows",
    	 *		"CHECKBOX_ROW": "Select row"
    	 * }
    	 * ```
    	 */
    	@Input()
    	set translations (value) {
    		const valueWithDefaults = merge(this.i18n.getMultiple("TABLE"), value);
    		this.filterTitle.override(valueWithDefaults.FILTER);
    		this.endOfDataText.override(valueWithDefaults.END_OF_DATA);
    		this.scrollTopText.override(valueWithDefaults.SCROLL_TOP);
    		this.checkboxHeaderLabel.override(valueWithDefaults.CHECKBOX_HEADER);
    		this.checkboxRowLabel.override(valueWithDefaults.CHECKBOX_ROW);
    }

    	checkboxHeaderLabel = this.i18n.getOverridable("TABLE.CHECKBOX_HEADER");
    	checkboxRowLabel = this.i18n.getOverridable("TABLE.CHECKBOX_ROW");
    	endOfDataText = this.i18n.getOverridable("TABLE.END_OF_DATA");
    	scrollTopText = this.i18n.getOverridable("TABLE.SCROLL_TOP");
    	filterTitle = this.i18n.getOverridable("TABLE.FILTER");

    	/**
    	 * Controls if all checkboxes are viewed as selected.
    	 *
    	 * @type {boolean}
    	 */
    	selectAllCheckbox = false;

    	/**
    	 * Controls the indeterminate state of the header checkbox.
    	 *
    	 * @type {boolean}
    	 */
    	selectAllCheckboxSomeSelected = false;

    	/**
    	 * Set to `false` to remove table rows (zebra) stripes.
    	 *
    	 * @type {boolean}
    	 */
    	@Input() striped = true;

    	/**
    	 * Set to `true` to stick the header to the top of the table
    	 */
    	@Input() stickyHeader = false;

    	/**
    	 * Set footer template to customize what is displayed in the tfoot section of the table
    	 */
    	@Input() footerTemplate: TemplateRef<any>;

    	/**
    	 * Used to populate the row selection checkbox label with a useful value if set.
    	 *
    	 * Example:
    	 * ```
    	 * <ibm-table [selectionLabelColumn]="0"></ibm-table>
    	 * <!-- results in aria-label="Select first column value"
    	 * (where "first column value" is the value of the first column in the row -->
    	 * ```
    	 */
    	@Input() selectionLabelColumn: number;

    	/**
    	 * Emits an index of the column that wants to be sorted.
    	 *
    	 */
    	@Output() sort = new EventEmitter<number>();

    	/**
    	 * Emits if all rows are selected.
    	 *
    	 * @param {TableModel} model
    	 */
    	@Output() selectAll = new EventEmitter<Object>();

    	/**
    	 * Emits if all rows are deselected.
    	 *
    	 * @param {TableModel} model
    	 */
    	@Output() deselectAll = new EventEmitter<Object>();

    	/**
    	 * Emits if a single row is selected.
    	 *
    	 * @param {Object} ({model: this.model, selectedRowIndex: index})
    	 */
    	@Output() selectRow = new EventEmitter<Object>();

    	/**
    	 * Emits if a single row is deselected.
    	 *
    	 * @param {Object} ({model: this.model, deselectedRowIndex: index})
    	 */
    	@Output() deselectRow = new EventEmitter<Object>();

    	/**
    	 * Emits when table requires more data to be loaded.
    	 *
    	 * @param {TableModel} model
    	 */
    	@Output() scrollLoad = new EventEmitter<TableModel>();

    	get noData() {
    		return !this.model.data ||
    			this.model.data.length === 0 ||
    			this.model.data.length === 1 && this.model.data[0].length === 0;
    	}

    	columnIndex = 0;

    	public isColumnDragging = false;
    	public columnDraggedHoverIndex = -1;
    	public columnDraggedPosition = "";

    	protected _model: TableModel;

    	protected _expandButtonAriaLabel  = this.i18n.getOverridable("TABLE.EXPAND_BUTTON");
    	protected _sortDescendingLabel = this.i18n.getOverridable("TABLE.SORT_DESCENDING");
    	protected _sortAscendingLabel = this.i18n.getOverridable("TABLE.SORT_ASCENDING");

    	protected columnResizeWidth: number;
    	protected columnResizeMouseX: number;
    	protected mouseMoveSubscription: Subscription;
    	protected mouseUpSubscription: Subscription;

    	/**
    	 * Creates an instance of Table.
    	 *
    	 * @param {ApplicationRef} applicationRef
    	 */
    	constructor(protected elementRef: ElementRef, protected applicationRef: ApplicationRef, protected i18n: I18n) {}

    	ngAfterViewInit() {
    		if (this.isDataGrid) {
    			this.handleTabIndex();
    		}
    	}

    	columnResizeStart(event, column) {
    		this.columnResizeWidth = parseInt(column.style.width, 10);
    		this.columnResizeMouseX = event.clientX;
    		event.preventDefault();

    		this.mouseMoveSubscription = fromEvent(document.body, "mousemove").subscribe(event => {
    			this.columnResizeProgress(event, column);
    		});
    		this.mouseUpSubscription = fromEvent(document.body, "mouseup").subscribe(event => {
    			this.columnResizeEnd(event, column);
    		});
    	}

    	columnResizeProgress(event, column) {
    		const move = event.clientX - this.columnResizeMouseX;
    		column.style.width = `${this.columnResizeWidth + move}px`;
    	}

    	columnResizeEnd(event, column) {
    		this.mouseMoveSubscription.unsubscribe();
    		this.mouseUpSubscription.unsubscribe();
    	}

    	onRowSelect(index: number) {
    		if (!this.showSelectionColumn && this.enableSingleSelect) {
    			this.model.rowsSelected.forEach((element, index) => {
    				this.model.selectRow(index, false);
    			});
    			this.model.selectRow(index, !this.model.rowsSelected[index]);
    			this.onRowCheckboxChange(index);
    		}
    	}

    	updateSelectAllCheckbox() {
    		const selectedRowsCount = this.model.selectedRowsCount();

    		if (selectedRowsCount <= 0) {
    			// reset select all checkbox if nothing selected
    			this.selectAllCheckbox = false;
    			this.selectAllCheckboxSomeSelected = false;
    		} else if (selectedRowsCount < this.model.data.length) {
    			this.selectAllCheckboxSomeSelected = true;
    		}
    	}

    	/**
    	 * Triggered whenever the header checkbox is clicked.
    	 * Updates all the checkboxes in the table view.
    	 * Emits the `selectAll` or `deselectAll` event.
    	 *
    	 */
    	onSelectAllCheckboxChange() {
    		this.applicationRef.tick(); // give app time to process the click if needed

    		if (this.selectAllCheckboxSomeSelected) {
    			this.selectAllCheckbox = false; // clear all boxes
    			this.deselectAll.emit(this.model);
    		} else if (this.selectAllCheckbox) {
    			this.selectAll.emit(this.model);
    		} else {
    			this.deselectAll.emit(this.model);
    		}

    		this.selectAllCheckboxSomeSelected = false;

    		for (let i = 0; i < this.model.rowsSelected.length; i++) {
    			this.model.rowsSelected[i] = this.selectAllCheckbox;
    		}
    	}

    	/**
    	 * Triggered when a single row is clicked.
    	 * Updates the header checkbox state.
    	 * Emits the `selectRow` or `deselectRow` event.
    	 *
    	 * @param {number} index
    	 * @returns
    	 */
    	onRowCheckboxChange(index: number) {
    		let startValue = this.model.rowsSelected[0];

    		if (this.model.rowsSelected[index]) {
    			this.selectRow.emit({model: this.model, selectedRowIndex: index});
    		} else {
    			this.deselectRow.emit({model: this.model, deselectedRowIndex: index});
    		}

    		for (let i = 1; i < this.model.rowsSelected.length; i++) {
    			let one = this.model.rowsSelected[i];

    			if (!!one !== !!startValue) {  // !! essentially converts to boolean and we want undefined to be false
    				// set indeterminate
    				this.selectAllCheckbox = false;
    				this.selectAllCheckboxSomeSelected = true;
    				return;
    			}
    		}

    		this.selectAllCheckboxSomeSelected = false;
    		this.selectAllCheckbox = startValue;
    	}

    	/**
    	 * Triggered when the user scrolls on the `<tbody>` element.
    	 * Emits the `scrollLoad` event.
    	 *
    	 * @param {any} event
    	 */
    	onScroll(event) {
    		const distanceFromBottom = event.target.scrollHeight - event.target.clientHeight - event.target.scrollTop;

    		if (distanceFromBottom <= this.scrollLoadDistance) {
    			this.scrollLoad.emit(this.model);
    		} else {
    			this.model.isEnd = false;
    		}
    	}

    	columnDragStart(event, columnIndex) {
    		this.isColumnDragging = true;
    		this.columnDraggedHoverIndex = columnIndex;
    		event.dataTransfer.setData("columnIndex", JSON.stringify(columnIndex));
    	}

    	columnDragEnd(event, columnIndex) {
    		this.isColumnDragging = false;
    		this.columnDraggedHoverIndex = -1;
    	}

    	columnDragEnter(event, position, columnIndex) {
    		this.columnDraggedPosition = position;
    		this.columnDraggedHoverIndex = columnIndex;
    	}

    	columnDragLeave(event, position, columnIndex) {
    		this.columnDraggedPosition = "";
    	}

    	columnDragover(event, position, columnIndex) {
    		this.columnDraggedHoverIndex = columnIndex;
    		this.columnDraggedPosition = position;

    		// needed to tell browser to allow dropping
    		event.preventDefault();
    	}

    	columnDrop(event, position, columnIndex) {
    		this.isColumnDragging = false;
    		this.columnDraggedHoverIndex = -1;
    		this.columnDraggedPosition = "";

    		this.model.moveColumn(
    			parseInt(event.dataTransfer.getData("columnIndex"), 10),
    			columnIndex + (position === "right" ? 1 : 0)
    		);
    	}

    	get scrollbarWidth() {
    		return getScrollbarWidth();
    	}

    	firstExpandedDataInRow(row) {
    		const found = row.find(d => d.expandedData);
    		if (found) {
    			return found.expandedData;
    		}
    		return found;
    	}

    	firstExpandedTemplateInRow(row) {
    		const found = row.find(d => d.expandedTemplate);
    		if (found) {
    			return found.expandedTemplate;
    		}
    		return found;
    	}
    	/**
    	 * Triggered when the user scrolls on the `<tbody>` element.
    	 * Emits the `scrollLoad` event.
    	 *
    	 * @param {any} event
    	 */
    	scrollToTop(event) {
    		event.target.parentElement.parentElement.parentElement.parentElement.children[1].scrollTop = 0;
    		this.model.isEnd = false;
    	}

    	handleTabIndex() {
    		setTimeout(() => {
    			const focusElementList = getFocusElementList(this.elementRef.nativeElement, tabbableSelectorIgnoreTabIndex);
    			if (focusElementList.length > 0) {
    				focusElementList.forEach(tabbable => {
    					tabbable.tabIndex = -1;
    				});
    			}
    			Array.from<HTMLElement>(this.elementRef.nativeElement.querySelectorAll("td, th")).forEach(cell => Table.setTabIndex(cell, -1));

    			const rows = this.elementRef.nativeElement.firstElementChild.rows;
    			if (Array.from(rows[0].querySelectorAll("th")).some(th => getFocusElementList(th, tabbableSelectorIgnoreTabIndex).length > 0)) {
    				Table.setTabIndex(rows[0].querySelector("th"), 0);
    			} else {
    				Table.setTabIndex(rows[1].querySelector("td"), 0);
    			}
    		});
    	}

    	setIndex(columnIndex) {
    		if (this.model.hasExpandableRows() && this.showSelectionColumn) {
    			this.columnIndex = columnIndex + 2;
    		} else if (this.model.hasExpandableRows() || this.showSelectionColumn) {
    			this.columnIndex = columnIndex + 1;
    		}
    	}

    	setCheckboxIndex() {
    		if (this.model.hasExpandableRows()) {
    			this.columnIndex = 1;
    		} else {
    			this.columnIndex = 0;
    		}
    	}

    	setExpandIndex(event) {
    		this.columnIndex = 0;
    	}

    	getSelectionLabelValue(row: TableItem[]) {
    		if (!this.selectionLabelColumn) {
    			return { value: this.i18n.get().TABLE.ROW };
    		}
    		return { value: row[this.selectionLabelColumn].data };
    	}

    	getExpandButtonAriaLabel() {
    		return this._expandButtonAriaLabel.subject;
    	}
    	getSortDescendingLabel() {
    		return this._sortDescendingLabel.subject;
    	}
    	getSortAscendingLabel() {
    		return this._sortAscendingLabel.subject;
    	}
    }

Legend
Html element
Component
Html element with directive

# result-matching ""

# No results matching ""