Row Pinning in Angular Grid Component
The Syncfusion ® Angular Grid control provides option to pin specific rows at the top, ensuring important information remains visible while scrolling vertically. This feature is useful for keeping specific rows always visible for quick reference or priority viewing, regardless of user interactions.
To enable row pinning, configure the isRowPinned callback function. This function receives each row’s data and returns true or false based on the desired pinning condition. It executes only during the initial rendering of the Grid, ensuring that each row’s pinning state is determined at that stage.
Row pinning does not alter the overall content height of the Grid. The scrollable area remains fully visible, allowing smooth vertical scrolling even when multiple rows are pinned. Pinned rows appear in a fixed region above the content area, while the Grid preserves its layout and continues to allow full interaction with all other rows.
The following example pins rows with “Critical” priority and “Open” status at the top using the isRowPinned callback function.
import { data } from './datasource';
import { Component, OnInit } from '@angular/core';
import { GridModule } from '@syncfusion/ej2-angular-grids';
export interface RowData {
TaskID: number;
Title: string;
Status: string;
Priority: string;
Assignee?: string;
}
@Component({
imports: [ GridModule],
standalone: true,
selector: 'app-root',
template: `<div>
<ejs-grid [dataSource]='data' height="260" [isRowPinned]="isRowPinned">
<e-columns>
<e-column field="TaskID" headerText="ID" width="80" isPrimaryKey="true" textAlign="Right"></e-column>
<e-column field="Title" headerText="Title" width="100"></e-column>
<e-column field="Status" headerText="Status" width="100"></e-column>
<e-column field="Assignee" headerText="Assignee" width="100"></e-column>
<e-column field="Priority" headerText="Priority" width="100"></e-column>
</e-columns>
</ejs-grid>
</div>`
})
export class AppComponent implements OnInit {
public data?: object[];
ngOnInit(): void {
this.data = data;
}
public isRowPinned=( data: RowData)=>
{
if (data && data.Status === 'Open' && data.Priority === 'Critical') {
return true;
}
return false;
}
}import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));Pinning rows requires a primary key column for mapping the pinned rows to their original records. To define the primary key, set columns->isPrimaryKey to
truein particular column.
Paging with row pinning
Row pinning keeps important rows visible at the top while navigating pages. Pinned rows are excluded from the page size calculation and remain fixed. For example, if pageSettings->pageSize is set to “10” and “2” rows are pinned, those “2” rows stay at the top while the grid displays “10” additional rows beneath them. When navigating to another page, the pinned rows remain fixed, and the next set of records is rendered below.
import { data } from './datasource';
import { Component, OnInit } from '@angular/core';
import { GridModule, PageService, PageSettingsModel } from '@syncfusion/ej2-angular-grids';
export interface RowData {
TaskID: number;
Title: string;
Status: string;
Priority: string;
Assignee?: string;
}
@Component({
imports: [GridModule],
providers: [PageService],
standalone: true,
selector: 'app-root',
template: `<div>
<ejs-grid [dataSource]='data' height="250" [allowPaging]="true" [pageSettings]="pageSettings" [isRowPinned]="isRowPinned">
<e-columns>
<e-column field="TaskID" headerText="ID" width="80" isPrimaryKey="true" textAlign="Right"></e-column>
<e-column field="Title" headerText="Title" width="100"></e-column>
<e-column field="Status" headerText="Status" width="100"></e-column>
<e-column field="Assignee" headerText="Assignee" width="100"></e-column>
<e-column field="Priority" headerText="Priority" width="100"></e-column>
</e-columns>
</ejs-grid>
</div>`
})
export class AppComponent implements OnInit {
public data?: object[];
public pageSettings!: PageSettingsModel;
ngOnInit(): void {
this.data = data;
this.pageSettings={pageSize:10};
}
public isRowPinned=( data: RowData)=>
{
if (data && data.Status === 'Open' && data.Priority === 'Critical') {
return true;
}
return false;
}
}import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));Pinned rows selection
Pinned rows can be selected like regular rows, with their selection state synchronized with corresponding data rows via the primary key. This ensures consistent behavior across all grid operations, such as paging, sorting, and filtering, whether selection is performed using the mouse, keyboard, or checkbox.
import { data } from './datasource';
import { Component, OnInit } from '@angular/core';
import { GridModule, PageService, PageSettingsModel } from '@syncfusion/ej2-angular-grids';
export interface RowData {
TaskID: number;
Title: string;
Status: string;
Priority: string;
Assignee?: string;
}
@Component({
imports: [GridModule],
providers: [PageService],
standalone: true,
selector: 'app-root',
template: `<div>
<ejs-grid [dataSource]='data' height="250" [allowPaging]="true" [pageSettings]="pageSettings" [isRowPinned]="isRowPinned">
<e-columns>
<e-column field="TaskID" headerText="ID" width="80" isPrimaryKey="true" textAlign="Right"></e-column>
<e-column field="Title" headerText="Title" width="100"></e-column>
<e-column field="Status" headerText="Status" width="100"></e-column>
<e-column field="Assignee" headerText="Assignee" width="100"></e-column>
<e-column field="Priority" headerText="Priority" width="100"></e-column>
</e-columns>
</ejs-grid>
</div>`
})
export class AppComponent implements OnInit {
public data?: object[];
public pageSettings!: PageSettingsModel;
ngOnInit(): void {
this.data = data;
this.pageSettings={pageSize:10};
}
public isRowPinned=( data: RowData)=>
{
if (data && data.Status === 'Open' && data.Priority === 'Critical') {
return true;
}
return false;
}
}import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));The selectionSettings->persistSelection will be automatically enabled when pinning the rows using the
isRowPinnedcallback function.
Filtering and sorting with pinned rows
Row pinning is fully compatible with filtering and sorting operations. When a filter is applied, both pinned and scrollable sections display only rows that meet the filter criteria, ensuring consistent results and preventing data mismatches.
When sorting is applied, pinned rows are reordered using the same sorting rules while remaining fixed at the top. This maintains visibility and correct positioning of pinned rows even as the remaining data is reorganized.
import { data } from './datasource';
import { Component, OnInit } from '@angular/core';
import { FilterService, FilterSettingsModel, GridModule, PageService, PageSettingsModel, SortService } from '@syncfusion/ej2-angular-grids';
export interface RowData {
TaskID: number;
Title: string;
Status: string;
Priority: string;
Assignee?: string;
}
@Component({
imports: [GridModule],
providers: [PageService, FilterService, SortService],
standalone: true,
selector: 'app-root',
template: `<div>
<ejs-grid [dataSource]='data' height="250" [allowPaging]="true" [pageSettings]="pageSettings" [allowSorting]="true" [allowFiltering]="true" [filterSettings]="filterSettings" [isRowPinned]="isRowPinned">
<e-columns>
<e-column field="TaskID" headerText="ID" width="80" isPrimaryKey="true" textAlign="Right"></e-column>
<e-column field="Title" headerText="Title" width="100"></e-column>
<e-column field="Status" headerText="Status" width="100"></e-column>
<e-column field="Assignee" headerText="Assignee" width="100"></e-column>
<e-column field="Priority" headerText="Priority" width="100"></e-column>
</e-columns>
</ejs-grid>
</div>`
})
export class AppComponent implements OnInit {
public data?: object[];
public pageSettings!: PageSettingsModel;
public filterSettings!: FilterSettingsModel;
ngOnInit(): void {
this.data = data;
this.pageSettings={ pageSize: 10 };
this.filterSettings={ type: 'Menu' };
}
public isRowPinned=( data: RowData)=>
{
if (data && data.Status === 'Open' && data.Priority === 'Critical') {
return true;
}
return false;
}
}import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));CRUD operations with row pinning
Row pinning fully supports CRUD operations with automatic synchronization between pinned rows and their original rows. Any edit, update, or deletion is immediately reflected in both views, ensuring consistent and accurate data.
import { data } from './datasource';
import { Component, OnInit } from '@angular/core';
import { EditService, EditSettingsModel, GridModule, PageService, PageSettingsModel } from '@syncfusion/ej2-angular-grids';
export interface RowData {
TaskID: number;
Title: string;
Status: string;
Priority: string;
Assignee?: string;
}
@Component({
imports: [ GridModule],
providers: [PageService, EditService],
standalone: true,
selector: 'app-root',
template: `<div>
<ejs-grid [dataSource]='data' height="215" [isRowPinned]="isRowPinned" [allowPaging]="true" [pageSettings]="pageSettings" [editSettings]="editSettings" [isRowPinned]="isRowPinned" height="200">
<e-columns>
<e-column field="TaskID" headerText="ID" width="80" isPrimaryKey="true" textAlign="Right"></e-column>
<e-column field="Title" headerText="Title" width="100"></e-column>
<e-column field="Status" headerText="Status" width="100"></e-column>
<e-column field="Assignee" headerText="Assignee" width="100"></e-column>
<e-column field="Priority" headerText="Priority" width="100"></e-column>
</e-columns>
</ejs-grid>
</div>`
})
export class AppComponent implements OnInit {
public data?: object[];
public pageSettings: PageSettingsModel = { pageSize: 15};
public editSettings: EditSettingsModel = { allowEditing: true, allowAdding: true, allowDeleting: true, mode: 'Normal' };
ngOnInit(): void {
this.data = data;
}
public isRowPinned=( data: RowData)=>
{
if (data && data.Status === 'Open' && data.Priority === 'Critical') {
return true;
}
return false;
}
}import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));Dynamic row pinning
Dynamic row pinning is available through the built-in context menu, allowing quick actions pin or unpin rows. Right-click any row to access PinRow and UnpinRow options for flexible row management.
import { taskData } from './datasource';
import { Component, OnInit } from '@angular/core';
import { ContextMenuService, GridModule, PageService, PageSettingsModel } from '@syncfusion/ej2-angular-grids';
export interface RowData {
TaskID: number;
Title: string;
Status: string;
Priority: string;
Assignee?: string;
}
@Component({
imports: [ GridModule ],
providers: [PageService, ContextMenuService],
standalone: true,
selector: 'app-root',
template: `<div>
<ejs-grid [dataSource]='data' [isRowPinned]="isRowPinned" height='210' [allowPaging]="true" [pageSettings]="pageSettings" [contextMenuItems]="contextMenuItems">
<e-columns>
<e-column field="TaskID" headerText="ID" width="80" isPrimaryKey="true" textAlign="Right"></e-column>
<e-column field="Title" headerText="Title" width="100"></e-column>
<e-column field="Status" headerText="Status" width="100"></e-column>
<e-column field="Assignee" headerText="Assignee" width="100"></e-column>
<e-column field="Priority" headerText="Priority" width="100"></e-column>
</e-columns>
</ejs-grid>
</div>`
})
export class AppComponent implements OnInit {
public data?: object[];
public contextMenuItems: string[] = ['PinRow', 'UnpinRow'];
public pageSettings: PageSettingsModel = { pageSize: 15 };
ngOnInit(): void {
this.data = taskData;
}
public isRowPinned=( data: RowData)=>
{
if (data && data.Status === 'Open' && data.Priority === 'Critical') {
return true;
}
return false;
}
}import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));