Row Spanning in Angular Grid Component
16 Sep 202520 minutes to read
The grid provides an option to span row cells, allowing you to merge two or more cells in a row into a single cell. This feature can be useful in scenarios where you want to display information that spans across multiple rows, but want to avoid repeating the same information in each row.
To achieve this, You need to define the rowSpan attribute to span cells in the queryCellInfo event. The rowSpan attribute is used to specify the number of rows that the current cell should span.
The queryCellInfo event triggers for each cell during grid rendering, providing opportunity to customize individual cells. Within this event handler, evaluate cell data and conditions to determine appropriate spanning behavior.
In the following example, the Davolio cell spans two rows in the EmployeeName column. Additionally, the Grid demonstrates simultaneous row and column spanning where the Lunch Break cell spans two rows and three columns in the 1:00 time slot:
import { NgModule } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
import { GridModule } from '@syncfusion/ej2-angular-grids'
import { Component, OnInit } from '@angular/core';
import { QueryCellInfoEventArgs, GridLine, Column } from '@syncfusion/ej2-angular-grids';
import { columnSpanData, ColumnSpanDataType } from './datasource';
import { EmitType } from '@syncfusion/ej2-base';
@Component({
imports: [
GridModule
],
standalone: true,
selector: 'app-root',
template: `<ejs-grid [dataSource]='data' [height]='300' [width]='width' [gridLines]='gridLines'
[allowTextWrap]='textWrap' (queryCellInfo)='queryCellInfoEvent($event)'>
<e-columns>
<e-column field='EmployeeID' headerText='Employee ID' width='150' textAlign="Right" isPrimaryKey=true></e-column>
<e-column field='EmployeeName' headerText='Employee Name' width='200'></e-column>
<e-column field='9:00' headerText='9:00 AM' width='120'></e-column>
<e-column field='9:30' headerText='9:30 AM' width='120'></e-column>
<e-column field='10:00' headerText='10:00 AM' width='120'></e-column>
<e-column field='10:30' headerText='10:30 AM' width='120'></e-column>
<e-column field='11:00' headerText='11:00 AM' width='120'></e-column>
<e-column field='11:30' headerText='11:30 AM' width='120'></e-column>
<e-column field='12:00' headerText='12:00 PM' width='120'></e-column>
<e-column field='12:30' headerText='12:30 PM' width='120'></e-column>
<e-column field='1:00' headerText='1:00 PM' width='120'></e-column>
<e-column field='1:30' headerText='1:30 PM' width='120'></e-column>
<e-column field='2:00' headerText='2:00 PM' width='120'></e-column>
<e-column field='2:30' headerText='2:30 PM' width='120'></e-column>
<e-column field='3:00' headerText='3:00 PM' width='120'></e-column>
<e-column field='3:30' headerText='3:30 PM' width='120'></e-column>
<e-column field='4:00' headerText='4:00 PM' width='120'></e-column>
<e-column field='4:30' headerText='4:30 PM' width='120'></e-column>
<e-column field='5:00' headerText='5:00 PM' width='120'></e-column>
</e-columns>
</ejs-grid>`
})
export class AppComponent implements OnInit {
public data?: object[];
public width?: string | number;
public gridLines?: GridLine;
public textWrap?: boolean;
public queryCellInfoEvent: EmitType<QueryCellInfoEventArgs> = (args: QueryCellInfoEventArgs) => {
const data: ColumnSpanDataType = args.data as ColumnSpanDataType;
switch (data.EmployeeID) {
case 10001:
if (((args.column as Column) as Column).field === '9:00' || (args.column as Column).field === '2:30' || (args.column as Column).field === '4:30') {
args.colSpan = 2;
} else if ((args.column as Column).field === '11:00') {
args.colSpan = 3;
} else if ((args.column as Column).field === 'EmployeeName') {
args.rowSpan = 2;
} else if ((args.column as Column).field === '1:00') {
args.colSpan = 3;
args.rowSpan = 2;
}
break;
case 10002:
if ((args.column as Column).field === '9:30' || (args.column as Column).field === '2:30' ||
(args.column as Column).field === '4:30') {
args.colSpan = 3;
} else if ((args.column as Column).field === '11:00') {
args.colSpan = 4;
}
break;
case 10003:
if ((args.column as Column).field === '9:00' || (args.column as Column).field === '11:30') {
args.colSpan = 3;
} else if ((args.column as Column).field === '10:30' || (args.column as Column).field === '3:30' ||
(args.column as Column).field === '4:30' || (args.column as Column).field === '2:30') {
args.colSpan = 2;
}
break;
case 10004:
if ((args.column as Column).field === '9:00') {
args.colSpan = 3;
} else if ((args.column as Column).field === '11:00') {
args.colSpan = 4;
} else if ((args.column as Column).field === '4:00' || (args.column as Column).field === '2:30') {
args.colSpan = 2;
}
break;
case 10005:
if ((args.column as Column).field === '9:00') {
args.colSpan = 4;
} else if ((args.column as Column).field === '11:30') {
args.colSpan = 3;
} else if ((args.column as Column).field === '3:30' || (args.column as Column).field === '4:30' || (args.column as Column).field === '2:30') {
args.colSpan = 2;
}
break;
case 10006:
if ((args.column as Column).field === '9:00' || (args.column as Column).field === '4:30' ||
(args.column as Column).field === '2:30' || (args.column as Column).field === '3:30') {
args.colSpan = 2;
} else if ((args.column as Column).field === '10:00' || (args.column as Column).field === '11:30') {
args.colSpan = 3;
}
break;
case 10007:
if ((args.column as Column).field === '9:00' || (args.column as Column).field === '3:00' || (args.column as Column).field === '10:30') {
args.colSpan = 2;
} else if ((args.column as Column).field === '11:30' || (args.column as Column).field === '4:00') {
args.colSpan = 3;
}
break;
case 10008:
if ((args.column as Column).field === '9:00' || (args.column as Column).field === '10:30' || (args.column as Column).field === '2:30') {
args.colSpan = 3;
} else if ((args.column as Column).field === '4:00') {
args.colSpan = 2;
}
break;
case 10009:
if ((args.column as Column).field === '9:00' || (args.column as Column).field === '11:30') {
args.colSpan = 3;
} else if ((args.column as Column).field === '4:30' || (args.column as Column).field === '2:30') {
args.colSpan = 2;
}
break;
case 100010:
if ((args.column as Column).field === '9:00' || (args.column as Column).field === '2:30' ||
(args.column as Column).field === '4:00' || (args.column as Column).field === '11:30') {
args.colSpan = 3;
} else if ((args.column as Column).field === '10:30') {
args.colSpan = 2;
}
break;
}
}
ngOnInit(): void {
this.data = columnSpanData;
this.gridLines = 'Both';
this.width = 'auto';
this.textWrap = true;
}
}import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));To disable spanning for a particular grid page, use the requestType property from the
queryCellInfoevent argument to identify the current operation.
TherowSpanandcolSpanattributes can be used together to merge cells both vertically and horizontally for complex layout requirements.
Limitations
- The updateCell method does not support row spanning.
- Row spanning is not compatible with the following features:
- Virtual scrolling
- Infinite scrolling
- Grouping
- Row drag and drop
- Autofill
- Inline editing
- Batch editing
- CRUD
Row spanning using enableRowSpan property
The Syncfusion Angular Grid introduces a simplified approach to vertically merge cells using the enableRowSpan property.
When the enableRowSpan property is enabled, the Grid automatically merges cells with matching data across adjacent columns without requiring manual span configuration using the queryCellInfo event. These merged cells are visually combined into a single cell, improving readability.
Here is an example of how to use the enableRowSpan property to merge cells vertically:
import { NgModule } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
import { Component, OnInit, ViewChild } from '@angular/core';
import { GridComponent, GridModule, FreezeService, SortService } from '@syncfusion/ej2-angular-grids';
import { telecastData } from './datasource';
@Component({
selector: 'app-root',
standalone: true,
imports: [GridModule],
providers: [FreezeService, SortService],
template: `
<ejs-grid #grid [dataSource]="telecastData" gridLines="Both" [enableHover]="false" [allowSelection]="false" [allowSorting]="true" [enableRowSpan]="true" [allowTextWrap]="true" [textWrapSettings]="{ wrapMode: 'Content' }" height="450" width="auto">
<e-columns>
<e-column field="Channel" headerText="Channel" width="150" freeze="Left" isPrimaryKey="true"></e-column>
<e-column field="Genre" headerText="Genre" width="120" freeze="Left"></e-column>
<e-column field="Program12AM" headerText="12 AM" width="110" textAlign="Center" [allowSorting]="false"></e-column>
<e-column field="Program1AM" headerText="1 AM" width="110" textAlign="Center" [allowSorting]="false"></e-column>
<e-column field="Program2AM" headerText="2 AM" width="110" textAlign="Center" [allowSorting]="false"></e-column>
<e-column field="Program3AM" headerText="3 AM" width="110" textAlign="Center" [allowSorting]="false"></e-column>
<e-column field="Program4AM" headerText="4 AM" width="110" textAlign="Center" [allowSorting]="false"></e-column>
<e-column field="Program5AM" headerText="5 AM" width="110" textAlign="Center" [allowSorting]="false"></e-column>
<e-column field="Program6AM" headerText="6 AM" width="110" textAlign="Center" [allowSorting]="false"></e-column>
<e-column field="Program7AM" headerText="7 AM" width="110" textAlign="Center" [allowSorting]="false"></e-column>
<e-column field="Program8AM" headerText="8 AM" width="110" textAlign="Center" [allowSorting]="false"></e-column>
<e-column field="Program9AM" headerText="9 AM" width="110" textAlign="Center" [allowSorting]="false"></e-column>
<e-column field="Program10AM" headerText="10 AM" width="110" textAlign="Center" [allowSorting]="false"></e-column>
<e-column field="Program11AM" headerText="11 AM" width="110" textAlign="Center" [allowSorting]="false"></e-column>
<e-column field="Program12PM" headerText="12 PM" width="110" textAlign="Center" [allowSorting]="false"></e-column>
<e-column field="Program1PM" headerText="1 PM" width="110" textAlign="Center" [allowSorting]="false"></e-column>
<e-column field="Program2PM" headerText="2 PM" width="110" textAlign="Center" [allowSorting]="false"></e-column>
<e-column field="Program3PM" headerText="3 PM" width="110" textAlign="Center" [allowSorting]="false"></e-column>
<e-column field="Program4PM" headerText="4 PM" width="110" textAlign="Center" [allowSorting]="false"></e-column>
<e-column field="Program5PM" headerText="5 PM" width="110" textAlign="Center" [allowSorting]="false"></e-column>
<e-column field="Program6PM" headerText="6 PM" width="110" textAlign="Center" [allowSorting]="false"></e-column>
<e-column field="Program7PM" headerText="7 PM" width="110" textAlign="Center" [allowSorting]="false"></e-column>
<e-column field="Program8PM" headerText="8 PM" width="110" textAlign="Center" [allowSorting]="false"></e-column>
<e-column field="Program9PM" headerText="9 PM" width="110" textAlign="Center" [allowSorting]="false"></e-column>
<e-column field="Program10PM" headerText="10 PM" width="110" textAlign="Center" [allowSorting]="false"></e-column>
<e-column field="Program11PM" headerText="11 PM" width="110" textAlign="Center" [allowSorting]="false"></e-column>
</e-columns>
</ejs-grid>
`
})
export class AppComponent implements OnInit {
public telecastData?: Object[];
@ViewChild('grid')
public grid?: GridComponent;
ngOnInit(): void {
this.telecastData = telecastData;
}
}import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));You can also control spanning at the column level. To prevent merging for specific columns, set
enableRowSpanto false in the column definition.
Limitation
- Virtualization
- Infinite Scrolling
- Lazy Load Grouping
- Row Drag and Drop
- Column Virtualization
- Detail Template
- Editing
- Export
- Foreign Key
- Hierarchy Grid