
# MergeCallback

When `data` changes, the grid generates merge information again, but the user can update the merge information directly based on external values.

Below is an example of selecting the area you want to merge and merging it.  
Select the area you want to merge by dragging and click the `Merge` button to merge it.

```js
var mergeInfo = {};
var mergeCnt = 0;

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,
  header: {
    height: 40
  },
  display: {
    rowHeight: 36,
    emptyMessage: 'No data to display.'
  },
  edit: {
    insertable: true,
    appendable: true
  },
  footer: {
    height: 40
  },
  stateBar: {
    width: 16
  },
  onInitialized: (grid) => {
    fetch('/data/en/simple_data.json')
      .then(response => response.json())
      .then(data => {
        const limitedData = data.slice(0, 8);
        grid.getDataSource().setRows(limitedData);
      });
  }
}, {
  fields
});

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

window.btnMerge = function() {
  const selection = gridView.getSelection();
  const startItem = selection.startItem;
  const endItem = selection.endItem;

  if (startItem === endItem) {
    return;
  }
  mergeCnt++;

  const columnName = selection.startColumn;

  if (!mergeInfo[columnName]) {
    mergeInfo[columnName] = {};
  }

  for (let i = startItem; i <= endItem; i++) {
    const dataRow = gridView.getDataRow(i);
    const dataRowId = gridView.getDataSource().getDataRowId(dataRow);
    mergeInfo[columnName][dataRowId] = mergeCnt;
  }

  const mergeCallback = (grid, column, itemIndex, value) => {
    const dataRow = grid.getDataRow(itemIndex);
    const dataRowId = grid.getDataSource().getDataRowId(dataRow);
    return mergeInfo[column.name] && mergeInfo[column.name][dataRowId];
  };

  gridView.columnByName(gridView.getCurrent().column).mergeCallback = mergeCallback;
  gridView.columnByName(gridView.getCurrent().column).breakMergeOnEmpty = true;
};
```

```js
function btnMerge() {
  const selection = gridView.getSelection();
  const startItem = selection.startItem;
  const endItem = selection.endItem;

  if (startItem === endItem) {
    return;
  }
  mergeCnt++;

  const columnName = selection.startColumn;

  if (!mergeInfo[columnName]) {
    mergeInfo[columnName] = {};
  }

  for (let i = startItem; i <= endItem; i++) {
    const dataRowId = gridView.getDataSource().getDataRowId(i);
    mergeInfo[columnName][dataRowId] = mergeCnt;
  }

  const mergeCallback = (grid, column, dataRow, value) => {
    const dataRowId = grid.getDataSource().getDataRowId(dataRow);
    return mergeInfo[column.name] && mergeInfo[column.name][dataRowId];
  };

  gridView.columnByName(gridView.getCurrent().column).mergeCallback = mergeCallback;
  gridView.columnByName(gridView.getCurrent().column).breakMergeOnEmpty = true;
}
```