Table displays data in tabular format.
import { TableModule } from 'primeng/table';
DataTable requires a collection to display along with column components for the representation of the data.
Code | Name | Category | Quantity |
---|---|---|---|
f230fh0g3 | Bamboo Watch | Accessories | 24 |
nvklal433 | Black Watch | Accessories | 61 |
zz21cz3c1 | Blue Band | Fitness | 2 |
244wgerg2 | Blue T-Shirt | Clothing | 25 |
h456wer53 | Bracelet | Accessories | 73 |
<p-table [value]="products" [tableStyle]="{ 'min-width': '50rem' }">
<ng-template pTemplate="header">
<tr>
<th>Code</th>
<th>Name</th>
<th>Category</th>
<th>Quantity</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-product>
<tr>
<td>{{ product.code }}</td>
<td>{{ product.name }}</td>
<td>{{ product.category }}</td>
<td>{{ product.quantity }}</td>
</tr>
</ng-template>
</p-table>
Columns can be defined dynamically using the *ngFor directive.
Code | Name | Category | Quantity |
---|---|---|---|
f230fh0g3 | Bamboo Watch | Accessories | 24 |
nvklal433 | Black Watch | Accessories | 61 |
zz21cz3c1 | Blue Band | Fitness | 2 |
244wgerg2 | Blue T-Shirt | Clothing | 25 |
h456wer53 | Bracelet | Accessories | 73 |
<p-table [columns]="cols" [value]="products" [tableStyle]="{ 'min-width': '50rem' }">
<ng-template pTemplate="header" let-columns>
<tr>
<th *ngFor="let col of columns">
{{ col.header }}
</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-rowData let-columns="columns">
<tr>
<td *ngFor="let col of columns">
{{ rowData[col.field] }}
</td>
</tr>
</ng-template>
</p-table>
Custom content at header, body and footer sections are supported via templating.
Name | Image | Price | Category | Reviews | Status |
---|---|---|---|---|---|
Bamboo Watch | ![]() | $65.00 | Accessories | ||
Black Watch | ![]() | $72.00 | Accessories | ||
Blue Band | ![]() | $79.00 | Fitness | ||
Blue T-Shirt | ![]() | $29.00 | Clothing | ||
Bracelet | ![]() | $15.00 | Accessories |
<p-table [value]="products" [tableStyle]="{'min-width': '60rem'}">
<ng-template pTemplate="caption">
<div class="flex align-items-center justify-content-between">
Products
<p-button icon="pi pi-refresh"></p-button>
</div>
</ng-template>
<ng-template pTemplate="header">
<tr>
<th>Name</th>
<th>Image</th>
<th>Price</th>
<th>Category</th>
<th>Reviews</th>
<th>Status</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-product>
<tr>
<td>{{product.name}}</td>
<td><img [src]="'https://primefaces.org/cdn/primeng/images/demo/product/' + product.image" [alt]="product.name" width="100" class="shadow-4" /></td>
<td>{{product.price | currency:'USD'}}</td>
<td>{{product.category}}</td>
<td><p-rating [ngModel]="product.rating" [readonly]="true" [cancel]="false"></p-rating></td>
<td><p-tag [value]="product.inventoryStatus" [severity]="getSeverity(product.inventoryStatus)"></p-tag></td>
</tr>
</ng-template>
<ng-template pTemplate="summary">
<div class="flex align-items-center justify-content-between">
In total there are {{products ? products.length : 0 }} products.
</div>
</ng-template>
</p-table>
In addition to a regular table, alternatives with alternative sizes are available.
Code | Name | Category | Quantity |
---|---|---|---|
f230fh0g3 | Bamboo Watch | Accessories | 24 |
nvklal433 | Black Watch | Accessories | 61 |
zz21cz3c1 | Blue Band | Fitness | 2 |
244wgerg2 | Blue T-Shirt | Clothing | 25 |
h456wer53 | Bracelet | Accessories | 73 |
<div class="flex justify-content-center mb-3">
<p-selectButton [options]="sizes" [(ngModel)]="selectedSize" [multiple]="false" optionLabel="name" optionValue="class"></p-selectButton>
</div>
<p-table [value]="products" [tableStyle]="{ 'min-width': '50rem' }" [styleClass]="selectedSize.class">
<ng-template pTemplate="header">
<tr>
<th>Code</th>
<th>Name</th>
<th>Category</th>
<th>Quantity</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-product>
<tr>
<td>{{ product.code }}</td>
<td>{{ product.name }}</td>
<td>{{ product.category }}</td>
<td>{{ product.quantity }}</td>
</tr>
</ng-template>
</p-table>
Adding p-datatable-gridlines class displays grid lines.
Code | Name | Category | Quantity |
---|---|---|---|
f230fh0g3 | Bamboo Watch | Accessories | 24 |
nvklal433 | Black Watch | Accessories | 61 |
zz21cz3c1 | Blue Band | Fitness | 2 |
244wgerg2 | Blue T-Shirt | Clothing | 25 |
h456wer53 | Bracelet | Accessories | 73 |
<p-table [value]="products" styleClass="p-datatable-gridlines" [tableStyle]="{ 'min-width': '50rem' }">
<ng-template pTemplate="caption"> Header </ng-template>
<ng-template pTemplate="header">
<tr>
<th>Code</th>
<th>Name</th>
<th>Category</th>
<th>Quantity</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-product>
<tr>
<td>{{ product.code }}</td>
<td>{{ product.name }}</td>
<td>{{ product.category }}</td>
<td>{{ product.quantity }}</td>
</tr>
</ng-template>
<ng-template pTemplate="summary"> Footer </ng-template>
</p-table>
Adding p-datatable-striped class displays striped rows.
Code | Name | Category | Quantity |
---|---|---|---|
f230fh0g3 | Bamboo Watch | Accessories | 24 |
nvklal433 | Black Watch | Accessories | 61 |
zz21cz3c1 | Blue Band | Fitness | 2 |
244wgerg2 | Blue T-Shirt | Clothing | 25 |
h456wer53 | Bracelet | Accessories | 73 |
<p-table [value]="products" styleClass="p-datatable-striped" [tableStyle]="{'min-width': '50rem'}">
<ng-template pTemplate="header">
<tr>
<th>Code</th>
<th>Name</th>
<th>Category</th>
<th>Quantity</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-product>
<tr>
<td>{{product.code}}</td>
<td>{{product.name}}</td>
<td>{{product.category}}</td>
<td>{{product.quantity}}</td>
</tr>
</ng-template>
</p-table>
Certain rows or cells can easily be styled based on conditions.
Code | Name | Category | Quantity |
---|---|---|---|
f230fh0g3 | Bamboo Watch | Accessories | 24 |
nvklal433 | Black Watch | Accessories | 61 |
zz21cz3c1 | Blue Band | Fitness | 2 |
244wgerg2 | Blue T-Shirt | Clothing | 25 |
h456wer53 | Bracelet | Accessories | 73 |
<p-table [value]="products" [tableStyle]="{'min-width': '50rem'}">
<ng-template pTemplate="header">
<tr>
<th>Code</th>
<th>Name</th>
<th>Category</th>
<th>Quantity</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-product>
<tr [ngClass]="{'row-accessories': product.category === 'Accessories'}">
<td>{{product.code}}</td>
<td>{{product.name}}</td>
<td>{{product.category}}</td>
<td>
<div [ngClass]="{'outofstock': product.quantity === 0, 'lowstock': (product.quantity > 0 && product.quantity < 10),'instock': product.quantity > 10}">
{{product.quantity}}
</div>
</td>
</tr>
</ng-template>
</p-table>
When there is not enough space for the table to fit all the content efficiently, table displays a horizontal scrollbar. It is suggested to give a min-width to the table to avoid design issues due wrapping of cell contents.
Following table displays a horizontal scrollbar when viewport is smaller than 50rem.
Name | Price | Category | Quantity | Status | Reviews |
---|---|---|---|---|---|
Bamboo Watch | $65.00 | Accessories | 24 | ||
Black Watch | $72.00 | Accessories | 61 | ||
Blue Band | $79.00 | Fitness | 2 | ||
Blue T-Shirt | $29.00 | Clothing | 25 | ||
Bracelet | $15.00 | Accessories | 73 |
<p-table [value]="products" [tableStyle]="{'min-width': '50rem'}">
<ng-template pTemplate="header" let-columns>
<tr>
<th>Name </th>
<th>Price</th>
<th>Category</th>
<th>Quantity</th>
<th>Status</th>
<th>Reviews</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-product let-columns="columns">
<tr>
<td>{{product.name}}</td>
<td>{{product.price | currency:'USD'}}</td>
<td>{{product.category}}</td>
<td>{{product.quantity}}</td>
<td><p-tag [value]="product.inventoryStatus" [severity]="getSeverity(product.inventoryStatus)"></p-tag></td>
<td><p-rating [ngModel]="product.rating" [readonly]="true" [cancel]="false"></p-rating></td>
</tr>
</ng-template>
</p-table>
In stack layout, columns are displayed as stacked after a certain breakpoint. Default is '960px' as max-width. This feature is enabled by setting responsiveLayout to stack and adding an element with p-column-title style class to the body cells.
Name | Price | Category | Quantity | Status | Reviews |
---|---|---|---|---|---|
NameBamboo Watch | Price$65.00 | CategoryAccessories | Quantity24 | Reviews | |
NameBlack Watch | Price$72.00 | CategoryAccessories | Quantity61 | Reviews | |
NameBlue Band | Price$79.00 | CategoryFitness | Quantity2 | Reviews | |
NameBlue T-Shirt | Price$29.00 | CategoryClothing | Quantity25 | Reviews | |
NameBracelet | Price$15.00 | CategoryAccessories | Quantity73 | Reviews |
<p-table [value]="products" responsiveLayout="stack" [breakpoint]="'960px'" [tableStyle]="{'min-width': '50rem'}">
<ng-template pTemplate="header" let-columns>
<tr>
<th>Name</th>
<th>Price</th>
<th>Category</th>
<th>Quantity</th>
<th>Status</th>
<th>Reviews</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-product let-columns="columns">
<tr>
<td><span class="p-column-title">Name</span>{{product.name}}</td>
<td><span class="p-column-title">Price</span>{{product.price | currency:'USD'}}</td>
<td><span class="p-column-title">Category</span>{{product.category}}</td>
<td><span class="p-column-title">Quantity</span>{{product.quantity}}</td>
<td><p-tag [value]="product.inventoryStatus" [severity]="getSeverity(product.inventoryStatus)"></p-tag></td>
<td><span class="p-column-title">Reviews</span><p-rating [ngModel]="product.rating" [readonly]="true" [cancel]="false"></p-rating></td>
</tr>
</ng-template>
</p-table>
Pagination is enabled by setting paginator property to true and defining a rows property to specify the number of rows per page. For server side pagination, see the lazy loading example.
Name | Country | Company | Representative |
---|---|---|---|
James Butt | Algeria | Benton, John B Jr | Ioni Bowcher |
Josephine Darakjy | Egypt | Chanay, Jeffrey A Esq | Amy Elsner |
Art Venere | Panama | Chemel, James L Cpa | Asiya Javayant |
Lenna Paprocki | Slovenia | Feltz Printing Service | Xuxue Feng |
Donette Foller | South Africa | Printing Dimensions | Asiya Javayant |
<p-table
[value]="customers"
[paginator]="true"
[rows]="5"
[showCurrentPageReport]="true"
[tableStyle]="{ 'min-width': '50rem' }"
currentPageReportTemplate="Showing {first} to {last} of {totalRecords} entries"
[rowsPerPageOptions]="[5, 10, 20]"
>
<ng-template pTemplate="header">
<tr>
<th style="width:25%">Name</th>
<th style="width:25%">Country</th>
<th style="width:25%">Company</th>
<th style="width:25%">Representative</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-customer>
<tr>
<td>{{ customer.name }}</td>
<td>{{ customer.country.name }}</td>
<td>{{ customer.company }}</td>
<td>{{ customer.representative.name }}</td>
</tr>
</ng-template>
<ng-template pTemplate="paginatorleft">
<p-button type="button" icon="pi pi-plus" styleClass="p-button-text"></p-button>
</ng-template>
<ng-template pTemplate="paginatorright">
<p-button type="button" icon="pi pi-cloud" styleClass="p-button-text"></p-button>
</ng-template>
</p-table>
paginator localization information such as page numbers and rows per page options are defined with the paginatorLocale property which defaults to the user locale.
Name | Country | Company | Representative |
---|---|---|---|
James Butt | Algeria | Benton, John B Jr | Ioni Bowcher |
Josephine Darakjy | Egypt | Chanay, Jeffrey A Esq | Amy Elsner |
Art Venere | Panama | Chemel, James L Cpa | Asiya Javayant |
Lenna Paprocki | Slovenia | Feltz Printing Service | Xuxue Feng |
Donette Foller | South Africa | Printing Dimensions | Asiya Javayant |
<p-table
[value]="customers"
[paginator]="true"
[rows]="5"
[showCurrentPageReport]="true"
[tableStyle]="{ 'min-width': '50rem' }"
currentPageReportTemplate="Showing {first} to {last} of {totalRecords} entries"
[rowsPerPageOptions]="[10, 25, 50]"
paginatorLocale="fa-IR"
>
<ng-template pTemplate="header">
<tr>
<th style="width:25%">Name</th>
<th style="width:25%">Country</th>
<th style="width:25%">Company</th>
<th style="width:25%">Representative</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-customer>
<tr>
<td>{{ customer.name }}</td>
<td>{{ customer.country.name }}</td>
<td>{{ customer.company }}</td>
<td>{{ customer.representative.name }}</td>
</tr>
</ng-template>
<ng-template pTemplate="paginatorleft">
<p-button type="button" icon="pi pi-plus" styleClass="p-button-text"></p-button>
</ng-template>
<ng-template pTemplate="paginatorright">
<p-button type="button" icon="pi pi-cloud" styleClass="p-button-text"></p-button>
</ng-template>
</p-table>
Paginator can also be controlled via model using a binding to the first property where changes trigger a pagination.
Name | Country | Company | Representative |
---|---|---|---|
James Butt | Algeria | Benton, John B Jr | Ioni Bowcher |
Josephine Darakjy | Egypt | Chanay, Jeffrey A Esq | Amy Elsner |
Art Venere | Panama | Chemel, James L Cpa | Asiya Javayant |
Lenna Paprocki | Slovenia | Feltz Printing Service | Xuxue Feng |
Donette Foller | South Africa | Printing Dimensions | Asiya Javayant |
Simona Morasca | Egypt | Chapman, Ross E Esq | Ivan Magalhaes |
Mitsue Tollner | Paraguay | Morlong Associates | Ivan Magalhaes |
Leota Dilliard | Serbia | Commercial Press | Onyama Limba |
Sage Wieser | Egypt | Truhlar And Truhlar Attys | Ivan Magalhaes |
Kris Marrier | Mexico | King, Christopher A Esq | Onyama Limba |
<div class="mb-3">
<p-button type="button" icon="pi pi-chevron-left" (click)="prev()" [disabled]="isFirstPage()" styleClass="p-button-text"></p-button>
<p-button type="button" icon="pi pi-refresh" (click)="reset()" styleClass="p-button-text"></p-button>
<p-button type="button" icon="pi pi-chevron-right" (click)="next()" [disabled]="isLastPage()" styleClass="p-button-text"></p-button>
</div>
<p-table
[value]="customers"
[paginator]="true"
[rows]="5"
[showCurrentPageReport]="true"
[tableStyle]="{ 'min-width': '50rem' }"
currentPageReportTemplate="Showing {first} to {last} of {totalRecords} entries"
(onPage)="pageChange($event)"
[rowsPerPageOptions]="[10, 25, 50]"
>
<ng-template pTemplate="header">
<tr>
<th style="width:25%">Name</th>
<th style="width:25%">Country</th>
<th style="width:25%">Company</th>
<th style="width:25%">Representative</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-customer>
<tr>
<td>{{ customer.name }}</td>
<td>{{ customer.country.name }}</td>
<td>{{ customer.company }}</td>
<td>{{ customer.representative.name }}</td>
</tr>
</ng-template>
<ng-template pTemplate="paginatorleft">
<p-button type="button" icon="pi pi-plus" styleClass="p-button-text"></p-button>
</ng-template>
<ng-template pTemplate="paginatorright">
<p-button type="button" icon="pi pi-cloud" styleClass="p-button-text"></p-button>
</ng-template>
</p-table>
A column can be made sortable by adding the pSortableColumn directive whose value is the field to sort against and a sort indicator via p-sortIcon component. For dynamic columns, setting pSortableColumnDisabled property as true disables sorting for that particular column.
Default sorting is executed on a single column, in order to enable multiple field sorting, set sortMode property to "multiple" and use metakey when clicking on another column.
Code | Name | Category | Quantity | Price |
---|---|---|---|---|
f230fh0g3 | Bamboo Watch | Accessories | 24 | $65.00 |
nvklal433 | Black Watch | Accessories | 61 | $72.00 |
zz21cz3c1 | Blue Band | Fitness | 2 | $79.00 |
244wgerg2 | Blue T-Shirt | Clothing | 25 | $29.00 |
h456wer53 | Bracelet | Accessories | 73 | $15.00 |
<p-table [value]="products" [tableStyle]="{'min-width': '60rem'}">
<ng-template pTemplate="header">
<tr>
<th pSortableColumn="code" style="width:20%">Code <p-sortIcon field="code"></p-sortIcon></th>
<th pSortableColumn="name" style="width:20%">Name <p-sortIcon field="name"></p-sortIcon></th>
<th pSortableColumn="category" style="width:20%">Category <p-sortIcon field="category"></p-sortIcon></th>
<th pSortableColumn="quantity" style="width:20%">Quantity <p-sortIcon field="quantity"></p-sortIcon></th>
<th pSortableColumn="price" style="width:20%">Price <p-sortIcon field="price"></p-sortIcon></th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-product>
<tr>
<td>{{product.code}}</td>
<td>{{product.name}}</td>
<td>{{product.category}}</td>
<td>{{product.quantity}}</td>
<td>{{product.price | currency: 'USD'}}</td>
</tr>
</ng-template>
</p-table>
Multiple columns can be sorted by defining sortMode as multiple. This mode requires metaKey (e.g. ⌘) to be pressed when clicking a header.
Code | Name | Category | Quantity | Price |
---|---|---|---|---|
f230fh0g3 | Bamboo Watch | Accessories | 24 | $65.00 |
nvklal433 | Black Watch | Accessories | 61 | $72.00 |
zz21cz3c1 | Blue Band | Fitness | 2 | $79.00 |
244wgerg2 | Blue T-Shirt | Clothing | 25 | $29.00 |
h456wer53 | Bracelet | Accessories | 73 | $15.00 |
<p-table [value]="products1" [tableStyle]="{'min-width': '60rem'}">
<ng-template pTemplate="header">
<tr>
<th pSortableColumn="code" style="width:20%">Code <p-sortIcon field="code"></p-sortIcon></th>
<th pSortableColumn="name" style="width:20%">Name <p-sortIcon field="name"></p-sortIcon></th>
<th pSortableColumn="category" style="width:20%">Category <p-sortIcon field="category"></p-sortIcon></th>
<th pSortableColumn="quantity" style="width:20%">Quantity <p-sortIcon field="quantity"></p-sortIcon></th>
<th pSortableColumn="price" style="width:20%">Price <p-sortIcon field="price"></p-sortIcon></th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-product>
<tr>
<td>{{product.code}}</td>
<td>{{product.name}}</td>
<td>{{product.category}}</td>
<td>{{product.quantity}}</td>
<td>{{product.price | currency: 'USD'}}</td>
</tr>
</ng-template>
</p-table>
Instead of using the built-in sorting algorithm a custom sort can be attached by enabling customSort property and defining a sortFunction implementation. This function gets a SortEvent instance that provides the data to sort, sortField, sortOrder and multiSortMeta.
Code | Name | Category | Quantity | Price |
---|---|---|---|---|
f230fh0g3 | Bamboo Watch | Accessories | 24 | $65.00 |
nvklal433 | Black Watch | Accessories | 61 | $72.00 |
zz21cz3c1 | Blue Band | Fitness | 2 | $79.00 |
244wgerg2 | Blue T-Shirt | Clothing | 25 | $29.00 |
h456wer53 | Bracelet | Accessories | 73 | $15.00 |
<p-table [value]="products" (sortFunction)="customSort($event)" [customSort]="true" [tableStyle]="{'min-width': '60rem'}">
<ng-template pTemplate="header">
<tr>
<th pSortableColumn="code" style="width:20%">Code <p-sortIcon field="code"></p-sortIcon></th>
<th pSortableColumn="name" style="width:20%">Name <p-sortIcon field="name"></p-sortIcon></th>
<th pSortableColumn="category" style="width:20%">Category <p-sortIcon field="category"></p-sortIcon></th>
<th pSortableColumn="quantity" style="width:20%">Quantity <p-sortIcon field="quantity"></p-sortIcon></th>
<th pSortableColumn="price" style="width:20%">Price <p-sortIcon field="price"></p-sortIcon></th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-product>
<tr>
<td>{{product.code}}</td>
<td>{{product.name}}</td>
<td>{{product.category}}</td>
<td>{{product.quantity}}</td>
<td>{{product.price | currency: 'USD'}}</td>
</tr>
</ng-template>
</p-table>
Filters are displayed in an overlay.
Name | Country | Agent | Date | Balance | Status | Activity | Verified |
---|---|---|---|---|---|---|---|
James Butt | ![]() | ![]() | 09/13/2015 | $70,663.00 | |||
Josephine Darakjy | ![]() | ![]() | 02/09/2019 | $82,429.00 | |||
Art Venere | ![]() | ![]() | 05/13/2017 | $28,334.00 | |||
Lenna Paprocki | ![]() | ![]() | 09/15/2020 | $88,521.00 | |||
Donette Foller | ![]() | ![]() | 05/20/2016 | $93,905.00 | |||
Simona Morasca | ![]() | ![]() | 02/16/2018 | $50,041.00 | |||
Mitsue Tollner | ![]() | ![]() | 02/19/2018 | $58,706.00 | |||
Leota Dilliard | ![]() | ![]() | 08/13/2019 | $26,640.00 | |||
Sage Wieser | ![]() | ![]() | 11/21/2018 | $65,369.00 | |||
Kris Marrier | ![]() | ![]() | 07/07/2015 | $63,451.00 |
<p-table
#dt1
[value]="customers"
dataKey="id"
[rows]="10"
[showCurrentPageReport]="true"
[rowsPerPageOptions]="[10, 25, 50]"
[loading]="loading"
[paginator]="true"
currentPageReportTemplate="Showing {first} to {last} of {totalRecords} entries"
[globalFilterFields]="['name', 'country.name', 'representative.name', 'status']"
>
<ng-template pTemplate="caption">
<div class="flex">
<button pButton label="Clear" class="p-button-outlined" icon="pi pi-filter-slash" (click)="clear(dt1)"></button>
<span class="p-input-icon-left ml-auto">
<i class="pi pi-search"></i>
<input pInputText type="text" (input)="dt1.filterGlobal($event.target.value, 'contains')" placeholder="Search keyword" />
</span>
</div>
</ng-template>
<ng-template pTemplate="header">
<tr>
<th style="min-width:15rem">
<div class="flex align-items-center">
Name
<p-columnFilter type="text" field="name" display="menu"></p-columnFilter>
</div>
</th>
<th style="min-width:15rem">
<div class="flex align-items-center">
Country
<p-columnFilter type="text" field="country.name" display="menu"></p-columnFilter>
</div>
</th>
<th style="min-width:15rem">
<div class="flex align-items-center">
Agent
<p-columnFilter field="representative" matchMode="in" display="menu" [showMatchModes]="false" [showOperator]="false" [showAddButton]="false">
<ng-template pTemplate="header">
<div class="px-3 pt-3 pb-0">
<span class="font-bold">Agent Picker</span>
</div>
</ng-template>
<ng-template pTemplate="filter" let-value let-filter="filterCallback">
<p-multiSelect [ngModel]="value" [options]="representatives" placeholder="Any" (onChange)="filter($event.value)" optionLabel="name">
<ng-template let-option pTemplate="item">
<div class="inline-block vertical-align-middle">
<img [alt]="option.label" src="https://primefaces.org/cdn/primeng/images/demo/avatar/{{ option.image }}" width="24" class="vertical-align-middle" />
<span class="ml-1 mt-1">{{ option.name }}</span>
</div>
</ng-template>
</p-multiSelect>
</ng-template>
</p-columnFilter>
</div>
</th>
<th style="min-width:10rem">
<div class="flex align-items-center">
Date
<p-columnFilter type="date" field="date" display="menu"></p-columnFilter>
</div>
</th>
<th style="min-width:10rem">
<div class="flex align-items-center">
Balance
<p-columnFilter type="numeric" field="balance" display="menu" currency="USD"></p-columnFilter>
</div>
</th>
<th style="min-width:10rem">
<div class="flex align-items-center">
Status
<p-columnFilter field="status" matchMode="equals" display="menu">
<ng-template pTemplate="filter" let-value let-filter="filterCallback">
<p-dropdown [ngModel]="value" [options]="statuses" (onChange)="filter($event.value)" placeholder="Any">
<ng-template let-option pTemplate="item">
<p-tag [value]="option.value" [severity]="getSeverity(option.label)"></p-tag>
</ng-template>
</p-dropdown>
</ng-template>
</p-columnFilter>
</div>
</th>
<th style="min-width:10rem">
<div class="flex align-items-center">
Activity
<p-columnFilter field="activity" matchMode="between" display="menu" [showMatchModes]="false" [showOperator]="false" [showAddButton]="false">
<ng-template pTemplate="filter" let-filter="filterCallback">
<p-slider [ngModel]="activityValues" [range]="true" (onSlideEnd)="filter($event.values)" styleClass="m-3"></p-slider>
<div class="flex align-items-center px-2">
<span>{{ activityValues[0] }}</span>
<span>{{ activityValues[1] }}</span>
</div>
</ng-template>
</p-columnFilter>
</div>
</th>
<th style="width: 3rem">
<div class="flex align-items-center">
Verified
<p-columnFilter type="boolean" field="verified" display="menu"></p-columnFilter>
</div>
</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-customer>
<tr>
<td>
{{ customer.name }}
</td>
<td>
<img src="https://primefaces.org/cdn/primeng/images/demo/flag/flag_placeholder.png" [class]="'flag flag-' + customer.country.code" style="width: 20px" />
<span class="ml-1 vertical-align-middle">{{ customer.country.name }}</span>
</td>
<td>
<img [alt]="customer.representative.name" src="https://primefaces.org/cdn/primeng/images/demo/avatar/{{ customer.representative.image }}" width="32" style="vertical-align: middle" />
<span class="ml-1 vertical-align-middle">{{ customer.representative.name }}</span>
</td>
<td>
{{ customer.date | date: 'MM/dd/yyyy' }}
</td>
<td>
{{ customer.balance | currency: 'USD':'symbol' }}
</td>
<td>
<p-tag [value]="customer.status" [severity]="getSeverity(customer.status)"></p-tag>
</td>
<td>
<p-progressBar [value]="customer.activity" [showValue]="false"></p-progressBar>
</td>
<td class="text-center">
<i class="pi" [ngClass]="{ 'text-green-500 pi-check-circle': customer.verified, 'text-red-500 pi-times-circle': !customer.verified }"></i>
</td>
</tr>
</ng-template>
<ng-template pTemplate="emptymessage">
<tr>
<td colspan="7">No customers found.</td>
</tr>
</ng-template>
</p-table>
Filters are displayed inline within a separate row.
Name | Country | Agent | Status | Verified |
---|---|---|---|---|
Any | Any | |||
James Butt | ![]() | ![]() | ||
Josephine Darakjy | ![]() | ![]() | ||
Art Venere | ![]() | ![]() | ||
Lenna Paprocki | ![]() | ![]() | ||
Donette Foller | ![]() | ![]() | ||
Simona Morasca | ![]() | ![]() | ||
Mitsue Tollner | ![]() | ![]() | ||
Leota Dilliard | ![]() | ![]() | ||
Sage Wieser | ![]() | ![]() | ||
Kris Marrier | ![]() | ![]() |
<p-table
#dt2
[value]="customers"
dataKey="id"
[rows]="10"
[showCurrentPageReport]="true"
[rowsPerPageOptions]="[10, 25, 50]"
[loading]="loading"
[paginator]="true"
currentPageReportTemplate="Showing {first} to {last} of {totalRecords} entries"
[globalFilterFields]="['name', 'country.name', 'representative.name', 'status']"
[tableStyle]="{ 'min-width': '75rem' }"
>
<ng-template pTemplate="caption">
<div class="flex">
<span class="p-input-icon-left ml-auto">
<i class="pi pi-search"></i>
<input pInputText type="text" (input)="dt2.filterGlobal($event.target.value, 'contains')" placeholder="Search keyword" />
</span>
</div>
</ng-template>
<ng-template pTemplate="header">
<tr>
<th style="width:22%">Name</th>
<th style="width:22%">Country</th>
<th style="width:22%">Agent</th>
<th style="width:22%">Status</th>
<th style="width:12%">Verified</th>
</tr>
<tr>
<th>
<p-columnFilter type="text" field="name"></p-columnFilter>
</th>
<th>
<p-columnFilter type="text" field="country.name"></p-columnFilter>
</th>
<th>
<p-columnFilter field="representative" matchMode="in" [showMenu]="false">
<ng-template pTemplate="filter" let-value let-filter="filterCallback">
<p-multiSelect [ngModel]="value" [options]="representatives" placeholder="Any" (onChange)="filter($event.value)" optionLabel="name">
<ng-template let-option pTemplate="item">
<div class="inline-block vertical-align-middle">
<img [alt]="option.label" src="https://primefaces.org/cdn/primeng/images/demo/avatar/{{ option.image }}" width="24" class="vertical-align-middle" />
<span class="ml-1 mt-1">{{ option.name }}</span>
</div>
</ng-template>
</p-multiSelect>
</ng-template>
</p-columnFilter>
</th>
<th>
<p-columnFilter field="status" matchMode="equals" [showMenu]="false">
<ng-template pTemplate="filter" let-value let-filter="filterCallback">
<p-dropdown [ngModel]="value" [options]="statuses" (onChange)="filter($event.value)" placeholder="Any" [showClear]="true">
<ng-template let-option pTemplate="item">
<p-tag [value]="option.value" [severity]="getSeverity(option.label)"></p-tag>
</ng-template>
</p-dropdown>
</ng-template>
</p-columnFilter>
</th>
<th>
<p-columnFilter type="boolean" field="verified"></p-columnFilter>
</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-customer>
<tr>
<td>
{{ customer.name }}
</td>
<td>
<img src="https://primefaces.org/cdn/primeng/images/demo/flag/flag_placeholder.png" [class]="'flag flag-' + customer.country.code" style="width: 20px" />
<span class="ml-1 vertical-align-middle">{{ customer.country.name }}</span>
</td>
<td>
<img [alt]="customer.representative.name" src="https://primefaces.org/cdn/primeng/images/demo/avatar/{{ customer.representative.image }}" width="32" style="vertical-align: middle" />
<span class="ml-1 vertical-align-middle">{{ customer.representative.name }}</span>
</td>
<td>
<p-tag [value]="customer.status" [severity]="getSeverity(customer.status)"></p-tag>
</td>
<td>
<i class="pi" [ngClass]="{ 'text-green-500 pi-check-circle': customer.verified, 'text-red-500 pi-times-circle': !customer.verified }"></i>
</td>
</tr>
</ng-template>
<ng-template pTemplate="emptymessage">
<tr>
<td colspan="5">No customers found.</td>
</tr>
</ng-template>
</p-table>
Single row selection is enabled by defining selectionMode as single along with a value binding using selection property. When available, it is suggested to provide a unique identifier of a row with dataKey to optimize performance.
By default, metaKey press (e.g. ⌘) is necessary to unselect a row however this can be configured with disabling the metaKeySelection property. In touch enabled devices this option has no effect and behavior is same as setting it to false.
Code | Name | Category | Quantity |
---|---|---|---|
f230fh0g3 | Bamboo Watch | Accessories | 24 |
nvklal433 | Black Watch | Accessories | 61 |
zz21cz3c1 | Blue Band | Fitness | 2 |
244wgerg2 | Blue T-Shirt | Clothing | 25 |
h456wer53 | Bracelet | Accessories | 73 |
<p-table [value]="products" selectionMode="single" [(selection)]="selectedProduct" dataKey="code" [tableStyle]="{'min-width': '50rem'}">
<ng-template pTemplate="header">
<tr>
<th>Code</th>
<th>Name</th>
<th>Category</th>
<th>Quantity</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-product>
<tr [pSelectableRow]="product">
<td>{{product.code}}</td>
<td>{{product.name}}</td>
<td>{{product.category}}</td>
<td>{{product.quantity}}</td>
</tr>
</ng-template>
</p-table>
In multiple mode, selection binding should be an array. For touch enabled devices, selection is managed by tapping and for other devices metakey or shiftkey are required. Setting metaKeySelection property as false enables multiple selection without meta key.
Code | Name | Category | Quantity |
---|---|---|---|
f230fh0g3 | Bamboo Watch | Accessories | 24 |
nvklal433 | Black Watch | Accessories | 61 |
zz21cz3c1 | Blue Band | Fitness | 2 |
244wgerg2 | Blue T-Shirt | Clothing | 25 |
h456wer53 | Bracelet | Accessories | 73 |
<div class="flex justify-content-center align-items-center gap-2 mb-3">
<p-inputSwitch inputId="metakey" [(ngModel)]="metaKeySelection" label="MetaKey"></p-inputSwitch>
<span>MetaKey</span>
</div>
<p-table [value]="products" selectionMode="multiple" [(selection)]="selectedProducts" [metaKeySelection]="metaKeySelection" dataKey="code" [tableStyle]="{ 'min-width': '50rem' }">
<ng-template pTemplate="caption"> Multiple Selection with MetaKey </ng-template>
<ng-template pTemplate="header">
<tr>
<th>Code</th>
<th>Name</th>
<th>Category</th>
<th>Quantity</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-product let-rowIndex="rowIndex">
<tr [pSelectableRow]="product" [pSelectableRowIndex]="rowIndex">
<td>{{ product.code }}</td>
<td>{{ product.name }}</td>
<td>{{ product.category }}</td>
<td>{{ product.quantity }}</td>
</tr>
</ng-template>
</p-table>
Multiple selection can also be handled using checkboxes by enabling the selectionMode property of column as multiple.
Code | Name | Category | Quantity | |
---|---|---|---|---|
f230fh0g3 | Bamboo Watch | Accessories | 24 | |
nvklal433 | Black Watch | Accessories | 61 | |
zz21cz3c1 | Blue Band | Fitness | 2 | |
244wgerg2 | Blue T-Shirt | Clothing | 25 | |
h456wer53 | Bracelet | Accessories | 73 |
<p-table [value]="products" [(selection)]="selectedProducts" dataKey="code" [tableStyle]="{'min-width': '50rem'}">
<ng-template pTemplate="header">
<tr>
<th style="width: 4rem">
<p-tableHeaderCheckbox></p-tableHeaderCheckbox>
</th>
<th>Code</th>
<th>Name</th>
<th>Category</th>
<th>Quantity</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-product>
<tr>
<td>
<p-tableCheckbox [value]="product"></p-tableCheckbox>
</td>
<td>{{product.code}}</td>
<td>{{product.name}}</td>
<td>{{product.category}}</td>
<td>{{product.quantity}}</td>
</tr>
</ng-template>
</p-table>
Single selection can also be handled using radio buttons.
Code | Name | Category | Quantity | |
---|---|---|---|---|
f230fh0g3 | Bamboo Watch | Accessories | 24 | |
nvklal433 | Black Watch | Accessories | 61 | |
zz21cz3c1 | Blue Band | Fitness | 2 | |
244wgerg2 | Blue T-Shirt | Clothing | 25 | |
h456wer53 | Bracelet | Accessories | 73 |
<p-table [value]="products" [(selection)]="selectedProduct" dataKey="code" [tableStyle]="{'min-width': '50rem'}">
<ng-template pTemplate="header">
<tr>
<th style="width: 4rem"></th>
<th>Code</th>
<th>Name</th>
<th>Category</th>
<th>Quantity</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-product>
<tr>
<td>
<p-tableRadioButton [value]="product"></p-tableRadioButton>
</td>
<td>{{product.code}}</td>
<td>{{product.name}}</td>
<td>{{product.category}}</td>
<td>{{product.quantity}}</td>
</tr>
</ng-template>
</p-table>
Row selection can be controlled by utilizing rowSelectable and disabled properties.
Code | Name | Category | Quantity | |
---|---|---|---|---|
f230fh0g3 | Bamboo Watch | Accessories | 24 | |
nvklal433 | Black Watch | Accessories | 61 | |
zz21cz3c1 | Blue Band | Fitness | 2 | |
244wgerg2 | Blue T-Shirt | Clothing | 25 | |
h456wer53 | Bracelet | Accessories | 73 |
<p-table [value]="products" [(selection)]="selectedProducts" dataKey="code" [rowSelectable]="isRowSelectable" [tableStyle]="{'min-width': '50rem'}">
<ng-template pTemplate="header">
<tr>
<th style="width: 4rem">
<p-tableHeaderCheckbox></p-tableHeaderCheckbox>
</th>
<th style="min-width:200px">Code</th>
<th style="min-width:200px">Name</th>
<th style="min-width:200px">Category</th>
<th style="min-width:200px">Quantity</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-product>
<tr>
<td>
<p-tableCheckbox [value]="product" [disabled]="isOutOfStock(product)"></p-tableCheckbox>
</td>
<td>{{product.code}}</td>
<td>{{product.name}}</td>
<td>{{product.category}}</td>
<td>{{product.quantity}}</td>
</tr>
</ng-template>
</p-table>
Code | Name | Category | Quantity | |
---|---|---|---|---|
f230fh0g3 | Bamboo Watch | Accessories | 24 | |
nvklal433 | Black Watch | Accessories | 61 | |
zz21cz3c1 | Blue Band | Fitness | 2 | |
244wgerg2 | Blue T-Shirt | Clothing | 25 | |
h456wer53 | Bracelet | Accessories | 73 |
<p-table [value]="products" [(selection)]="selectedProducts" dataKey="code" [paginator]="true" [rows]="5" [selectionPageOnly]="true" [tableStyle]="{'min-width': '50rem'}">
<ng-template pTemplate="header">
<tr>
<th style="width: 4rem">
<p-tableHeaderCheckbox></p-tableHeaderCheckbox>
</th>
<th style="min-width:200px">Code</th>
<th style="min-width:200px">Name</th>
<th style="min-width:200px">Category</th>
<th style="min-width:200px">Quantity</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-product>
<tr>
<td>
<p-tableCheckbox [value]="product"></p-tableCheckbox>
</td>
<td>{{product.code}}</td>
<td>{{product.name}}</td>
<td>{{product.category}}</td>
<td>{{product.quantity}}</td>
</tr>
</ng-template>
</p-table>
Table provides onRowSelect and onRowUnselect events to listen selection events.
Code | Name | Category | Quantity |
---|---|---|---|
f230fh0g3 | Bamboo Watch | Accessories | 24 |
nvklal433 | Black Watch | Accessories | 61 |
zz21cz3c1 | Blue Band | Fitness | 2 |
244wgerg2 | Blue T-Shirt | Clothing | 25 |
h456wer53 | Bracelet | Accessories | 73 |
<p-toast></p-toast>
<p-table [value]="products" selectionMode="single" [(selection)]="selectedProduct" dataKey="code"
(onRowSelect)="onRowSelect($event)" (onRowUnselect)="onRowUnselect($event)" [tableStyle]="{'min-width': '50rem'}">
<ng-template pTemplate="header">
<tr>
<th>Code</th>
<th>Name</th>
<th>Category</th>
<th>Quantity</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-product>
<tr [pSelectableRow]="product">
<td>{{product.code}}</td>
<td>{{product.name}}</td>
<td>{{product.category}}</td>
<td>{{product.quantity}}</td>
</tr>
</ng-template>
</p-table>
Selection using custom elements.
Code | Name | Category | Quantity | |
---|---|---|---|---|
f230fh0g3 | Bamboo Watch | Accessories | 24 | |
nvklal433 | Black Watch | Accessories | 61 | |
zz21cz3c1 | Blue Band | Fitness | 2 | |
244wgerg2 | Blue T-Shirt | Clothing | 25 | |
h456wer53 | Bracelet | Accessories | 73 |
<p-toast></p-toast>
<p-table [value]="products" [tableStyle]="{ 'min-width': '50rem' }">
<ng-template pTemplate="header">
<tr>
<th>Code</th>
<th>Name</th>
<th>Category</th>
<th>Quantity</th>
<th style="width: 5rem"></th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-product>
<tr>
<td>{{ product.code }}</td>
<td>{{ product.name }}</td>
<td>{{ product.category }}</td>
<td>{{ product.quantity }}</td>
<td>
<button type="button" pButton pRipple icon="pi pi-plus" (click)="selectProduct(product)"></button>
</td>
</tr>
</ng-template>
</p-table>
Row expansion allows displaying detailed content for a particular row. To use this feature, add a template named rowexpansion and use the pRowToggler directive whose value is the row data instance on an element of your choice whose click event toggles the expansion. This enables providing your custom UI such as buttons, links and so on. Example below uses an anchor with an icon as a toggler. Setting pRowTogglerDisabled as true disables the toggle event for the element.
Name | Image | Price | Category | Reviews | Status | |
---|---|---|---|---|---|---|
Bamboo Watch | ![]() | $65.00 | Accessories | |||
Black Watch | ![]() | $72.00 | Accessories | |||
Blue Band | ![]() | $79.00 | Fitness | |||
Blue T-Shirt | ![]() | $29.00 | Clothing | |||
Bracelet | ![]() | $15.00 | Accessories | |||
Brown Purse | ![]() | $120.00 | Accessories | |||
Chakra Bracelet | ![]() | $32.00 | Accessories | |||
Galaxy Earrings | ![]() | $34.00 | Accessories | |||
Game Controller | ![]() | $99.00 | Electronics | |||
Gaming Set | ![]() | $299.00 | Electronics |
<p-table [value]="products" dataKey="name" [tableStyle]="{ 'min-width': '60rem' }">
<ng-template pTemplate="header">
<tr>
<th style="width: 5rem"></th>
<th pSortableColumn="name">Name <p-sortIcon field="name"></p-sortIcon></th>
<th>Image</th>
<th pSortableColumn="price">Price <p-sortIcon field="price"></p-sortIcon></th>
<th pSortableColumn="category">Category <p-sortIcon field="category"></p-sortIcon></th>
<th pSortableColumn="rating">Reviews <p-sortIcon field="rating"></p-sortIcon></th>
<th pSortableColumn="inventoryStatus">Status <p-sortIcon field="inventoryStatus"></p-sortIcon></th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-product let-expanded="expanded">
<tr>
<td>
<button type="button" pButton pRipple [pRowToggler]="product" class="p-button-text p-button-rounded p-button-plain" [icon]="expanded ? 'pi pi-chevron-down' : 'pi pi-chevron-right'"></button>
</td>
<td>{{ product.name }}</td>
<td><img [src]="'https://primefaces.org/cdn/primeng/images/demo/product/' + product.image" [alt]="product.name" width="50" class="shadow-4" /></td>
<td>{{ product.price | currency: 'USD' }}</td>
<td>{{ product.category }}</td>
<td><p-rating [ngModel]="product.rating" [readonly]="true" [cancel]="false"></p-rating></td>
<td>
<p-tag [value]="product.inventoryStatus" [severity]="getSeverity(product.inventoryStatus)"></p-tag>
</td>
</tr>
</ng-template>
<ng-template pTemplate="rowexpansion" let-product>
<tr>
<td colspan="7">
<div class="p-3">
<p-table [value]="product.orders" dataKey="id">
<ng-template pTemplate="header">
<tr>
<th pSortableColumn="id">Id <p-sortIcon field="price"></p-sortIcon></th>
<th pSortableColumn="customer">Customer <p-sortIcon field="customer"></p-sortIcon></th>
<th pSortableColumn="date">Date <p-sortIcon field="date"></p-sortIcon></th>
<th pSortableColumn="amount">Amount <p-sortIcon field="amount"></p-sortIcon></th>
<th pSortableColumn="status">Status <p-sortIcon field="status"></p-sortIcon></th>
<th style="width: 4rem"></th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-order>
<tr>
<td>{{ order.id }}</td>
<td>{{ order.customer }}</td>
<td>{{ order.date }}</td>
<td>{{ order.amount | currency: 'USD' }}</td>
<td>
<p-tag [value]="order.status" [severity]="getStatusSeverity(order.status)"></p-tag>
</td>
<td><p-button type="button" icon="pi pi-plus"></p-button></td>
</tr>
</ng-template>
<ng-template pTemplate="emptymessage">
<tr>
<td colspan="6">There are no order for this product yet.</td>
</tr>
</ng-template>
</p-table>
</div>
</td>
</tr>
</ng-template>
</p-table>
Row editing toggles the visibility of all the editors in the row at once and provides additional options to save and cancel editing. Row editing functionality is enabled by setting the editMode to "row" on table, defining a dataKey to uniquely identify a row, adding pEditableRow directive to the editable rows and defining the UI Controls with pInitEditableRow, pSaveEditableRow and pCancelEditableRow directives respectively.
Save and Cancel functionality implementation is left to the page author to provide more control over the editing business logic. Example below utilizes a simple implementation where a row is cloned when editing is initialized and is saved or restored depending on the result of the editing. An implicit variable called "editing" is passed to the body template so you may come up with your own UI controls that implement editing based on your own requirements such as adding validations and styling. Note that pSaveEditableRow only switches the row to back view mode when there are no validation errors.
Moreover, you may use setting pEditableRowDisabled property as true to disable editing for that particular row and in case you need to display rows in edit mode by default, use the editingRowKeys property which is a map whose key is the dataKey of the record where the value is any arbitrary number greater than zero.
Code | Name | Inventory Status | Price | |
---|---|---|---|---|
<p-toast></p-toast>
<p-table [value]="products" dataKey="id" editMode="row" [tableStyle]="{'min-width': '50rem'}">
<ng-template pTemplate="header">
<tr>
<th style="width:20%">Code</th>
<th style="width:20%">Name</th>
<th style="width:20%">Inventory Status</th>
<th style="width:20%">Price</th>
<th style="width:20%"></th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-product let-editing="editing" let-ri="rowIndex">
<tr [pEditableRow]="product">
<td>
<p-cellEditor>
<ng-template pTemplate="input">
<input pInputText type="text" [(ngModel)]="product.code">
</ng-template>
<ng-template pTemplate="output">
{{product.code}}
</ng-template>
</p-cellEditor>
</td>
<td>
<p-cellEditor>
<ng-template pTemplate="input">
<input pInputText type="text" [(ngModel)]="product.name" required>
</ng-template>
<ng-template pTemplate="output">
{{product.name}}
</ng-template>
</p-cellEditor>
</td>
<td>
<p-cellEditor>
<ng-template pTemplate="input">
<p-dropdown [options]="statuses" appendTo="body" [(ngModel)]="product.inventoryStatus" [style]="{'width':'100%'}"></p-dropdown>
</ng-template>
<ng-template pTemplate="output">
{{product.inventoryStatus}}
</ng-template>
</p-cellEditor>
</td>
<td>
<p-cellEditor>
<ng-template pTemplate="input">
<input pInputText type="text" [(ngModel)]="product.price">
</ng-template>
<ng-template pTemplate="output">
{{product.price | currency: 'USD'}}
</ng-template>
</p-cellEditor>
</td>
<td>
<div class="flex align-items-center justify-content-center gap-2">
<button *ngIf="!editing" pButton pRipple type="button" pInitEditableRow icon="pi pi-pencil" (click)="onRowEditInit(product)" class="p-button-rounded p-button-text"></button>
<button *ngIf="editing" pButton pRipple type="button" pSaveEditableRow icon="pi pi-check" (click)="onRowEditSave(product)" class="p-button-rounded p-button-text p-button-success mr-2"></button>
<button *ngIf="editing" pButton pRipple type="button" pCancelEditableRow icon="pi pi-times" (click)="onRowEditCancel(product, ri)" class="p-button-rounded p-button-text p-button-danger"></button>
</div>
</td>
</tr>
</ng-template>
</p-table>
In-cell editing is enabled by adding pEditableColumn directive to an editable cell that has a p-cellEditor helper component to define the input-output templates for the edit and view modes respectively.
Code | Name | Inventory Status | Price |
---|---|---|---|
<p-table [value]="products" dataKey="id" [tableStyle]="{ 'min-width': '50rem' }">
<ng-template pTemplate="header">
<tr>
<th style="width:25%">Code</th>
<th style="width:25%">Name</th>
<th style="width:25%">Inventory Status</th>
<th style="width:25%">Price</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-product let-editing="editing">
<tr>
<td [pEditableColumn]="product.code" pEditableColumnField="code">
<p-cellEditor>
<ng-template pTemplate="input">
<input pInputText type="text" [(ngModel)]="product.code" />
</ng-template>
<ng-template pTemplate="output">
{{ product.code }}
</ng-template>
</p-cellEditor>
</td>
<td [pEditableColumn]="product.name" pEditableColumnField="name">
<p-cellEditor>
<ng-template pTemplate="input">
<input pInputText type="text" [(ngModel)]="product.name" required />
</ng-template>
<ng-template pTemplate="output">
{{ product.name }}
</ng-template>
</p-cellEditor>
</td>
<td [pEditableColumn]="product.inventoryStatus" pEditableColumnField="inventoryStatus">
<p-cellEditor>
<ng-template pTemplate="input">
<input pInputText [(ngModel)]="product.inventoryStatus" />
</ng-template>
<ng-template pTemplate="output">
{{ product.inventoryStatus }}
</ng-template>
</p-cellEditor>
</td>
<td [pEditableColumn]="product.price" pEditableColumnField="price">
<p-cellEditor>
<ng-template pTemplate="input">
<input pInputText type="text" [(ngModel)]="product.price" />
</ng-template>
<ng-template pTemplate="output">
{{ product.price | currency: 'USD' }}
</ng-template>
</p-cellEditor>
</td>
</tr>
</ng-template>
</p-table>
Lazy mode is handy to deal with large datasets, instead of loading the entire data, small chunks of data is loaded by invoking onLazyLoad callback everytime paging, sorting and filtering happens. Sample here loads the data from remote datasource efficiently using lazy loading. Also, the implementation of checkbox selection in lazy tables is left entirely to the user. Since the table component does not know what will happen to the data on the next page or whether there are instant data changes, the selection array can be implemented in several ways. One of them is as in the example below.
Name | Country | Company | Representative | |
---|---|---|---|---|
Any | ||||
James Butt | Algeria | Benton, John B Jr | Ioni Bowcher | |
Josephine Darakjy | Egypt | Chanay, Jeffrey A Esq | Amy Elsner | |
Art Venere | Panama | Chemel, James L Cpa | Asiya Javayant | |
Lenna Paprocki | Slovenia | Feltz Printing Service | Xuxue Feng | |
Donette Foller | South Africa | Printing Dimensions | Asiya Javayant | |
Simona Morasca | Egypt | Chapman, Ross E Esq | Ivan Magalhaes | |
Mitsue Tollner | Paraguay | Morlong Associates | Ivan Magalhaes | |
Leota Dilliard | Serbia | Commercial Press | Onyama Limba | |
Sage Wieser | Egypt | Truhlar And Truhlar Attys | Ivan Magalhaes | |
Kris Marrier | Mexico | King, Christopher A Esq | Onyama Limba |
<p-table
[value]="customers"
[lazy]="true"
(onLazyLoad)="loadCustomers($event)"
dataKey="id"
[tableStyle]="{ 'min-width': '75rem' }"
[selection]="selectedCustomers"
(selectionChange)="onSelectionChange($event)"
[selectAll]="selectAll"
(selectAllChange)="onSelectAllChange($event)"
[paginator]="true"
[rows]="10"
[totalRecords]="totalRecords"
[loading]="loading"
[globalFilterFields]="['name', 'country.name', 'company', 'representative.name']"
>
<ng-template pTemplate="header">
<tr>
<th style="width: 4rem"></th>
<th pSortableColumn="name">Name <p-sortIcon field="name"></p-sortIcon></th>
<th pSortableColumn="country.name">Country <p-sortIcon field="country.name"></p-sortIcon></th>
<th pSortableColumn="company">Company <p-sortIcon field="company"></p-sortIcon></th>
<th pSortableColumn="representative.name">Representative <p-sortIcon field="representative.name"></p-sortIcon></th>
</tr>
<tr>
<th style="width: 4rem">
<p-tableHeaderCheckbox></p-tableHeaderCheckbox>
</th>
<th>
<p-columnFilter type="text" field="name"></p-columnFilter>
</th>
<th>
<p-columnFilter type="text" field="country.name"></p-columnFilter>
</th>
<th>
<p-columnFilter type="text" field="company"></p-columnFilter>
</th>
<th>
<p-columnFilter field="representative" matchMode="in" [showMenu]="false">
<ng-template pTemplate="filter" let-value let-filter="filterCallback">
<p-multiSelect [ngModel]="value" appendTo="body" [options]="representatives" placeholder="Any" (onChange)="filter($event.value)" optionLabel="name" [maxSelectedLabels]="1" [selectedItemsLabel]="'{0} items'">
<ng-template let-option pTemplate="item">
<div class="inline-block vertical-align-middle">
<img [alt]="option.label" src="https://primefaces.org/cdn/primeng/images/demo/avatar/{{ option.image }}" width="24" class="vertical-align-middle" />
<span class="ml-1 mt-1">{{ option.name }}</span>
</div>
</ng-template>
</p-multiSelect>
</ng-template>
</p-columnFilter>
</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-customer>
<tr>
<td>
<p-tableCheckbox [value]="customer"></p-tableCheckbox>
</td>
<td>{{ customer.name }}</td>
<td>{{ customer.country.name }}</td>
<td>{{ customer.company }}</td>
<td>{{ customer.representative.name }}</td>
</tr>
</ng-template>
</p-table>
Adding scrollable property along with a scrollHeight for the data viewport enables vertical scrolling with fixed headers.
Name | Country | Company | Representative |
---|---|---|---|
James Butt | Algeria | Benton, John B Jr | Ioni Bowcher |
Josephine Darakjy | Egypt | Chanay, Jeffrey A Esq | Amy Elsner |
Art Venere | Panama | Chemel, James L Cpa | Asiya Javayant |
Lenna Paprocki | Slovenia | Feltz Printing Service | Xuxue Feng |
Donette Foller | South Africa | Printing Dimensions | Asiya Javayant |
Simona Morasca | Egypt | Chapman, Ross E Esq | Ivan Magalhaes |
Mitsue Tollner | Paraguay | Morlong Associates | Ivan Magalhaes |
Leota Dilliard | Serbia | Commercial Press | Onyama Limba |
Sage Wieser | Egypt | Truhlar And Truhlar Attys | Ivan Magalhaes |
Kris Marrier | Mexico | King, Christopher A Esq | Onyama Limba |
Minna Amigon | Romania | Dorl, James J Esq | Anna Fali |
Abel Maclead | Singapore | Rangoni Of Florence | Bernardo Dominic |
Kiley Caldarera | Serbia | Feiner Bros | Onyama Limba |
Graciela Ruta | Chile | Buckley Miller & Wright | Amy Elsner |
Cammy Albares | Philippines | Rousseaux, Michael Esq | Asiya Javayant |
Mattie Poquette | Venezuela | Century Communications | Anna Fali |
Meaghan Garufi | Malaysia | Bolton, Wilbur Esq | Ivan Magalhaes |
Gladys Rim | Netherlands | T M Byxbee Company Pc | Stephen Shaw |
Yuki Whobrey | Israel | Farmers Insurance Group | Bernardo Dominic |
Fletcher Flosi | Argentina | Post Box Services Plus | Xuxue Feng |
Bette Nicka | Paraguay | Sport En Art | Onyama Limba |
Veronika Inouye | Ecuador | C 4 Network Inc | Ioni Bowcher |
Willard Kolmetz | Tunisia | Ingalls, Donald R Esq | Asiya Javayant |
Maryann Royster | Belarus | Franklin, Peter L Esq | Elwin Sharvill |
Alisha Slusarski | Iceland | Wtlz Power 107 Fm | Stephen Shaw |
Allene Iturbide | Italy | Ledecky, David Esq | Ivan Magalhaes |
Chanel Caudy | Argentina | Professional Image Inc | Ioni Bowcher |
Ezekiel Chui | Ireland | Sider, Donald C Esq | Amy Elsner |
Willow Kusko | Romania | U Pull It | Onyama Limba |
Bernardo Figeroa | Israel | Clark, Richard Cpa | Ioni Bowcher |
Ammie Corrio | Hungary | Moskowitz, Barry S | Asiya Javayant |
Francine Vocelka | Honduras | Cascade Realty Advisors Inc | Ioni Bowcher |
Ernie Stenseth | Australia | Knwz Newsradio | Xuxue Feng |
Albina Glick | Ukraine | Giampetro, Anthony D | Bernardo Dominic |
Alishia Sergi | Qatar | Milford Enterprises Inc | Ivan Magalhaes |
Solange Shinko | Cameroon | Mosocco, Ronald A | Onyama Limba |
Jose Stockham | Italy | Tri State Refueler Co | Amy Elsner |
Rozella Ostrosky | Venezuela | Parkway Company | Amy Elsner |
Valentine Gillian | Paraguay | Fbs Business Finance | Bernardo Dominic |
Kati Rulapaugh | Puerto Rico | Eder Assocs Consltng Engrs Pc | Ioni Bowcher |
Youlanda Schemmer | Bolivia | Tri M Tool Inc | Xuxue Feng |
Dyan Oldroyd | Argentina | International Eyelets Inc | Amy Elsner |
Roxane Campain | France | Rapid Trading Intl | Anna Fali |
Lavera Perin | Vietnam | Abc Enterprises Inc | Stephen Shaw |
Erick Ferencz | Belgium | Cindy Turner Associates | Amy Elsner |
Fatima Saylors | Canada | Stanton, James D Esq | Onyama Limba |
Jina Briddick | Mexico | Grace Pastries Inc | Xuxue Feng |
Kanisha Waycott | Ecuador | Schroer, Gene E Esq | Xuxue Feng |
Emerson Bowley | Finland | Knights Inn | Stephen Shaw |
Blair Malet | Finland | Bollinger Mach Shp & Shipyard | Asiya Javayant |
<p-table [value]="customers" [scrollable]="true" scrollHeight="400px" [tableStyle]="{'min-width': '50rem'}">
<ng-template pTemplate="header">
<tr>
<th>Name</th>
<th>Country</th>
<th>Company</th>
<th>Representative</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-customer>
<tr>
<td>{{customer.name}}</td>
<td>{{customer.country.name}}</td>
<td>{{customer.company}}</td>
<td>{{customer.representative.name}}</td>
</tr>
</ng-template>
</p-table>
Horizontal and vertical scroll can be used together to enable double axis scrolling.
Id | Name | Country | Date | Balance | Company | Status | Activity | Representative |
---|---|---|---|---|---|---|---|---|
1000 | James Butt | Algeria | 2015-09-13 | $70,663.00 | Benton, John B Jr | unqualified | 17 | Ioni Bowcher |
1001 | Josephine Darakjy | Egypt | 2019-02-09 | $82,429.00 | Chanay, Jeffrey A Esq | proposal | 0 | Amy Elsner |
1002 | Art Venere | Panama | 2017-05-13 | $28,334.00 | Chemel, James L Cpa | qualified | 63 | Asiya Javayant |
1003 | Lenna Paprocki | Slovenia | 2020-09-15 | $88,521.00 | Feltz Printing Service | new | 37 | Xuxue Feng |
1004 | Donette Foller | South Africa | 2016-05-20 | $93,905.00 | Printing Dimensions | proposal | 33 | Asiya Javayant |
1005 | Simona Morasca | Egypt | 2018-02-16 | $50,041.00 | Chapman, Ross E Esq | qualified | 68 | Ivan Magalhaes |