This module allows you to create various tables, both static and editable.
| Name | Balance |
|---|---|
| Alex Inkin | 1 323 525 |
| Roman Sedov | 423 242 |
<table tuiTable>
<thead>
<tr>
<th tuiTh>Name</th>
<th tuiTh>Balance</th>
</tr>
</thead>
<tbody tuiTbody>
@for (item of data; track item) {
<tr>
<td tuiTd>{{ item.name }}</td>
<td tuiTd>{{ item.balance | tuiFormatNumber }}</td>
</tr>
}
</tbody>
</table>
Checkbox | Title | Cell | Status | Items | Progress | Actions |
|---|---|---|---|---|---|---|
Data point 1 The first element | John Cleese silly@walk.uk | Success | Some items displayed here and can overflow | 78ms | ||
Some title Some more text | Eric Idle cool@dude.com | Failure | One Item | 91ms | ||
And now Completely different | Michael Palin its@man.com | Pending | 32ms |
<div [style.margin-block-end.rem]="2">
<tui-radio-list
[itemContent]="content"
[items]="sizes"
[style.flex-direction]="'row'"
[style.width]="'max-content'"
[(ngModel)]="size"
/>
<ng-template
#content
let-value
>
{{ value === 's' ? 'Small' : value === 'm' ? 'Medium' : 'Large' }}
</ng-template>
</div>
<table
tuiTable
[size]="size"
[style.width.rem]="58"
[(ngModel)]="selected"
>
<thead>
<tr>
<th tuiTh>
<div [tuiCell]="size">
<input
tuiCheckbox
tuiCheckboxTable
type="checkbox"
[size]="size === 'l' ? 'm' : 's'"
/>
<span tuiTitle>Checkbox</span>
</div>
</th>
<th tuiTh>Title</th>
<th tuiTh>Cell</th>
<th tuiTh>Status</th>
<th
tuiTh
[style.width.rem]="10"
>
Items
</th>
<th tuiTh>Progress</th>
<th tuiTh>Actions</th>
</tr>
</thead>
<tbody tuiTbody>
@for (item of data; track item) {
<tr>
<td tuiTd>
<div [tuiCell]="size">
<input
tuiCheckbox
type="checkbox"
[size]="size === 'l' ? 'm' : 's'"
[tuiCheckboxRow]="item"
/>
<span tuiTitle>
{{ item.checkbox.title }}
<span tuiSubtitle>{{ item.checkbox.subtitle }}</span>
</span>
</div>
</td>
<td tuiTd>
<div [tuiCell]="size">
<span tuiTitle>
<span tuiStatus>
<tui-icon [icon]="item.title.icon" />
{{ item.title.title }}
@if (item.title.chip && item.title.subtitle) {
<span tuiChip>{{ item.title.chip }}</span>
}
</span>
@if (!item.title.subtitle && item.title.chip) {
<span tuiChip>
{{ item.title.chip }}
</span>
} @else {
<span tuiSubtitle>
{{ item.title.subtitle }}
</span>
}
</span>
</div>
</td>
<td tuiTd>
<div [tuiCell]="size">
<div
[style.background]="item.cell.name | tuiAutoColor"
[tuiAvatar]="item.cell.name | tuiInitials"
></div>
<span tuiTitle>
{{ item.cell.name }}
<span tuiSubtitle>{{ item.cell.email }}</span>
</span>
</div>
</td>
<td tuiTd>
<span [tuiStatus]="item.status.color">{{ item.status.value }}</span>
</td>
<td tuiTd>
<tui-items-with-more>
@for (chip of item.items; track chip) {
<div
*tuiItem
tuiBadge
[style.margin-inline-end.rem]="0.25"
>
{{ chip }}
</div>
}
<ng-template
let-number
tuiMore
>
<button
appearance="action-grayscale"
tuiDropdownAlign="end"
tuiDropdownAuto
tuiLink
type="button"
[style.text-decoration-style]="'dashed'"
[tuiDropdown]="dropdown"
>
+ {{ item.items.length - number - 1 }}
</button>
<ng-template #dropdown>
<div
tuiItemGroup
[style.padding]="'1rem 0.75rem 0.75rem 1rem'"
>
@for (chip of item.items; track chip) {
@if ($index > number) {
<div tuiBadge>{{ chip }}</div>
}
}
</div>
</ng-template>
</ng-template>
</tui-items-with-more>
</td>
<td tuiTd>
<span tuiStatus>
<progress
tuiProgressBar
[style.width.rem]="6"
[value]="item.progress / 100"
></progress>
{{ item.progress }}ms
</span>
</td>
<td tuiTd>
<span tuiStatus>
<button
appearance="action"
iconStart="@tui.pencil"
size="xs"
tuiIconButton
type="button"
>
Edit
</button>
<button
appearance="action"
iconStart="@tui.ellipsis"
size="xs"
tuiIconButton
type="button"
>
More
</button>
</span>
</td>
</tr>
}
</tbody>
</table>
[tuiTh],
[tuiTd] {
border-inline-start: none;
border-inline-end: none;
}
[tuiTable][data-size='s'] [tuiTitle] {
flex-direction: row;
gap: 0.375rem;
}
| Name | Purchase | ||||
|---|---|---|---|---|---|
| 999 999 | |||||
| 998 | |||||
| 149.75 | |||||
| 19 999 | |||||
| 14 997 | |||||
| 74.75 | |||||
<tui-scrollbar
waIntersectionRoot
class="scrollbar"
>
<table
size="l"
tuiTable
class="table"
[columns]="columns"
[direction]="-1"
[sorter]="totalSorter"
>
<thead tuiThead>
<tr tuiThGroup>
<th
*tuiHead="'name'"
rowspan="2"
tuiTh
class="first"
[sorter]="null"
[sticky]="true"
>
Name
</th>
<th
*tuiHead="'price'"
rowspan="2"
tuiTh
class="number second"
[sticky]="true"
>
Price, $
</th>
<th
*tuiHead="'quantity'"
colspan="2"
tuiTh
[sorter]="null"
>
Purchase
</th>
<ng-container *tuiHead="'unit'" />
<th
*tuiHead="'date'"
rowspan="2"
tuiTh
[minWidth]="155"
>
Date
</th>
<th
*tuiHead="'total'"
rowspan="2"
tuiTh
class="number"
[sorter]="totalSorter"
>
Total
</th>
</tr>
<tr tuiThGroup>
<th
*tuiHead="'quantity'"
tuiTh
class="number border"
>
Quantity
</th>
<th
*tuiHead="'unit'"
tuiTh
>
Units
</th>
</tr>
</thead>
@let sortedPythons = pythons | tuiTableSort;
<tbody
heading="Monty Python"
tuiTbody
[data]="sortedPythons"
>
@for (item of sortedPythons; track trackByIndex($index)) {
<tr tuiTr>
<th
*tuiCell="'name'"
tuiTd
[colSpan]="item.price > 1000 ? 2 : 0"
>
<tui-textfield>
<textarea
tuiTextarea
[ngModel]="item.name"
[ngModelOptions]="options"
(ngModelChange)="onValueChange($event, 'name', item, pythons)"
></textarea>
</tui-textfield>
</th>
<ng-container *tuiCell="'price'">
@if (item.price <= 1000) {
<th
tuiTd
class="second"
>
<tui-textfield>
<input
tuiInputNumber
class="number"
[ngModel]="item.price"
[ngModelOptions]="options"
[tuiValidator]="minPrice"
(ngModelChange)="onValueChange($event, 'price', item, pythons)"
/>
</tui-textfield>
</th>
}
</ng-container>
<td
*tuiCell="'quantity'"
tuiTd
>
<tui-textfield>
<input
tuiInputNumber
[ngModel]="item.quantity"
[ngModelOptions]="options"
[tuiNumberFormat]="{precision: 0}"
(ngModelChange)="onValueChange($event, 'quantity', item, pythons)"
/>
</tui-textfield>
</td>
<td
*tuiCell="'unit'"
tuiTd
>
<tui-textfield
tuiChevron
class="select"
>
<input
placeholder="Units"
tuiSelect
[ngModel]="item.unit"
[ngModelOptions]="options"
(ngModelChange)="onValueChange($event, 'unit', item, pythons)"
/>
<tui-data-list-wrapper
*tuiDropdown
[items]="units"
/>
</tui-textfield>
</td>
<td
*tuiCell="'date'"
tuiTd
>
<tui-textfield>
<input
tuiInputDate
[ngModel]="item.date"
[ngModelOptions]="options"
(ngModelChange)="onValueChange($event, 'date', item, pythons)"
/>
<tui-calendar *tuiDropdown />
</tui-textfield>
</td>
<td
*tuiCell="'total'"
tuiTd
class="number text"
>
{{ getTotal(item) | tuiFormatNumber }}
</td>
</tr>
}
</tbody>
@let sortedStarwars = starwars | tuiTableSort;
<tbody
tuiTbody
[data]="sortedStarwars"
[heading]="template"
>
@for (item of sortedStarwars; track trackByIndex($index)) {
<tr tuiTr>
<th
*tuiCell="'name'"
tuiTd
>
<tui-textfield>
<textarea
tuiTextarea
[ngModel]="item.name"
[ngModelOptions]="options"
(ngModelChange)="onValueChange($event, 'name', item, starwars)"
></textarea>
</tui-textfield>
</th>
<th
*tuiCell="'price'"
tuiTd
class="second"
>
<tui-textfield>
<input
tuiInputNumber
[ngModel]="item.price"
[ngModelOptions]="options"
[tuiValidator]="minPrice"
(ngModelChange)="onValueChange($event, 'price', item, starwars)"
/>
</tui-textfield>
</th>
<td
*tuiCell="'quantity'"
tuiTd
>
<tui-textfield>
<input
tuiInputNumber
[ngModel]="item.quantity"
[ngModelOptions]="options"
[tuiNumberFormat]="{precision: 0}"
(ngModelChange)="onValueChange($event, 'quantity', item, starwars)"
/>
</tui-textfield>
</td>
<td
*tuiCell="'unit'"
tuiTd
>
<tui-textfield
tuiChevron
class="select"
>
<input
placeholder="Units"
tuiSelect
[ngModel]="item.unit"
[ngModelOptions]="options"
(ngModelChange)="onValueChange($event, 'unit', item, starwars)"
/>
<tui-data-list-wrapper
*tuiDropdown
[items]="units"
/>
</tui-textfield>
</td>
<td
*tuiCell="'date'"
tuiTd
>
<tui-textfield>
<input
tuiInputDate
[ngModel]="item.date"
[ngModelOptions]="options"
(ngModelChange)="onValueChange($event, 'date', item, starwars)"
/>
<tui-calendar *tuiDropdown />
</tui-textfield>
</td>
<td
*tuiCell="'total'"
tuiTd
class="number text"
>
{{ getTotal(item) | tuiFormatNumber }}
</td>
</tr>
}
</tbody>
</table>
</tui-scrollbar>
<ng-template #template>
<tui-icon
icon="@tui.star"
class="tui-space_right-3"
/>
Star Wars
</ng-template>
.table {
table-layout: fixed;
}
.number {
text-align: end;
flex-direction: row-reverse;
}
.first {
min-inline-size: 11rem;
max-inline-size: 11rem;
}
.second {
inset-inline-start: 11rem;
}
.text {
vertical-align: top;
padding-block-start: 1rem;
}
// Due to rowSpan this item appears to be the first child
// but it shouldn't have the left border in reality
.border {
border-inline-start: none;
}
.select {
inline-size: 6.25rem;
}
.scrollbar {
max-block-size: 18.75rem;
}
SortBy directive to work with column titles instead of manual sorters.
<p
tuiTextfieldSize="m"
class="filters"
>
<tui-textfield class="input">
<label tuiLabel>Find on page</label>
<input
tuiInput
[(ngModel)]="search"
/>
</tui-textfield>
<tui-textfield>
<label tuiLabel>Minimum age</label>
<input
tuiInputNumber
[formControl]="minAge"
[tuiNumberFormat]="{precision: 0}"
/>
</tui-textfield>
</p>
<p class="filters">
<label tuiLabel>
<input
tuiCheckbox
type="checkbox"
[(ngModel)]="dob"
/>
Enable sorting by DOB
</label>
<button
size="m"
tuiButton
tuiChevron
tuiDropdownAuto
type="button"
[tuiDropdown]="dropdown"
>
Columns
</button>
<ng-template #dropdown>
<tui-reorder
class="columns"
[enabled]="enabled"
[(items)]="initial"
(enabledChange)="onEnabled($event)"
/>
</ng-template>
</p>
<tui-loader
[loading]="!!(loading$ | async)"
[overlay]="true"
>
<p>
<b>Sort key:</b>
{{ sortKey$ | async }},
<b>direction:</b>
{{ direction$ | async }}
</p>
@if (data$ | async; as data) {
<table
tuiTable
class="table"
[columns]="columns"
[direction]="(direction$ | async) ?? -1"
[tuiSortBy]="sortKey$ | async"
(tuiSortChange)="change($event)"
>
<thead>
<tr tuiThGroup>
<th
*tuiHead="'name'"
tuiSortable
tuiTh
[requiredSort]="true"
>
Name
</th>
<th
*tuiHead="'dob'"
tuiTh
[tuiSortable]="dob"
>
Date of Birth
</th>
<th
*tuiHead="'age'"
tuiSortable
tuiTh
[requiredSort]="true"
>
Age
</th>
</tr>
</thead>
@let sortedData = data | tuiTableSort;
<tbody
tuiTbody
[data]="sortedData"
>
@for (item of sortedData; track item) {
<tr tuiTr>
<td
*tuiCell="'name'"
tuiTd
[class.match]="isMatch(item.name)"
>
{{ item.name }}
</td>
<td
*tuiCell="'dob'"
tuiTd
[class.match]="isMatch(item.dob)"
>
{{ item.dob }}
</td>
<td
*tuiCell="'age'"
tuiTd
[class.match]="isMatch(item.age)"
>
{{ item.age }}
</td>
</tr>
}
</tbody>
<tfoot>
<tr>
<td [colSpan]="columns.length">
<tui-table-pagination
class="tui-space_top-2"
[page]="(page$ | async) || 0"
[total]="(total$ | async) || 0"
(paginationChange)="onPagination($event)"
/>
</td>
</tr>
</tfoot>
</table>
}
</tui-loader>
.table {
inline-size: 100%;
}
.filters {
display: flex;
gap: 1rem;
white-space: nowrap;
align-items: center;
}
.input {
flex: 1;
}
.columns {
inline-size: 10.625rem;
}
.match {
background: var(--tui-service-selection-background);
}
ScrollingModule from @angular/cdk/scrolling to implement virtual scroll and tuiSorter pipe for easy sorting by key.
<tui-scrollbar>
<cdk-virtual-scroll-viewport
#viewport
appendOnly
tuiScrollable
class="viewport tui-zero-scrollbar"
[itemSize]="45"
[maxBufferPx]="500"
[minBufferPx]="400"
>
<table tuiTable>
<thead>
<tr [style.inset-block-start.px]="-(viewport.getOffsetToRenderedContentStart() || 0)">
<th
tuiTh
[sorter]="'name' | tuiSorter"
[sticky]="true"
>
Name
</th>
<th
tuiTh
[sorter]="'dob' | tuiSorter"
[sticky]="true"
>
Date of Birth
</th>
<th
tuiTh
[sorter]="ageSorter"
[sticky]="true"
>
Age
</th>
</tr>
</thead>
<tbody tuiTbody>
<tr *cdkVirtualFor="let item of data | tuiTableSort">
<td tuiTd>{{ item.name }}</td>
<td tuiTd>{{ item.dob }}</td>
<td tuiTd>{{ getAge(item) }}</td>
</tr>
</tbody>
</table>
</cdk-virtual-scroll-viewport>
</tui-scrollbar>
table {
inline-size: 100%;
th {
inset-block-start: inherit;
}
tr {
block-size: 2.8125rem;
}
td {
&:first-child {
inline-size: 10rem;
font-weight: bold;
}
&:last-child {
inline-size: 3rem;
}
}
}
.viewport {
block-size: 15.625rem;
}
| 1 | name |
<button
size="s"
tuiButton
type="button"
(click)="addColumn()"
>
Add column
</button>
<button
size="s"
tuiButton
type="button"
class="tui-space_left-2"
(click)="addRows()"
>
Add row
</button>
<table
tuiTable
class="table tui-space_top-3"
[columns]="columns"
>
<thead>
<tr tuiThGroup>
@for (col of columns; track col) {
<th
*tuiHead="col"
tuiTh
>
{{ col }}
</th>
}
</tr>
</thead>
@let sortedData = data | tuiTableSort;
<tbody
tuiTbody
[data]="sortedData"
>
@for (item of sortedData; track item) {
<tr tuiTr>
@for (col of columns; track col) {
<td
*tuiCell="col"
tuiTd
>
{{ item[col] }}
</td>
}
</tr>
}
</tbody>
</table>
.table {
inline-size: 100%;
}
caption tag to place pagination at the bottom of the table. | Name | Balance |
|---|---|
| Alex Inkin | 1 323 525 |
| Roman Sedov | 423 242 |
<table
tuiTable
[style.width.rem]="36"
>
<caption tuiCaption>
<span>999 rows</span>
<button
appearance="flat"
tuiButton
tuiButtonSelect
type="button"
[(ngModel)]="size"
>
{{ index * size + 1 }}-{{ (index + 1) * size }} rows
<tui-data-list-wrapper
*tuiDropdown
[itemContent]="content"
[items]="items"
/>
</button>
<tui-pagination
[length]="length"
[style.float]="'right'"
[(index)]="index"
/>
</caption>
<thead>
<tr>
<th tuiTh>Name</th>
<th tuiTh>Balance</th>
</tr>
</thead>
<tbody tuiTbody>
@for (item of data; track item) {
<tr>
<td tuiTd>{{ item.name }}</td>
<td tuiTd>{{ item.balance | tuiFormatNumber }}</td>
</tr>
}
</tbody>
</table>
| Name | Items | Balance |
|---|---|---|
| Alex Inkin | ||
| Roman Sedov |
<table tuiTable>
<thead>
<tr>
<th
tuiTh
[maxWidth]="300"
[minWidth]="150"
[resizable]="true"
>
Name
</th>
<th
tuiTh
[maxWidth]="300"
[minWidth]="100"
[resizable]="true"
>
Items
</th>
<th
tuiTh
[maxWidth]="200"
[minWidth]="100"
[resizable]="true"
>
Balance
</th>
</tr>
</thead>
<tbody tuiTbody>
@for (item of data; track item) {
<tr>
<td tuiTd>
{{ item.name }}
</td>
<td tuiTd>
<tui-textfield multi>
<input
tuiInputChip
[(ngModel)]="item.items"
/>
</tui-textfield>
</td>
<td tuiTd>
<tui-textfield>
<input
placeholder="Value"
prefix="$"
tuiInputNumber
[style.height.%]="100"
[(ngModel)]="item.balance"
/>
</tui-textfield>
</td>
</tr>
}
</tbody>
</table>
| # | Name | Date of Birth | |
|---|---|---|---|
| 1 | John Matrix | 30.07.1947 | |
| 1.1 | Jenny Matrix | 15.07.1975 | |
| 2 | John Rambo | 04.07.1946 | |
| 3 | John McClane | 19.03.1955 | |
| 3.1 | Lucy McClane | 10.08.1982 | |
| 3.2 | Jack McClane | 05.04.1985 |
<table tuiTable>
<thead>
<tr>
<th tuiTh></th>
<th tuiTh>#</th>
<th tuiTh>Name</th>
<th tuiTh>Date of Birth</th>
</tr>
</thead>
<tbody tuiTbody>
@for (item of data; track $index; let i = $index) {
<tr>
<td tuiTd>
<button
appearance="flat-grayscale"
size="xs"
tuiIconButton
type="button"
[attr.title]="item.children.length ? null : 'No children'"
[disabled]="!item.children.length"
[style.border-radius.%]="100"
[tuiChevron]="state[i] ?? false"
(click)="state[i] = !state[i]"
>
Toggle
</button>
</td>
<td tuiTd>{{ i + 1 }}</td>
<td tuiTd>{{ item.name }}</td>
<td tuiTd>{{ item.dob }}</td>
</tr>
@if (item.children.length) {
<tui-table-expand [expanded]="state[i] ?? false">
@for (child of item.children; track $index; let j = $index) {
<tr>
<td tuiTd></td>
<td tuiTd>{{ i + 1 }}.{{ j + 1 }}</td>
<td tuiTd>{{ child.name }}</td>
<td tuiTd>{{ child.dob }}</td>
</tr>
}
</tui-table-expand>
}
}
</tbody>
</table>
@import '@taiga-ui/styles/utils.less';
tui-table-expand {
td {
background: var(--tui-background-base-alt);
}
tr:hover td {
background: var(--tui-background-neutral-1-hover);
}
}
tr:hover td {
background: var(--tui-background-base-alt);
}
[tuiTh] {
border-block-start: none;
}
[tuiTh],
[tuiTd] {
min-inline-size: 3rem;
border-inline: none;
white-space: nowrap;
}
| First name | Last name | Role | Balance | |
|---|---|---|---|---|
| 3 Developers (basic usage) | dev | 5 970 009 | ||
Alex | Inkin | dev | 1 323 525 | |
Roman | Sedov | dev | 423 242 | |
Andrei | Serebrennikov | dev | 4 223 242 | |
| 2 Designers (manual handling) | design | 4 646 484 | ||
| Joe | Wilson | design | 423 242 | |
| Julia | Johnson | design | 4 223 242 | |
| Custom content (click on chevron) | all | 10 616 493 | ||
Alex Inkin Roman Sedov Andrei Serebrennikov Joe Wilson Julia Johnson | ||||
<table
size="l"
tuiTable
class="table"
[columns]="columns"
>
<thead>
<tr tuiThGroup>
<th
*tuiHead="'action'"
tuiTh
[sorter]="null"
></th>
<th
*tuiHead="'firstName'"
tuiTh
[sorter]="null"
>
First name
</th>
<th
*tuiHead="'lastName'"
tuiTh
class="last-name-col"
[sorter]="null"
>
Last name
</th>
<th
*tuiHead="'role'"
tuiTh
[sorter]="null"
>
Role
</th>
<th
*tuiHead="'balance'"
tuiTh
[sorter]="null"
>
Balance
</th>
</tr>
</thead>
<tbody tuiTbody>
<tr
tuiTr
class="expand-heading-row"
(click)="expand.toggle()"
>
<td
*tuiCell="'action'"
tuiTd
>
<button
appearance="icon"
size="xs"
tuiIconButton
type="button"
[tuiChevron]="expand.expanded()"
>
Toggle
</button>
</td>
<td
*tuiCell="'firstName'"
tuiTd
[colSpan]="2"
>
{{ basicData.length }}
Developers (basic usage)
</td>
<ng-container *tuiCell="'lastName'" />
<td
*tuiCell="'role'"
tuiTd
>
dev
</td>
<td
*tuiCell="'balance'"
tuiTd
>
{{ basicData | tuiMapper: getSumBalance | tuiFormatNumber }}
</td>
</tr>
<tui-table-expand
#expand
[expanded]="true"
>
@for (item of basicData; track item) {
<tr tuiTr>
<td
*tuiCell="'action'"
tuiTd
></td>
<td
*tuiCell="'firstName'"
tuiTd
>
<div>
<span
size="s"
tuiChip
[appearance]="item.role === 'dev' ? 'primary' : 'secondary'"
>
{{ item.firstName }}
</span>
</div>
</td>
<td
*tuiCell="'lastName'"
tuiTd
>
{{ item.lastName }}
</td>
<td
*tuiCell="'role'"
tuiTd
>
{{ item.role }}
</td>
<td
*tuiCell="'balance'"
tuiTd
>
{{ item.balance | tuiFormatNumber }}
</td>
</tr>
}
</tui-table-expand>
</tbody>
<tbody tuiTbody>
<tr
tuiTr
class="expand-heading-row"
(click)="manualToggle()"
>
<td
*tuiCell="'action'"
tuiTd
>
<button
appearance="icon"
size="xs"
tuiIconButton
type="button"
[tuiChevron]="manualOpen"
>
Toggle
</button>
</td>
<td
*tuiCell="'firstName'"
tuiTd
[colSpan]="2"
>
{{ manualOpenData.length }}
Designers (manual handling)
</td>
<ng-container *tuiCell="'lastName'" />
<td
*tuiCell="'role'"
tuiTd
>
design
</td>
<td
*tuiCell="'balance'"
tuiTd
>
{{ manualOpenData | tuiMapper: getSumBalance | tuiFormatNumber }}
</td>
</tr>
<tui-table-expand [expanded]="manualOpen">
@for (item of manualOpenData; track item) {
<tr tuiTr>
<td
*tuiCell="'action'"
tuiTd
></td>
<td
*tuiCell="'firstName'"
tuiTd
>
{{ item.firstName }}
</td>
<td
*tuiCell="'lastName'"
tuiTd
>
{{ item.lastName }}
</td>
<td
*tuiCell="'role'"
tuiTd
>
{{ item.role }}
</td>
<td
*tuiCell="'balance'"
tuiTd
>
{{ item.balance | tuiFormatNumber }}
</td>
</tr>
}
</tui-table-expand>
</tbody>
<tbody tuiTbody>
<tr tuiTr>
<td
*tuiCell="'action'"
tuiTd
>
<button
appearance="flat-grayscale"
size="xs"
tuiIconButton
type="button"
[style.border-radius.%]="100"
[tuiChevron]="customOpen"
(click)="customToggle()"
>
Toggle
</button>
</td>
<td
*tuiCell="'firstName'"
tuiTd
[colSpan]="2"
>
Custom content (click on chevron)
</td>
<ng-container *tuiCell="'lastName'" />
<td
*tuiCell="'role'"
tuiTd
>
all
</td>
<td
*tuiCell="'balance'"
tuiTd
>
{{ customContentData | tuiMapper: getSumBalance | tuiFormatNumber }}
</td>
</tr>
<tr>
<td [colSpan]="columns.length">
<tui-expand [expanded]="customOpen">
<div class="chips">
@for (item of customContentData; track item) {
<span
size="s"
tuiChip
[appearance]="item.role === 'dev' ? 'primary' : 'secondary'"
[tuiHint]="`Balance: ${item.balance}. Role: ${item.role}`"
>
{{ item.firstName }}
{{ item.lastName }}
</span>
}
</div>
</tui-expand>
</td>
</tr>
</tbody>
</table>
th {
white-space: nowrap;
}
.table {
inline-size: 36rem;
}
.chips {
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
padding: 0.5rem;
}
.last-name-col {
inline-size: 10rem;
}
.expand-heading-row {
cursor: pointer;
td {
background: var(--tui-background-neutral-1);
}
&:hover td {
background: var(--tui-background-neutral-1-hover);
}
}
| Growing height | Static height |
|---|---|
<table
tuiTable
[style.width.%]="100"
[tuiTextfieldCleaner]="false"
>
<thead>
<tr>
<th
tuiTh
[style.width.%]="50"
>
Growing height
</th>
<th
tuiTh
[style.width.%]="50"
>
Static height
</th>
</tr>
</thead>
<tbody tuiTbody>
<tr>
<td tuiTd>
<tui-textfield>
<textarea
placeholder="Textarea"
tuiTextarea
[(ngModel)]="textarea"
></textarea>
</tui-textfield>
</td>
<td tuiTd>
<tui-textfield>
<input
placeholder="Default table style"
tuiInput
[(ngModel)]="input"
/>
</tui-textfield>
</td>
</tr>
<tr>
<td tuiTd>
<tui-textfield multi>
<input
placeholder="InputChip"
tuiInputChip
[(ngModel)]="chip"
/>
</tui-textfield>
</td>
<td tuiTd>
<tui-textfield tuiChevron>
@if (isMobile) {
<select
aria-label="Select"
placeholder="Select"
tuiSelect
[items]="items"
[(ngModel)]="select"
></select>
}
@if (!isMobile) {
<input
placeholder="Select"
tuiSelect
[(ngModel)]="select"
/>
}
@if (!isMobile) {
<tui-data-list-wrapper
*tuiDropdown
[items]="items"
/>
}
</tui-textfield>
</td>
</tr>
<tr>
<td tuiTd>
<tui-textfield
multi
tuiChevron
>
<select
aria-label="MultiSelect"
placeholder="MultiSelect"
tuiMultiSelect
[items]="items"
[(ngModel)]="multiselect"
></select>
<tui-input-chip *tuiItem />
</tui-textfield>
</td>
<td
tuiTd
[style.z-index]="2"
>
<tui-textfield>
<input
placeholder="InputSlider"
tuiInputSlider
[max]="1000"
[min]="0"
[(ngModel)]="slider"
/>
<input
tuiSlider
type="range"
/>
</tui-textfield>
</td>
</tr>
<tr>
<td tuiTd>
<tui-textfield multi>
<input
placeholder="InputDateMulti"
tuiInputDateMulti
[(ngModel)]="date"
/>
<tui-calendar *tuiDropdown />
</tui-textfield>
</td>
<td
tuiTd
[style.z-index]="1"
>
<tui-input-range
[max]="1000"
[min]="0"
[(ngModel)]="range"
/>
</td>
</tr>
<tr>
<td
colspan="2"
tuiTd
>
<tui-input-card-group [(ngModel)]="card">
@if (!card) {
InputCardGroup
}
</tui-input-card-group>
</td>
</tr>
<tr>
<td tuiTd>
<tui-textfield [style.margin.rem]="0.5">
<input
placeholder="Default textfield style"
tuiInput
tuiTextfieldAppearance="textfield"
[(ngModel)]="input"
/>
</tui-textfield>
</td>
<td tuiTd>
<tui-textfield
multi
tuiChevron
[style.margin.rem]="0.5"
>
<select
aria-label="MultiSelect"
placeholder="MultiSelect"
tuiMultiSelect
tuiTextfieldAppearance="textfield"
[items]="items"
[(ngModel)]="multiselect"
></select>
<tui-input-chip *tuiItem />
</tui-textfield>
</td>
</tr>
</tbody>
</table>