Row Groups
Row groups allow you to organize large spreadsheets by combining related rows into collapsible sections. This section explains how to define and configure groups, the programmatic methods available to manage them, and the events triggered when groups are expanded or collapsed.
What's new with Version 13
- Outline levels: nested groups render the Excel outline model, with
jss_group_headeron the owner rows andjss_group_level_1tojss_group_level_8for the nesting depth. The default visuals are enabled with thegroupIndentationoption- Visibility: opening a parent never reveals the content of a closed nested group, and nested owners keep their own state
- Validation:
setRowGrouprefuses invalid positions or a negative size withfalse
Documentation
Methods
The following methods manage row groups programmatically.
| Method | Description |
|---|---|
getRowGroup |
Get all row groups.getRowGroup() : object |
setRowGroup |
Create a new collapsible group of rows.setRowGroup(rowNumber: Number, numOfItems: Number) : void |
resetRowGroup |
Reset the row group.resetRowGroup(rowNumber: Number) : void |
openRowGroup |
Open a group of rows.openRowGroup(rowNumber?: Number|Number[]) : void |
closeRowGroup |
Close a group of rows.closeRowGroup(rowNumber?: Number|Number[]) : void |
Events
Events triggered by row group actions.
| Property | Description |
|---|---|
ongrouprow |
When the user creates, updates or resets a group of rowsongrouprow?: (worksheet: Object, row: Number, numOfItems: Number) => void |
onopenrowgroup |
When the user opens a group of rowsonopenrowgroup?: (worksheet: Object, row: Number) => void |
oncloserowgroup |
When the user closes a group of rowsoncloserowgroup?: (worksheet: Object, row: Number) => void |
Initial Settings
Configure row groups during initialization using these row properties.
| Property | Description |
|---|---|
group?: number |
Number of rows to include in this group |
state?: boolean |
Whether the group starts expanded. Default: false (collapsed) |
Worksheet Level
| Property | Description |
|---|---|
groupIndentation?: boolean | number |
Enable the default outline visuals: the owner rows are highlighted and the first data cell is indented per nesting level. true indents by 16px; a number sets the pixel step. Default: off |
Outline levels
Nested row groups render the Excel outline model. The owner row of a group carries the jss_group_header class, and every row inside a group carries jss_group_level_1 to jss_group_level_8, the nesting depth, with eight levels as in Excel. The classes are always emitted, so a stylesheet can style the outline; the default visuals are opt-in through groupIndentation. A row stays hidden while any enclosing group is closed: opening a parent never reveals the content of a closed nested group, and nested owners keep their own open state.
The open and close state of a group is a personal view preference: it is never persisted or synchronized to other connected users.
Examples
Basic Collapsible Row Group
Create collapsible row groups in Jspreadsheet.
<html>
<script src="https://jspreadsheet.com/v13/jspreadsheet.js"></script>
<script src="https://jsuites.net/v6/jsuites.js"></script>
<link rel="stylesheet" href="https://jsuites.net/v6/jsuites.css" type="text/css" />
<link rel="stylesheet" href="https://jspreadsheet.com/v13/jspreadsheet.css" type="text/css" />
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Material+Icons" />
<div id="spreadsheet"></div>
<p><input type="button" value="setRowGroup(0,3)" id="btn1" />
<input type="button" value="resetRowGroup(0)" id="btn2" /></p>
<script>
// You can use the following license for quick testing on localhost, StackBlitz, or CodeSandbox.
// The license is valid for one day, after which the spreadsheet will become read-only.
// For a longer trial period, you can create a free account and generate a demo license with an extended expiration date.
jspreadsheet.setLicense('MDNkMjE4NjNmMDc0YTkwZDQ1MGViZmQ0ZDc4ZWNjMzc0MTMxZThlMzNhYTQ0OGYzYTNhMTI0NDYwMzg1NGIzMTcyNjk1YTA0MDM4ZjdkZDczMWFkYTM0ODZmZmQyMGYyMDVmY2JhYTc2N2MzN2QzMjgxZjQ1MzJjZmI2ZDI2NjQsZXlKamJHbGxiblJKWkNJNklpSXNJbTVoYldVaU9pSktjM0J5WldGa2MyaGxaWFFpTENKa1lYUmxJam94Tnprd01qRXlOemszTENKa2IyMWhhVzRpT2xzaWFuTndjbVZoWkhOb1pXVjBMbU52YlNJc0ltTnZaR1Z6WVc1a1ltOTRMbWx2SWl3aWFuTm9aV3hzTG01bGRDSXNJbU56WWk1aGNIQWlMQ0p6ZEdGamEySnNhWFI2TG1sdklpd2lkMlZpWTI5dWRHRnBibVZ5TG1sdklpd2liRzlqWVd4b2IzTjBJbDBzSW5Cc1lXNGlPaUl6TkNJc0luTmpiM0JsSWpwYkluWTNJaXdpZGpnaUxDSjJPU0lzSW5ZeE1DSXNJbll4TVNJc0luWXhNaUlzSW5ZeE15SXNJbU5vWVhKMGN5SXNJbVp2Y20xeklpd2labTl5YlhWc1lTSXNJbkJoY25ObGNpSXNJbkpsYm1SbGNpSXNJbU52YlcxbGJuUnpJaXdpYVcxd2IzSjBaWElpTENKaVlYSWlMQ0oyWVd4cFpHRjBhVzl1Y3lJc0luTmxZWEpqYUNJc0luQnlhVzUwSWl3aWMyaGxaWFJ6SWl3aVkyeHBaVzUwSWl3aWMyVnlkbVZ5SWl3aWMyaGhjR1Z6SWl3aVptOXliV0YwSWl3aWNHbDJiM1FpWFN3aVpHVnRieUk2ZEhKMVpYMD0=');
let data = [];
for (let j = 0; j < 50; j++) {
data[j] = [];
for (let i = 0; i < 50; i++) {
data[j][i] = jspreadsheet.helpers.getColumnNameFromCoords(i, j);
}
}
// Create a new spreadsheet
let spreadsheet = jspreadsheet(document.getElementById('spreadsheet'), {
tableOverflow: true,
tableWidth: '700px',
tableHeight: '400px',
worksheets: [{
data: data,
rows: { 6: { group: 3 }},
}]
});
document.getElementById("btn1").onclick = () => spreadsheet[0].setRowGroup(0,3);
document.getElementById("btn2").onclick = () => spreadsheet[0].resetRowGroup(0);
</script>
</html>
import React, {useRef} from "react";
import {Spreadsheet, Worksheet, jspreadsheet} from "@jspreadsheet/react";
import "jsuites/dist/jsuites.css";
import "jspreadsheet/dist/jspreadsheet.css";
// You can use the following license for quick testing on localhost, StackBlitz, or CodeSandbox.
// The license is valid for one day, after which the spreadsheet will become read-only.
// For a longer trial period, you can create a free account and generate a demo license with an extended expiration date.
jspreadsheet.setLicense('MDNkMjE4NjNmMDc0YTkwZDQ1MGViZmQ0ZDc4ZWNjMzc0MTMxZThlMzNhYTQ0OGYzYTNhMTI0NDYwMzg1NGIzMTcyNjk1YTA0MDM4ZjdkZDczMWFkYTM0ODZmZmQyMGYyMDVmY2JhYTc2N2MzN2QzMjgxZjQ1MzJjZmI2ZDI2NjQsZXlKamJHbGxiblJKWkNJNklpSXNJbTVoYldVaU9pSktjM0J5WldGa2MyaGxaWFFpTENKa1lYUmxJam94Tnprd01qRXlOemszTENKa2IyMWhhVzRpT2xzaWFuTndjbVZoWkhOb1pXVjBMbU52YlNJc0ltTnZaR1Z6WVc1a1ltOTRMbWx2SWl3aWFuTm9aV3hzTG01bGRDSXNJbU56WWk1aGNIQWlMQ0p6ZEdGamEySnNhWFI2TG1sdklpd2lkMlZpWTI5dWRHRnBibVZ5TG1sdklpd2liRzlqWVd4b2IzTjBJbDBzSW5Cc1lXNGlPaUl6TkNJc0luTmpiM0JsSWpwYkluWTNJaXdpZGpnaUxDSjJPU0lzSW5ZeE1DSXNJbll4TVNJc0luWXhNaUlzSW5ZeE15SXNJbU5vWVhKMGN5SXNJbVp2Y20xeklpd2labTl5YlhWc1lTSXNJbkJoY25ObGNpSXNJbkpsYm1SbGNpSXNJbU52YlcxbGJuUnpJaXdpYVcxd2IzSjBaWElpTENKaVlYSWlMQ0oyWVd4cFpHRjBhVzl1Y3lJc0luTmxZWEpqYUNJc0luQnlhVzUwSWl3aWMyaGxaWFJ6SWl3aVkyeHBaVzUwSWl3aWMyVnlkbVZ5SWl3aWMyaGhjR1Z6SWl3aVptOXliV0YwSWl3aWNHbDJiM1FpWFN3aVpHVnRieUk2ZEhKMVpYMD0=');
export default function App() {
// Spreadsheet array of worksheets
const spreadsheet = useRef();
// Data
let data = [];
for (let j = 0; j < 50; j++) {
data[j] = [];
for (let i = 0; i < 50; i++) {
data[j][i] = jspreadsheet.helpers.getColumnNameFromCoords(i, j);
}
}
// Rows settings
const rows = {6: {group: 3}}
// Render component
return (
<>
<Spreadsheet ref={spreadsheet} tableOverflow tableWidth="700px" tableHeight="400px">
<Worksheet data={data} rows={rows} />
</Spreadsheet>
<input type="button" value="setRowGroup(0,3)" onClick={() => spreadsheet.current[0].setRowGroup(0, 3)} />
<input type="button" value="resetRowGroup(0)" onClick={() => spreadsheet.current[0].resetRowGroup(0)} />
</>
);
}
<template>
<Spreadsheet ref="spreadsheet" tableOverflow tableWidth="700px" tableHeight="400px">
<Worksheet :data="data" :rows="rows" />
</Spreadsheet>
<input type="button" value="setRowGroup(0,3)" @click="setRowGroup(0,3)" />
<input type="button" value="resetRowGroup(0)" @click="resetRowGroup(0);" />
</template>
<script>
import { Spreadsheet, Worksheet, jspreadsheet } from "@jspreadsheet/vue";
import "jsuites/dist/jsuites.css";
import "jspreadsheet/dist/jspreadsheet.css";
// You can use the following license for quick testing on localhost, StackBlitz, or CodeSandbox.
// The license is valid for one day, after which the spreadsheet will become read-only.
// For a longer trial period, you can create a free account and generate a demo license with an extended expiration date.
jspreadsheet.setLicense('MDNkMjE4NjNmMDc0YTkwZDQ1MGViZmQ0ZDc4ZWNjMzc0MTMxZThlMzNhYTQ0OGYzYTNhMTI0NDYwMzg1NGIzMTcyNjk1YTA0MDM4ZjdkZDczMWFkYTM0ODZmZmQyMGYyMDVmY2JhYTc2N2MzN2QzMjgxZjQ1MzJjZmI2ZDI2NjQsZXlKamJHbGxiblJKWkNJNklpSXNJbTVoYldVaU9pSktjM0J5WldGa2MyaGxaWFFpTENKa1lYUmxJam94Tnprd01qRXlOemszTENKa2IyMWhhVzRpT2xzaWFuTndjbVZoWkhOb1pXVjBMbU52YlNJc0ltTnZaR1Z6WVc1a1ltOTRMbWx2SWl3aWFuTm9aV3hzTG01bGRDSXNJbU56WWk1aGNIQWlMQ0p6ZEdGamEySnNhWFI2TG1sdklpd2lkMlZpWTI5dWRHRnBibVZ5TG1sdklpd2liRzlqWVd4b2IzTjBJbDBzSW5Cc1lXNGlPaUl6TkNJc0luTmpiM0JsSWpwYkluWTNJaXdpZGpnaUxDSjJPU0lzSW5ZeE1DSXNJbll4TVNJc0luWXhNaUlzSW5ZeE15SXNJbU5vWVhKMGN5SXNJbVp2Y20xeklpd2labTl5YlhWc1lTSXNJbkJoY25ObGNpSXNJbkpsYm1SbGNpSXNJbU52YlcxbGJuUnpJaXdpYVcxd2IzSjBaWElpTENKaVlYSWlMQ0oyWVd4cFpHRjBhVzl1Y3lJc0luTmxZWEpqYUNJc0luQnlhVzUwSWl3aWMyaGxaWFJ6SWl3aVkyeHBaVzUwSWl3aWMyVnlkbVZ5SWl3aWMyaGhjR1Z6SWl3aVptOXliV0YwSWl3aWNHbDJiM1FpWFN3aVpHVnRieUk2ZEhKMVpYMD0=');
// Data
let data = [];
for (let j = 0; j < 50; j++) {
data[j] = [];
for (let i = 0; i < 50; i++) {
data[j][i] = jspreadsheet.helpers.getColumnNameFromCoords(i, j);
}
}
export default {
components: {
Spreadsheet,
Worksheet,
},
methods: {
setRowGroup(row, numOfRows) {
this.$refs.spreadsheet.current[0].setRowGroup(row, numOfRows);
},
resetRowGroup(row) {
this.$refs.spreadsheet.current[0].resetRowGroup(row);
},
},
data() {
return {
// Worksheet data
data: data,
// Rows
rows: { 6: { group: 3 }}
};
}
}
</script>
import { Component, ViewChild, ElementRef, AfterViewInit } from "@angular/core";
import jspreadsheet from "jspreadsheet";
import "jsuites/dist/jsuites.css";
import "jspreadsheet/dist/jspreadsheet.css";
// You can use the following license for quick testing on localhost, StackBlitz, or CodeSandbox.
// The license is valid for one day, after which the spreadsheet will become read-only.
// For a longer trial period, you can create a free account and generate a demo license with an extended expiration date.
jspreadsheet.setLicense('MDNkMjE4NjNmMDc0YTkwZDQ1MGViZmQ0ZDc4ZWNjMzc0MTMxZThlMzNhYTQ0OGYzYTNhMTI0NDYwMzg1NGIzMTcyNjk1YTA0MDM4ZjdkZDczMWFkYTM0ODZmZmQyMGYyMDVmY2JhYTc2N2MzN2QzMjgxZjQ1MzJjZmI2ZDI2NjQsZXlKamJHbGxiblJKWkNJNklpSXNJbTVoYldVaU9pSktjM0J5WldGa2MyaGxaWFFpTENKa1lYUmxJam94Tnprd01qRXlOemszTENKa2IyMWhhVzRpT2xzaWFuTndjbVZoWkhOb1pXVjBMbU52YlNJc0ltTnZaR1Z6WVc1a1ltOTRMbWx2SWl3aWFuTm9aV3hzTG01bGRDSXNJbU56WWk1aGNIQWlMQ0p6ZEdGamEySnNhWFI2TG1sdklpd2lkMlZpWTI5dWRHRnBibVZ5TG1sdklpd2liRzlqWVd4b2IzTjBJbDBzSW5Cc1lXNGlPaUl6TkNJc0luTmpiM0JsSWpwYkluWTNJaXdpZGpnaUxDSjJPU0lzSW5ZeE1DSXNJbll4TVNJc0luWXhNaUlzSW5ZeE15SXNJbU5vWVhKMGN5SXNJbVp2Y20xeklpd2labTl5YlhWc1lTSXNJbkJoY25ObGNpSXNJbkpsYm1SbGNpSXNJbU52YlcxbGJuUnpJaXdpYVcxd2IzSjBaWElpTENKaVlYSWlMQ0oyWVd4cFpHRjBhVzl1Y3lJc0luTmxZWEpqYUNJc0luQnlhVzUwSWl3aWMyaGxaWFJ6SWl3aVkyeHBaVzUwSWl3aWMyVnlkbVZ5SWl3aWMyaGhjR1Z6SWl3aVptOXliV0YwSWl3aWNHbDJiM1FpWFN3aVpHVnRieUk2ZEhKMVpYMD0=');
// Data
let data: any = [];
for (let j = 0; j < 50; j++) {
data[j] = [];
for (let i = 0; i < 50; i++) {
data[j][i] = jspreadsheet.helpers.getColumnNameFromCoords(i, j);
}
}
@Component({
standalone: true,
selector: "app-root",
template: `
<div #spreadsheet></div>
<input type="button" value="setRowGroup(0,3)" (click)="this.worksheets[0].setRowGroup(0,3)" />
<input type="button" value="resetRowGroup(0)" (click)="this.worksheets[0].resetRowGroup(0);" />`
})
export class AppComponent implements AfterViewInit {
@ViewChild("spreadsheet") spreadsheet: ElementRef;
// Worksheets
worksheets: jspreadsheet.worksheetInstance[];
// Create a new data grid
ngAfterViewInit() {
// Create spreadsheet
this.worksheets = jspreadsheet(this.spreadsheet.nativeElement, {
tableOverflow: true,
tableWidth: '700px',
tableHeight: '400px',
worksheets: [
{
data: data,
rows: { 6: { group: 3 }},
}
]
});
}
}