Taiga UI 5.0 is out!

Table ADDON-TABLE

Examples API GitHub

On this page

See also

TablePagination, TableFilters

This module allows you to create various tables, both static and editable.

Basic

Most minimalistic usage example.
NameBalance
Alex Inkin1 323 525
Roman Sedov423 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>

    

Custom

Using various components within table cells.
Checkbox
TitleCellStatus Items ProgressActions
Data point 1 The first element
This is title Chip More information ・ Data
John Cleese silly@walk.uk
Success
Some
items
displayed
here
and
can
overflow
78ms
Some title Some more text
More info Chips can be here
Eric Idle cool@dude.com
Failure
One
Item
91ms
And now Completely different
Wow
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;
}

    

Editable

Using editable textfield controls inside a table.
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,&nbsp;$
                </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;
}

    

Sorting

Using SortBy directive to work with column titles instead of manual sorters.

Sort key: name, direction: -1

Date of Birth
Pages 1 Lines per page of 1
    
      
    
    
      <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);
}

    

Virtual scroll

Using 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;
}

    

Columns

Using structural directives for dynamic columns.
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%;
}

    

Footer

Using caption tag to place pagination at the bottom of the table.
999 rows
NameBalance
Alex Inkin1 323 525
Roman Sedov423 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>

    

Resizing

Making column headers draggable at the edge and setting width limits.
Name
Items
Balance
Alex Inkin
WalletPhone
Roman Sedov
Wallet
    
      
    
    
      <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>

    

Expandable rows

Animated expandable sections of the table.
#NameDate of Birth
1John Matrix30.07.1947
1.1Jenny Matrix15.07.1975
2John Rambo04.07.1946
3John McClane19.03.1955
3.1Lucy McClane10.08.1982
3.2Jack McClane05.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;
}

    

Toggle rows

More complex examples of expandable sections.
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);
    }
}

    

Controls

All unique textfield controls in their table representation.
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>