
# row group

Row grouping divides data rows based on the values of one or more specified columns and displays them in a hierarchical structure. If you drag a column header to the panel area, grouping is performed by that column. Alternatively, you can group by calling the GridView.groupBy() function.

#### Grouping

If you drag a column header to the panel area, grouping is performed by that column.
  Alternatively, you can group by calling the [GridView.groupBy()](/refs/Class/GridView#groupby) function.

```js
const fields = [
  { fieldName: "KorName", dataType: "text" },
  { fieldName: "Gender", dataType: "text" },
  { fieldName: "Age", dataType: "number" },
  { fieldName: "Phone", dataType: "text" },
  { fieldName: "ProductId", dataType: "text" },
  { fieldName: "KorCountry", dataType: "text" },
  { fieldName: "OrderDate", dataType: "datetime", datetimeFormat: "yyyy-MM-dd", amText: "AM", pmText: "PM" },
  { fieldName: "CardNumber", dataType: "text" },
  { fieldName: "Monetary", dataType: "text" },
  { fieldName: "StartDate", dataType: "datetime", datetimeFormat: "yyyy-MM-dd", amText: "AM", pmText: "PM" },
  { fieldName: "EndDate", dataType: "datetime", datetimeFormat: "yyyy-MM-dd", amText: "AM", pmText: "PM" },
  { fieldName: "ToMonth", dataType: "number" },
  { fieldName: "Month", dataType: "number" },
  { fieldName: "Year", dataType: "number" },
  { fieldName: "InterestRate", dataType: "number" },
  { fieldName: "SaveCost", dataType: "number" },
  { fieldName: "SaveMaturity", dataType: "number" },
  { fieldName: "CurrentSave", dataType: "number" }
];

const columns = [
  { name: "KorCountry", fieldName: "KorCountry", width: "100", header: { text: "Investment Country" } },
  { name: "Monetary", fieldName: "Monetary", width: "50", header: { text: "Currency" } },
  { name: "KorName", fieldName: "KorName", width: "60", header: { text: "Name" }, checked: "true" },
  { name: "Gender", fieldName: "Gender", width: "40", header: { text: "Gender" } },
  { name: "Age", fieldName: "Age", width: "40", header: { text: "Age" }, styleName: "right-column" },
  { name: "StartDate", fieldName: "StartDate", width: "100", header: { text: "First Payment Date" } },
  { name: "EndDate", fieldName: "EndDate", width: "100", header: { text: "End Date" } },
  { name: "ToMonth", fieldName: "ToMonth", width: "50", header: { text: "Payment Count" }, styleName: "right-column" },
  { name: "Month", fieldName: "Month", width: "50", header: { text: "Remaining Count" }, styleName: "right-column" },
  { name: "InterestRate", fieldName: "InterestRate", width: "50", numberFormat: "0.00", header: { text: "Interest Rate" }, groupFooter: { text: "Subtotal: " }, footer: { text: "Total: " }, styleName: "right-column" },
  { name: "SaveCost", fieldName: "SaveCost", width: "80", numberFormat: "#,##0", header: { text: "Payment Amount" }, groupFooter: { numberFormat: "#,##0", expression: "sum" }, footer: { numberFormat: "#,##0", expression: "sum" }, styleName: "right-column" },
  { name: "SaveMaturity", fieldName: "SaveMaturity", width: "80", numberFormat: "#,##0", header: { text: "Maturity Amount" }, groupFooter: { numberFormat: "#,##0", expression: "sum" }, footer: { numberFormat: "#,##0", expression: "sum" }, styleName: "right-column" },
  { name: "CurrentSave", fieldName: "CurrentSave", width: "80", numberFormat: "#,##0", header: { text: "Current Balance" }, groupFooter: { numberFormat: "#,##0", expression: "sum" }, footer: { numberFormat: "#,##0", expression: "sum" }, styleName: "right-column" }
];

const { gridView, dataProvider } = RealGrid.createGrid("realgrid", {
  columns,
  display: { rowHeight: 26, emptyMessage: "No data to display." },
  edit: { insertable: true, appendable: true },
  header: { height: 40 },
  footer: { height: 40 },
  stateBar: { width: 16 },
  groupPanel: { visible: true },
  onInitialized: (grid) => {
    fetch('/data/en/simple_data2700.json')
      .then(response => response.json())
      .then(data => {
        grid.getDataSource().setRows(data);
      });
  }
}, {
  fields
});

window.gridView = gridView;
window.dataProvider = dataProvider;

function btnGroupPanelVisibleF() {
  gridView.groupPanel.visible = false;
}

function btnGroupPanelVisibleT() {
  gridView.groupPanel.visible = true;
}

function btnGroupBy() {
  gridView.groupBy(["Gender", "Monetary"]);
}
```

```js
gridView.groupPanel.visible = false;

gridView.groupPanel.visible = true;

gridView.groupBy(["Gender", "Monetary"]);
```

#### Display of group header and footer

When in the rowGrouping state, you can set the display method of the group footer and group header depending on the expanded or collapsed state of the group.

* expandedAdornments: Specifies whether to display the group header and footer when the row group is expanded.
* collapsedAdornments: Specifies whether to display the group header and footer when the row group is not expanded.

```js
const fields = [
  { fieldName: "KorName", dataType: "text" },
  { fieldName: "Gender", dataType: "text" },
  { fieldName: "Age", dataType: "number" },
  { fieldName: "Phone", dataType: "text" },
  { fieldName: "ProductId", dataType: "text" },
  { fieldName: "KorCountry", dataType: "text" },
  { fieldName: "OrderDate", dataType: "datetime", datetimeFormat: "yyyy-MM-dd", amText: "AM", pmText: "PM" },
  { fieldName: "CardNumber", dataType: "text" },
  { fieldName: "Monetary", dataType: "text" },
  { fieldName: "StartDate", dataType: "datetime", datetimeFormat: "yyyy-MM-dd", amText: "AM", pmText: "PM" },
  { fieldName: "EndDate", dataType: "datetime", datetimeFormat: "yyyy-MM-dd", amText: "AM", pmText: "PM" },
  { fieldName: "ToMonth", dataType: "number" },
  { fieldName: "Month", dataType: "number" },
  { fieldName: "Year", dataType: "number" },
  { fieldName: "InterestRate", dataType: "number" },
  { fieldName: "SaveCost", dataType: "number" },
  { fieldName: "SaveMaturity", dataType: "number" },
  { fieldName: "CurrentSave", dataType: "number" }
];

const columns = [
  { name: "KorCountry", fieldName: "KorCountry", width: "100", header: { text: "Investment Country" } },
  { name: "Monetary", fieldName: "Monetary", width: "50", header: { text: "Currency" } },
  { name: "KorName", fieldName: "KorName", width: "60", header: { text: "Name" }, checked: "true" },
  { name: "Gender", fieldName: "Gender", width: "40", header: { text: "Gender" } },
  { name: "Age", fieldName: "Age", width: "40", header: { text: "Age" }, styleName: "right-column" },
  { name: "StartDate", fieldName: "StartDate", width: "100", header: { text: "First Payment Date" } },
  { name: "EndDate", fieldName: "EndDate", width: "100", header: { text: "End Date" } },
  { name: "ToMonth", fieldName: "ToMonth", width: "50", header: { text: "Payment Count" }, styleName: "right-column" },
  { name: "Month", fieldName: "Month", width: "50", header: { text: "Remaining Count" }, styleName: "right-column" },
  { name: "InterestRate", fieldName: "InterestRate", width: "50", numberFormat: "0.00", header: { text: "Interest Rate" }, groupFooter: { text: "Subtotal: " }, footer: { text: "Total: " }, styleName: "right-column" },
  { name: "SaveCost", fieldName: "SaveCost", width: "80", numberFormat: "#,##0", header: { text: "Payment Amount" }, groupFooter: { numberFormat: "#,##0", expression: "sum" }, footer: { numberFormat: "#,##0", expression: "sum" }, styleName: "right-column" },
  { name: "SaveMaturity", fieldName: "SaveMaturity", width: "80", numberFormat: "#,##0", header: { text: "Maturity Amount" }, groupFooter: { numberFormat: "#,##0", expression: "sum" }, footer: { numberFormat: "#,##0", expression: "sum" }, styleName: "right-column" },
  { name: "CurrentSave", fieldName: "CurrentSave", width: "80", numberFormat: "#,##0", header: { text: "Current Balance" }, groupFooter: { numberFormat: "#,##0", expression: "sum" }, footer: { numberFormat: "#,##0", expression: "sum" }, styleName: "right-column" }
];

const { gridView, dataProvider } = RealGrid.createGrid("realgrid", {
  columns,
  display: { rowHeight: 36, emptyMessage: "No data to display." },
  edit: { insertable: true, appendable: true },
  header: { height: 40 },
  footer: { height: 40 },
  stateBar: { width: 16 },
  groupPanel: { visible: true },
  onInitialized: (grid) => {
    fetch('/data/en/rowGroupData.json')
      .then(response => response.json())
      .then(data => {
        grid.getDataSource().setRows(data);
        grid.groupBy(["KorCountry"]);
        grid.setRowGroup({ expandedAdornments: "header", collapsedAdornments: "footer" });
      });
  }
}, {
  fields
});

window.gridView = gridView;
window.dataProvider = dataProvider;

function expandedAdornmentsChange(val) {
  gridView.setRowGroup({ expandedAdornments: val });
}

function collapsedAdornmentsChange(val) {
  gridView.setRowGroup({ collapsedAdornments: val });
}

function expanderChange(val) {
  gridView.setRowGroup({ mergeExpanderVisibility: val });
}
```

```js
gridView.setRowGroup({
   expandedAdornments: $(':radio[name="expanded"]:checked').val()
   collapsedAdornments: $(':radio[name="collapsed"]:checked').val()
});
```

#### Grouping event

When row grouping occurs, the [onGrouping](/refs/Class/GridView#ongrouping) and [onGrouped](/refs/Class/GridView#ongrouped) events of the Javascript grid are triggered.

- [onGrouping](/refs/Class/GridView#ongrouping)
- [onGrouped](/refs/Class/GridView#ongrouped)

```js
const fields = [
  { fieldName: "KorName", dataType: "text" },
  { fieldName: "Gender", dataType: "text" },
  { fieldName: "Age", dataType: "number" },
  { fieldName: "Phone", dataType: "text" },
  { fieldName: "ProductId", dataType: "text" },
  { fieldName: "KorCountry", dataType: "text" },
  { fieldName: "OrderDate", dataType: "datetime", datetimeFormat: "yyyy-MM-dd", amText: "AM", pmText: "PM" },
  { fieldName: "CardNumber", dataType: "text" },
  { fieldName: "Monetary", dataType: "text" },
  { fieldName: "StartDate", dataType: "datetime", datetimeFormat: "yyyy-MM-dd", amText: "AM", pmText: "PM" },
  { fieldName: "EndDate", dataType: "datetime", datetimeFormat: "yyyy-MM-dd", amText: "AM", pmText: "PM" },
  { fieldName: "ToMonth", dataType: "number" },
  { fieldName: "Month", dataType: "number" },
  { fieldName: "Year", dataType: "number" },
  { fieldName: "InterestRate", dataType: "number" },
  { fieldName: "SaveCost", dataType: "number" },
  { fieldName: "SaveMaturity", dataType: "number" },
  { fieldName: "CurrentSave", dataType: "number" }
];

const columns = [
  { name: "KorName", fieldName: "KorName", width: "60", header: { text: "Name" } },
  { name: "Gender", fieldName: "Gender", width: "40", header: { text: "Gender" } },
  { name: "Age", fieldName: "Age", width: "40", header: { text: "Age" }, styleName: "right-column" },
  { name: "Phone", fieldName: "Phone", width: "100", header: { text: "Phone" } },
  { name: "ProductId", fieldName: "ProductId", width: "120", header: { text: "Product ID" } },
  { name: "KorCountry", fieldName: "KorCountry", width: "100", header: { text: "Investment Country" } },
  { name: "OrderDate", fieldName: "OrderDate", width: "100", header: { text: "Order Date" } },
  { name: "CardNumber", fieldName: "CardNumber", width: "140", header: { text: "Card Number" } },
  { name: "Monetary", fieldName: "Monetary", width: "40", header: { text: "Currency" } },
  { name: "StartDate", fieldName: "StartDate", width: "100", header: { text: "First Payment Date" } },
  { name: "EndDate", fieldName: "EndDate", width: "100", header: { text: "End Date" } },
  { name: "ToMonth", fieldName: "ToMonth", width: "50", header: { text: "Payment Count" }, styleName: "right-column" },
  { name: "Month", fieldName: "Month", width: "50", header: { text: "Remaining Count" }, styleName: "right-column" },
  { name: "InterestRate", fieldName: "InterestRate", width: "50", numberFormat: "0.00", header: { text: "Interest Rate" }, styleName: "right-column" },
  { name: "SaveCost", fieldName: "SaveCost", width: "80", numberFormat: "#,##0", header: { text: "Payment Amount" }, footer: { text: "Total =>" }, styleName: "right-column" },
  { name: "SaveMaturity", fieldName: "SaveMaturity", width: "80", numberFormat: "#,##0", header: { text: "Maturity Amount" }, footer: { text: "Total", expression: "sum", groupExpression: "sum" }, styleName: "right-column" },
  { name: "CurrentSave", fieldName: "CurrentSave", width: "80", numberFormat: "#,##0", header: { text: "Current Balance" }, styleName: "right-column" }
];

const { gridView, dataProvider } = RealGrid.createGrid("realgrid", {
  columns,
  display: { rowHeight: 36, emptyMessage: "No data to display." },
  edit: { insertable: true, appendable: true },
  header: { height: 40 },
  footer: { height: 40 },
  stateBar: { width: 16 },
  groupPanel: { visible: true },
  onInitialized: (grid) => {
    fetch('/data/en/simple_data20000.json')
      .then(response => response.json())
      .then(data => {
        grid.getDataSource().setRows(data);
      });
  }
}, {
  fields
});

window.gridView = gridView;
window.dataProvider = dataProvider;

gridView.onGrouping = function(grid) {
  alert("onGrouping event occurred. Returning true. If false is returned, ungrouping will not occur.");
  return true;
};

gridView.onGrouped = function(grid) {
  alert(
    "onGrouped : isGrouped = " +
      grid.isGrouped() +
      ", isMergedGrouped  = " +
      grid.isMergedGrouped()
  );
};
```

```js
gridView.onGrouping = function(grid) {
   alert("onGrouping event occurred. Returns true. If it returns false, grouping will not occur.");
   return true;
};

gridView.onGrouped = function(grid) {
   alert(
     "onGrouped : isGrouped = " +
       grid.isGrouped() +
       ", isMergedGrouped = " +
       grid.isMergedGrouped()
   );
};
```

#### Display summary values in footer and group footer

If dataType is "number", summary values can be displayed.
Check [SummaryType](/refs/realgrid#summarytype) for summary values that can be displayed.

In the demo below, the age column is calculated as the average value and the maturity amount is calculated as the sum.

```js
const fields = [
  { fieldName: "KorName", dataType: "text" },
  { fieldName: "Gender", dataType: "text" },
  { fieldName: "Age", dataType: "number" },
  { fieldName: "Phone", dataType: "text" },
  { fieldName: "ProductId", dataType: "text" },
  { fieldName: "KorCountry", dataType: "text" },
  { fieldName: "OrderDate", dataType: "datetime", datetimeFormat: "yyyy-MM-dd", amText: "AM", pmText: "PM" },
  { fieldName: "CardNumber", dataType: "text" },
  { fieldName: "Monetary", dataType: "text" },
  { fieldName: "StartDate", dataType: "datetime", datetimeFormat: "yyyy-MM-dd", amText: "AM", pmText: "PM" },
  { fieldName: "EndDate", dataType: "datetime", datetimeFormat: "yyyy-MM-dd", amText: "AM", pmText: "PM" },
  { fieldName: "ToMonth", dataType: "number" },
  { fieldName: "Month", dataType: "number" },
  { fieldName: "Year", dataType: "number" },
  { fieldName: "InterestRate", dataType: "number" },
  { fieldName: "SaveCost", dataType: "number" },
  { fieldName: "SaveMaturity", dataType: "number" },
  { fieldName: "CurrentSave", dataType: "number" }
];

const columns = [
  { name: "KorName", fieldName: "KorName", width: "70", header: { text: "Name" } },
  { name: "Gender", fieldName: "Gender", width: "50", header: { text: "Gender" } },
  { name: "Age", fieldName: "Age", width: "40", header: { text: "Age", styleName: "orange-column" }, groupFooter: { expression: "avg", numberFormat: "#,##0.0", styleName: "right-column" }, footer: { expression: "avg", numberFormat: "#,##0.0", styleName: "right-column" }, styleName: "right-column" },
  { name: "SaveMaturity", fieldName: "SaveMaturity", width: "80", numberFormat: "#,##0", header: { text: "Maturity Amount", styleName: "orange-column" }, groupFooter: { expression: "sum", numberFormat: "#,##0", styleName: "right-column" }, footer: { expression: "sum", numberFormat: "#,##0", styleName: "right-column" }, styleName: "right-column" },
  { name: "Phone", fieldName: "Phone", width: "100", header: { text: "Phone" } },
  { name: "ProductId", fieldName: "ProductId", width: "120", header: { text: "Product ID" } },
  { name: "KorCountry", fieldName: "KorCountry", width: "100", header: { text: "Investment Country" } },
  { name: "OrderDate", fieldName: "OrderDate", width: "100", header: { text: "Order Date" } },
  { name: "CardNumber", fieldName: "CardNumber", width: "140", header: { text: "Card Number" } },
  { name: "Monetary", fieldName: "Monetary", width: "40", header: { text: "Currency" } },
  { name: "StartDate", fieldName: "StartDate", width: "100", header: { text: "First Payment Date" } },
  { name: "EndDate", fieldName: "EndDate", width: "100", header: { text: "End Date" } },
  { name: "ToMonth", fieldName: "ToMonth", width: "50", header: { text: "Payment Count" }, styleName: "right-column" },
  { name: "Month", fieldName: "Month", width: "50", header: { text: "Remaining Count" }, styleName: "right-column" },
  { name: "InterestRate", fieldName: "InterestRate", width: "50", numberFormat: "0.00", header: { text: "Interest Rate" }, styleName: "right-column" },
  { name: "SaveCost", fieldName: "SaveCost", width: "80", numberFormat: "#,##0", header: { text: "Payment Amount" }, styleName: "right-column" },
  { name: "CurrentSave", fieldName: "CurrentSave", width: "80", numberFormat: "#,##0", header: { text: "Current Balance" }, styleName: "right-column" }
];

const { gridView, dataProvider } = RealGrid.createGrid("realgrid", {
  columns,
  display: { rowHeight: 36, emptyMessage: "No data to display." },
  edit: { insertable: true, appendable: true },
  header: { height: 40 },
  footer: { height: 40 },
  stateBar: { width: 16 },
  groupPanel: { visible: true },
  onInitialized: (grid) => {
    fetch('/data/en/simple_data200.json')
      .then(response => response.json())
      .then(data => {
        grid.getDataSource().setRows(data);
        grid.groupBy(["Gender", "Monetary"]);
      });
  }
}, {
  fields
});

window.gridView = gridView;
window.dataProvider = dataProvider;
```

```js
var columns = [
   ...
   {
     name: "Age",
     fieldName: "Age",
     width: "40",
     header: {
       text: "Age"
     },
     groupFooter: {
       expression: "avg",
       numberFormat: "#,##0.0",
       styleName: "right-column"
     },
     footer: {
       expression: "avg",
       numberFormat: "#,##0.0",
       styleName: "right-column"
     },
     styleName: "right-column"
   },
   {
     name: "SaveMaturity",
     fieldName: "SaveMaturity",
     width: "80",
     numberFormat: "#,##0",
     header: {
       text: "Maturity amount"
     },
     groupFooter: {
       expression: "sum",
       numberFormat: "#,##0",
       styleName: "right-column"
     },
     footer: {
       expression: "sum",
       numberFormat: "#,##0",
       styleName: "right-column"
     },
     styleName: "right-column"
   },
   ...
];

gridView.setColumns(columns);
```

#### Dynamic calculation of group footer

If the developer wants to calculate and display the summary displayed in the group footer, set it to column.groupFooter.valueCallback.
(Proceed by returning the value you want to display.)

The fourth argument, group, contains information about the group. (ex: children contains information about the children of the group.)

```js
const fields = [
  { fieldName: "KorName", dataType: "text" },
  { fieldName: "Gender", dataType: "text" },
  { fieldName: "Age", dataType: "number" },
  { fieldName: "Phone", dataType: "text" },
  { fieldName: "ProductId", dataType: "text" },
  { fieldName: "KorCountry", dataType: "text" },
  { fieldName: "OrderDate", dataType: "datetime", datetimeFormat: "yyyy-MM-dd", amText: "AM", pmText: "PM" },
  { fieldName: "CardNumber", dataType: "text" },
  { fieldName: "Monetary", dataType: "text" },
  { fieldName: "StartDate", dataType: "datetime", datetimeFormat: "yyyy-MM-dd", amText: "AM", pmText: "PM" },
  { fieldName: "EndDate", dataType: "datetime", datetimeFormat: "yyyy-MM-dd", amText: "AM", pmText: "PM" },
  { fieldName: "ToMonth", dataType: "number" },
  { fieldName: "Month", dataType: "number" },
  { fieldName: "Year", dataType: "number" },
  { fieldName: "InterestRate", dataType: "number" },
  { fieldName: "SaveCost", dataType: "number" },
  { fieldName: "SaveMaturity", dataType: "number" },
  { fieldName: "CurrentSave", dataType: "number" }
];

const columns = [
  { name: "KorName", fieldName: "KorName", width: "70", header: { text: "Name" } },
  { name: "Gender", fieldName: "Gender", width: "50", header: { text: "Gender" } },
  { name: "Age", fieldName: "Age", width: "40", header: { text: "Age", styleName: "orange-column" }, groupFooter: { valueCallback: function (grid, column, groupFooterIndex, group, value) { return grid.getGroupSummary(group, "Age").count + " items"; }, styleName: "orange-column" }, styleName: "right-column" },
  { name: "SaveMaturity", fieldName: "SaveMaturity", width: "80", numberFormat: "#,##0", header: { text: "Maturity Amount" }, groupFooter: { expression: "sum", numberFormat: "#,##0" }, styleName: "right-column" },
  { name: "Phone", fieldName: "Phone", width: "100", header: { text: "Phone" } },
  { name: "ProductId", fieldName: "ProductId", width: "120", header: { text: "Product ID" } },
  { name: "KorCountry", fieldName: "KorCountry", width: "100", header: { text: "Investment Country" } },
  { name: "OrderDate", fieldName: "OrderDate", width: "100", header: { text: "Order Date" } },
  { name: "CardNumber", fieldName: "CardNumber", width: "140", header: { text: "Card Number" } },
  { name: "Monetary", fieldName: "Monetary", width: "40", header: { text: "Currency" } },
  { name: "StartDate", fieldName: "StartDate", width: "100", header: { text: "First Payment Date" } },
  { name: "EndDate", fieldName: "EndDate", width: "100", header: { text: "End Date" } },
  { name: "ToMonth", fieldName: "ToMonth", width: "50", header: { text: "Payment Count" }, styleName: "right-column" },
  { name: "Month", fieldName: "Month", width: "50", header: { text: "Remaining Count" }, styleName: "right-column" },
  { name: "InterestRate", fieldName: "InterestRate", width: "50", numberFormat: "0.00", header: { text: "Interest Rate" }, styleName: "right-column" },
  { name: "SaveCost", fieldName: "SaveCost", width: "80", numberFormat: "#,##0", header: { text: "Payment Amount" }, styleName: "right-column" },
  { name: "CurrentSave", fieldName: "CurrentSave", width: "80", numberFormat: "#,##0", header: { text: "Current Balance" }, styleName: "right-column" }
];

const { gridView, dataProvider } = RealGrid.createGrid("realgrid", {
  columns,
  display: { rowHeight: 36, emptyMessage: "No data to display." },
  edit: { insertable: true, appendable: true },
  header: { height: 40 },
  footer: { height: 40 },
  stateBar: { width: 16 },
  groupPanel: { visible: true },
  onInitialized: (grid) => {
    fetch('/data/en/simple_data200.json')
      .then(response => response.json())
      .then(data => {
        grid.getDataSource().setRows(data);
        grid.groupBy(["Monetary", "Gender"]);
      });
  }
}, {
  fields
});

window.gridView = gridView;
window.dataProvider = dataProvider;
```

```js
var columns = [
   ...
   {
     name: "Age",
     fieldName: "Age",
     width: "40",
     header: {
       text: "Age",
       styleName: "orange-column"
     },
     groupFooter: {
       valueCallback: function (grid, column, groupFooterIndex, group, value) {
         //Return the value you want to display after calculation
         return grid.getGroupSummary(group, "Age").count + 'gun';
       },
       styleName: "right-column"
     },
     styleName: "right-column"
   },
   ...
];

gridView.setColumns(columns);
```

#### Dynamic calculation of group footer 2

Below is an example where only the sum of the ages of the values with the gender of "male" in each group is output.

```js
const fields = [
  { fieldName: "KorName", dataType: "text" },
  { fieldName: "Gender", dataType: "text" },
  { fieldName: "Age", dataType: "number" },
  { fieldName: "Phone", dataType: "text" },
  { fieldName: "ProductId", dataType: "text" },
  { fieldName: "KorCountry", dataType: "text" },
  { fieldName: "OrderDate", dataType: "datetime", datetimeFormat: "yyyy-MM-dd", amText: "AM", pmText: "PM" },
  { fieldName: "CardNumber", dataType: "text" },
  { fieldName: "Monetary", dataType: "text" },
  { fieldName: "StartDate", dataType: "datetime", datetimeFormat: "yyyy-MM-dd", amText: "AM", pmText: "PM" },
  { fieldName: "EndDate", dataType: "datetime", datetimeFormat: "yyyy-MM-dd", amText: "AM", pmText: "PM" },
  { fieldName: "ToMonth", dataType: "number" },
  { fieldName: "Month", dataType: "number" },
  { fieldName: "Year", dataType: "number" },
  { fieldName: "InterestRate", dataType: "number" },
  { fieldName: "SaveCost", dataType: "number" },
  { fieldName: "SaveMaturity", dataType: "number" },
  { fieldName: "CurrentSave", dataType: "number" }
];

const columns = [
  { name: "KorName", fieldName: "KorName", width: "70", header: { text: "Name" } },
  { name: "Gender", fieldName: "Gender", width: "50", header: { text: "Gender" } },
  { name: "Age", fieldName: "Age", width: "40", header: { text: "Age", styleName: "orange-column" }, groupFooter: { valueCallback: function (grid, column, groupFooterIndex, group, value) { let tot_cnt = 0; group.rows.forEach(function (row) { if (row != -1) { if (dataProvider.getValue(row, "Gender") === "M") { tot_cnt += dataProvider.getValue(row, column.fieldName); } } }); return tot_cnt; }, styleName: "orange-column" }, styleName: "right-column" },
  { name: "SaveMaturity", fieldName: "SaveMaturity", width: "80", numberFormat: "#,##0", header: { text: "Maturity Amount" }, groupFooter: { expression: "sum", numberFormat: "#,##0" }, styleName: "right-column" },
  { name: "Phone", fieldName: "Phone", width: "100", header: { text: "Phone" } },
  { name: "ProductId", fieldName: "ProductId", width: "120", header: { text: "Product ID" } },
  { name: "KorCountry", fieldName: "KorCountry", width: "100", header: { text: "Investment Country" } },
  { name: "OrderDate", fieldName: "OrderDate", width: "100", header: { text: "Order Date" } },
  { name: "CardNumber", fieldName: "CardNumber", width: "140", header: { text: "Card Number" } },
  { name: "Monetary", fieldName: "Monetary", width: "40", header: { text: "Currency" } },
  { name: "StartDate", fieldName: "StartDate", width: "100", header: { text: "First Payment Date" } },
  { name: "EndDate", fieldName: "EndDate", width: "100", header: { text: "End Date" } },
  { name: "ToMonth", fieldName: "ToMonth", width: "50", header: { text: "Payment Count" }, styleName: "right-column" },
  { name: "Month", fieldName: "Month", width: "50", header: { text: "Remaining Count" }, styleName: "right-column" },
  { name: "InterestRate", fieldName: "InterestRate", width: "50", numberFormat: "0.00", header: { text: "Interest Rate" }, styleName: "right-column" },
  { name: "SaveCost", fieldName: "SaveCost", width: "80", numberFormat: "#,##0", header: { text: "Payment Amount" }, styleName: "right-column" },
  { name: "CurrentSave", fieldName: "CurrentSave", width: "80", numberFormat: "#,##0", header: { text: "Current Balance" }, styleName: "right-column" }
];

const { gridView, dataProvider } = RealGrid.createGrid("realgrid", {
  columns,
  display: { rowHeight: 36, emptyMessage: "No data to display." },
  edit: { insertable: true, appendable: true },
  header: { height: 40 },
  footer: { height: 40 },
  stateBar: { width: 16 },
  groupPanel: { visible: true },
  onInitialized: (grid) => {
    fetch('/data/en/simple_data200.json')
      .then(response => response.json())
      .then(data => {
        grid.getDataSource().setRows(data);
        grid.groupBy(["Monetary", "Gender"]);
      });
  }
}, {
  fields
});

window.gridView = gridView;
window.dataProvider = dataProvider;
```

```js
var columns = [
   ...
   {
     name: "Age",
     fieldName: "Age",
     width: "40",
     header: {
         text: "Age",
         styleName: "orange-column",
     },
     groupFooter: {
         valueCallback: function (grid, column, groupFooterIndex, group, value) {
             let tot_cnt = 0;
             console.log(group)
             group.rows.forEach(function (row) {
                 if (row != -1) {
                     if (dataProvider.getValue(row, "Gender") === "Male") {
                         tot_cnt += dataProvider.getValue(row, column.fieldName);
                     }
                 }
             });
             return tot_cnt;
         },
         styleName: "orange-column",
     },
     styleName: "right-column",
   },
   ...
];

gridView.setColumns(columns);
```

#### Display Group Footer in multiple lines

To display the Group Footer in two lines, set it with setFooters as shown below.

```js
//display in two lines
gridView.rowGroup.setFooters([{},{}])

//To display in three lines, set it to [{},{},{}].
```

Individual configuration codes can be processed in column.groupFooters.

The demo below displays sum in the first group footer and avg in the second group footer.

```js
const vc = function (grid, column, groupFooterIndex, group, value) {
  if (groupFooterIndex == 0) {
    return grid.getGroupSummary(group, "Age").sum;
  } else if (groupFooterIndex == 1) {
    return grid.getGroupSummary(group, "Age").avg;
  } 
};

const fields = [
  { fieldName: "KorName", dataType: "text" },
  { fieldName: "Gender", dataType: "text" },
  { fieldName: "Age", dataType: "number" },
  { fieldName: "Phone", dataType: "text" },
  { fieldName: "ProductId", dataType: "text" },
  { fieldName: "KorCountry", dataType: "text" },
  { fieldName: "OrderDate", dataType: "datetime", datetimeFormat: "yyyy-MM-dd", amText: "AM", pmText: "PM" },
  { fieldName: "CardNumber", dataType: "text" },
  { fieldName: "Monetary", dataType: "text" },
  { fieldName: "StartDate", dataType: "datetime", datetimeFormat: "yyyy-MM-dd", amText: "AM", pmText: "PM" },
  { fieldName: "EndDate", dataType: "datetime", datetimeFormat: "yyyy-MM-dd", amText: "AM", pmText: "PM" },
  { fieldName: "ToMonth", dataType: "number" },
  { fieldName: "Month", dataType: "number" },
  { fieldName: "Year", dataType: "number" },
  { fieldName: "InterestRate", dataType: "number" },
  { fieldName: "SaveCost", dataType: "number" },
  { fieldName: "SaveMaturity", dataType: "number" },
  { fieldName: "CurrentSave", dataType: "number" }
];

const columns = [
  { name: "KorName", fieldName: "KorName", width: "70", header: { text: "Name" } },
  { name: "Gender", fieldName: "Gender", width: "50", header: { text: "Gender", styleName: "orange-column" }, groupFooters: [{ text: "Total: ", styleName: "orange-column" }, { text: "Average: ", styleName: "orange-column" }] },
  { name: "Age", fieldName: "Age", width: "40", header: { text: "Age", styleName: "orange-column" }, groupFooters: [{ valueCallback: vc, numberFormat: "#,##0.#", styleName: "orange-column" }, { valueCallback: vc, numberFormat: "#,##0.#", styleName: "orange-column" }], styleName: "right-column" },
  { name: "SaveMaturity", fieldName: "SaveMaturity", width: "80", numberFormat: "#,##0", header: { text: "Maturity Amount" }, styleName: "right-column" },
  { name: "Phone", fieldName: "Phone", width: "100", header: { text: "Phone" } },
  { name: "ProductId", fieldName: "ProductId", width: "120", header: { text: "Product ID" } },
  { name: "KorCountry", fieldName: "KorCountry", width: "100", header: { text: "Investment Country" } },
  { name: "OrderDate", fieldName: "OrderDate", width: "100", header: { text: "Order Date" } },
  { name: "CardNumber", fieldName: "CardNumber", width: "140", header: { text: "Card Number" } },
  { name: "Monetary", fieldName: "Monetary", width: "40", header: { text: "Currency" } },
  { name: "StartDate", fieldName: "StartDate", width: "100", header: { text: "First Payment Date" } },
  { name: "EndDate", fieldName: "EndDate", width: "100", header: { text: "End Date" } },
  { name: "ToMonth", fieldName: "ToMonth", width: "50", header: { text: "Payment Count" }, styleName: "right-column" },
  { name: "Month", fieldName: "Month", width: "50", header: { text: "Remaining Count" }, styleName: "right-column" },
  { name: "InterestRate", fieldName: "InterestRate", width: "50", numberFormat: "0.00", header: { text: "Interest Rate" }, styleName: "right-column" },
  { name: "SaveCost", fieldName: "SaveCost", width: "80", numberFormat: "#,##0", header: { text: "Payment Amount" }, styleName: "right-column" },
  { name: "CurrentSave", fieldName: "CurrentSave", width: "80", numberFormat: "#,##0", header: { text: "Current Balance" }, styleName: "right-column" }
];

const { gridView, dataProvider } = RealGrid.createGrid("realgrid", {
  columns,
  display: { rowHeight: 36, emptyMessage: "No data to display." },
  edit: { insertable: true, appendable: true },
  header: { height: 40 },
  footer: { height: 40 },
  stateBar: { width: 16 },
  groupPanel: { visible: true },
  onInitialized: (grid) => {
    fetch('/data/en/simple_data200.json')
      .then(response => response.json())
      .then(data => {
        grid.getDataSource().setRows(data);
        grid.rowGroup.setFooters([{}, {}]);
        grid.groupBy(["Monetary"]);
      });
  }
}, {
  fields
});

window.gridView = gridView;
window.dataProvider = dataProvider;
```

```js
gridView.rowGroup.setFooters([{},{}])

var vc = function (grid, column, groupFooterIndex, group, value) {
   //Display sum in the first group footer and avg in the second group footer.
   if (groupFooterIndex == 0) {
     return grid.getGroupSummary(group, "Age").sum;
   } else if (groupFooterIndex == 1) {
     return grid.getGroupSummary(group, "Age").avg;
   }
};

var columns = [
   ...
     {
     name: "Gender",
     fieldName: "Gender",
     width: "50",
     header: {
       text: "Gender",
       styleName: "orange-column"
     },
     groupFooters: [{
       text: "Total: ",
       styleName: "orange-column"
     }, {
       text: "Average: ",
       styleName: "orange-column"
     }],
   },
   {
     name: "Age",
     fieldName: "Age",
     width: "40",
     header: {
       text: "Age",
       styleName: "orange-column"
     },
     groupFooters: [{
       valueCallback: vc,
       numberFormat: "#,##0.#",
       styleName: "orange-column"
     }, {
       valueCallback: vc;
       numberFormat: "#,##0.#",
       styleName: "orange-column"
     }],
     styleName: "right-column"
   },
   ...
];

gridView.setColumns(columns);
```

#### Display group footer values

Depending on the settings of the options below, each row group can display a ‘group footer’.
The text displayed in the group footer is specified by the `footerStatement` value of the settings object passed to the GridView.[setRowGroup](/refs/Class/GridView#setrowgroup) function.
You can also specify the types of values that can be displayed in the footer of each row group through [SummaryMode](/refs/realgrid#summarymode#summary).

```js
gridView.setRowGroup({
   footerStatement: "${groupValue}"
});
```

#### Show group header values

Depending on the settings of the options below, each row group can display a ‘group header’.
The text displayed in the group header is specified by the `headerStatement` value of the settings object passed to the GridView.[setRowGroup](/refs/Class/GridView#setrowgroup) function.
The default value of `headerStatement` is `"${groupField}: ${groupValue} - ${rowCount} rows"`.

You can set the following values as the headerStatement property.

- groupField: Displays the field name of the grouped field.
- fieldHeader: Displays the header set in the grouped field.
- groupColumn: Displays the header of the grouped column.
- columnHeader: Displays the header of the grouped column.
- columnFooter: Displays the footer value of the grouped column.
- groupValue: Displays the value for each group.
- rowCount: Displays the number of rows for each group.

```js
gridView.setRowGroup({
   headerStatement: "${groupField}: ${groupValue} - ${rowCount} rows"
});
```


#### rowGroup.Bar visibility setting

You can set whether or not the rowGroup.Bar area is visible by setting rowGroup.indentVisible.
If indentVisible is false, the expand icon disappears, so you can't collapse and expand it.

```js
const fields = [
  { fieldName: "KorName", dataType: "text" },
  { fieldName: "Gender", dataType: "text" },
  { fieldName: "Age", dataType: "number" },
  { fieldName: "Phone", dataType: "text" },
  { fieldName: "ProductId", dataType: "text" },
  { fieldName: "KorCountry", dataType: "text" },
  { fieldName: "OrderDate", dataType: "datetime", datetimeFormat: "yyyy-MM-dd", amText: "AM", pmText: "PM" },
  { fieldName: "CardNumber", dataType: "text" },
  { fieldName: "Monetary", dataType: "text" },
  { fieldName: "StartDate", dataType: "datetime", datetimeFormat: "yyyy-MM-dd", amText: "AM", pmText: "PM" },
  { fieldName: "EndDate", dataType: "datetime", datetimeFormat: "yyyy-MM-dd", amText: "AM", pmText: "PM" },
  { fieldName: "ToMonth", dataType: "number" },
  { fieldName: "Month", dataType: "number" },
  { fieldName: "Year", dataType: "number" },
  { fieldName: "InterestRate", dataType: "number" },
  { fieldName: "SaveCost", dataType: "number" },
  { fieldName: "SaveMaturity", dataType: "number" },
  { fieldName: "CurrentSave", dataType: "number" }
];

const columns = [
  { name: "KorName", fieldName: "KorName", width: "70", header: { text: "Name" } },
  { name: "Gender", fieldName: "Gender", width: "50", header: { text: "Gender" } },
  { name: "Age", fieldName: "Age", width: "40", header: { text: "Age" }, styleName: "right-column" },
  { name: "Phone", fieldName: "Phone", width: "100", header: { text: "Phone" } },
  { name: "ProductId", fieldName: "ProductId", width: "120", header: { text: "Product ID" } },
  { name: "KorCountry", fieldName: "KorCountry", width: "100", header: { text: "Investment Country" } },
  { name: "OrderDate", fieldName: "OrderDate", width: "100", header: { text: "Order Date" } },
  { name: "CardNumber", fieldName: "CardNumber", width: "140", header: { text: "Card Number" } },
  { name: "Monetary", fieldName: "Monetary", width: "40", header: { text: "Currency" } },
  { name: "StartDate", fieldName: "StartDate", width: "100", header: { text: "First Payment Date" } },
  { name: "EndDate", fieldName: "EndDate", width: "100", header: { text: "End Date" } },
  { name: "ToMonth", fieldName: "ToMonth", width: "50", header: { text: "Payment Count" }, styleName: "right-column" },
  { name: "Month", fieldName: "Month", width: "50", header: { text: "Remaining Count" }, styleName: "right-column" },
  { name: "InterestRate", fieldName: "InterestRate", width: "50", numberFormat: "0.00", header: { text: "Interest Rate" }, styleName: "right-column" },
  { name: "SaveCost", fieldName: "SaveCost", width: "80", numberFormat: "#,##0", header: { text: "Payment Amount" }, footer: { text: "Total =>" }, styleName: "right-column" },
  { name: "SaveMaturity", fieldName: "SaveMaturity", width: "80", numberFormat: "#,##0", header: { text: "Maturity Amount" }, footer: { text: "Total", expression: "sum", groupExpression: "sum" }, styleName: "right-column" },
  { name: "CurrentSave", fieldName: "CurrentSave", width: "80", numberFormat: "#,##0", header: { text: "Current Balance" }, styleName: "right-column" }
];

const { gridView, dataProvider } = RealGrid.createGrid("realgrid", {
  columns,
  display: { rowHeight: 26, emptyMessage: "No data to display." },
  edit: { insertable: true, appendable: true },
  header: { height: 40 },
  footer: { height: 40 },
  stateBar: { width: 16 },
  groupPanel: { visible: true },
  onInitialized: (grid) => {
    fetch('/data/en/simple_data200.json')
      .then(response => response.json())
      .then(data => {
        grid.getDataSource().setRows(data);
        grid.groupBy(["Gender", "KorCountry"]);
        grid.rowGroup.indentVisible = false;
        grid.rowGroup.expandedAdornments = "footer";
      });
  }
}, {
  fields
});

window.gridView = gridView;
window.dataProvider = dataProvider;
```

```js
gridView.rowGroup.indentVisible = false;
```

#### Group rows by date

You can use the valueForGroupCallback property of a row group to apply row grouping based on a specific date unit.
※ Not applicable to row merge grouping (mergeMode: true).

```js
const fields = [
  { fieldName: "KorName", dataType: "text" },
  { fieldName: "Gender", dataType: "text" },
  { fieldName: "Age", dataType: "number" },
  { fieldName: "Phone", dataType: "text" },
  { fieldName: "ProductId", dataType: "text" },
  { fieldName: "KorCountry", dataType: "text" },
  { fieldName: "OrderDate", dataType: "datetime", datetimeFormat: "yyyy-MM-dd", amText: "AM", pmText: "PM" },
  { fieldName: "CardNumber", dataType: "text" },
  { fieldName: "Monetary", dataType: "text" },
  { fieldName: "StartDate", dataType: "datetime", datetimeFormat: "yyyy-MM-dd", amText: "AM", pmText: "PM" },
  { fieldName: "EndDate", dataType: "datetime", datetimeFormat: "yyyy-MM-dd", amText: "AM", pmText: "PM" },
  { fieldName: "ToMonth", dataType: "number" },
  { fieldName: "Month", dataType: "number" },
  { fieldName: "Year", dataType: "number" },
  { fieldName: "InterestRate", dataType: "number" },
  { fieldName: "SaveCost", dataType: "number" },
  { fieldName: "SaveMaturity", dataType: "number" },
  { fieldName: "CurrentSave", dataType: "number" }
];

const columns = [
  { name: "KorCountry", fieldName: "KorCountry", width: "100", header: { text: "Investment Country" } },
  { name: "Monetary", fieldName: "Monetary", width: "50", header: { text: "Currency" } },
  { name: "KorName", fieldName: "KorName", width: "60", header: { text: "Name" }, checked: "true" },
  { name: "Gender", fieldName: "Gender", width: "40", header: { text: "Gender" } },
  { name: "Age", fieldName: "Age", width: "40", header: { text: "Age" }, styleName: "right-column" },
  { name: "StartDate", fieldName: "StartDate", width: "100", header: { text: "First Payment Date" } },
  { name: "EndDate", fieldName: "EndDate", width: "100", header: { text: "End Date" } },
  { name: "ToMonth", fieldName: "ToMonth", width: "50", header: { text: "Payment Count" }, styleName: "right-column" },
  { name: "Month", fieldName: "Month", width: "50", header: { text: "Remaining Count" }, styleName: "right-column" },
  { name: "InterestRate", fieldName: "InterestRate", width: "50", numberFormat: "0.00", header: { text: "Interest Rate" }, groupFooter: { text: "Subtotal: " }, footer: { text: "Total: " }, styleName: "right-column" },
  { name: "SaveCost", fieldName: "SaveCost", width: "80", numberFormat: "#,##0", header: { text: "Payment Amount" }, groupFooter: { numberFormat: "#,##0", expression: "sum" }, footer: { numberFormat: "#,##0", expression: "sum" }, styleName: "right-column" },
  { name: "SaveMaturity", fieldName: "SaveMaturity", width: "80", numberFormat: "#,##0", header: { text: "Maturity Amount" }, groupFooter: { numberFormat: "#,##0", expression: "sum" }, footer: { numberFormat: "#,##0", expression: "sum" }, styleName: "right-column" },
  { name: "CurrentSave", fieldName: "CurrentSave", width: "80", numberFormat: "#,##0", header: { text: "Current Balance" }, groupFooter: { numberFormat: "#,##0", expression: "sum" }, footer: { numberFormat: "#,##0", expression: "sum" }, styleName: "right-column" }
];

const { gridView, dataProvider } = RealGrid.createGrid("realgrid", {
  columns,
  display: { rowHeight: 26, emptyMessage: "No data to display." },
  edit: { insertable: true, appendable: true },
  header: { height: 40 },
  footer: { height: 40 },
  stateBar: { width: 16 },
  groupPanel: { visible: true },
  onInitialized: (grid) => {
    fetch('/data/en/simple_data200.json')
      .then(response => response.json())
      .then(data => {
        grid.getDataSource().setRows(data);
        grid.groupBy(["StartDate"]);
        
        grid.rowGroup.valueForGroupCallback = function(gv, dataRow, fieldName, value) {
          if (gv.getDataSource().getOrgFieldName(dataProvider.getFieldIndex(fieldName)) === "StartDate" && value) {
            return new Date(2000, value.getMonth());
          }
          return value;
        };

        grid.columnByName("StartDate").groupFooter.valueCallback = function(gv, column, footerIndex, group) { 
          var row = group.firstDataItem.dataRow; 
          var val = gv.getDataSource().getValue(row, column.fieldName); 
          return val.getFullYear() + " - " + (val.getMonth()+1)
        };

        grid.rowGroup.indentVisible = false;
        grid.rowGroup.expandedAdornments = "footer";
      });
  }
}, {
  fields
});

window.gridView = gridView;
window.dataProvider = dataProvider;
```

```js
gridView.rowGroup.valueForGroupCallback = function(grid, dataRow, fieldName, value) {

     if (grid.getDataSource().getOrgFieldName(dataProvider.getFieldIndex(fieldName)) === "StartDate" && value) {
         return new Date(2000, value.getMonth());
     }
     return value;
   }

   gridView.columnByName("StartDate").groupFooter.valueCallback = function(grid, column, footerIndex, group) {
     var row = group.firstDataItem.dataRow;
     var val = grid.getDataSource().getValue(row, column.fieldName);
     return val.getFullYear() + " - " + (val.getMonth()+1) + " Total"
   };

   // Additional things
   gridView.rowGroup.indentVisible = false;
   gridView.rowGroup.expandedAdornments = "footer";
```