
# Tree - Calculate subnodes

The calculated value of the tree child node can be applied to be output to the upper parent node.

```js
const sumFields = ["calc1", "calc2"];

const fields = [
  { fieldName: "icon", dataType: "number" },
  { fieldName: "col0", dataType: "text" },
  { fieldName: "col1", dataType: "number" },
  { fieldName: "col2", dataType: "number" },
  { fieldName: "col3", dataType: "number" },
  { fieldName: "col4", dataType: "number" },
  { fieldName: "col5", dataType: "number" },
  { fieldName: "col6", dataType: "number" },
  { fieldName: "col7", dataType: "number" },
  { fieldName: "calc1", dataType: "number" },
  { fieldName: "calc2", dataType: "number" }
];

const columns = [
  { name: "col0", fieldName: "col0", type: "data", width: "150", header: { text: "Type" } },
  { 
    name: "calc1", 
    fieldName: "calc1", 
    type: "data", 
    width: "120", 
    numberFormat: "#,##0", 
    header: { text: "Calc Column 1", styleName: "orange-color" },
    footer: { numberFormat: "#,##0" }
  },
  { 
    name: "calc2", 
    fieldName: "calc2", 
    type: "data", 
    width: "120", 
    numberFormat: "#,##0", 
    header: { text: "Calc Column 2", styleName: "orange-color" },
    footer: { numberFormat: "#,##0" }
  },
  { name: "col1", fieldName: "col1", type: "data", width: "80", numberFormat: "#,##0", header: { text: "Agriculture/Forestry/Fishery" } },
  { name: "col2", fieldName: "col2", type: "data", width: "75", numberFormat: "#,##0", header: { text: "Mining" } },
  { name: "col3", fieldName: "col3", type: "data", width: "75", numberFormat: "#,##0", header: { text: "Manufacturing" } },
  { name: "col4", fieldName: "col4", type: "data", width: "80", numberFormat: "#,##0", header: { text: "Construction" } },
  { name: "col5", fieldName: "col5", type: "data", width: "130", numberFormat: "#,##0", header: { text: "Wholesale/Retail, Consumer Goods" } },
  { name: "col6", fieldName: "col6", type: "data", width: "90", numberFormat: "#,##0", header: { text: "Food/Lodging" } },
  { name: "col7", fieldName: "col7", type: "data", width: "120", numberFormat: "#,##0", header: { text: "Transport, Warehousing, Telecom" } }
];

const testNumFooterCallback = (tree, column, footerIndex, footer, value) => {
  const ds = tree.getDataSource();
  const rows = ds.getChildren(-1);
  let sum = 0;
  for (let row of rows) {
    sum += (ds.getValue(row, column.name) || 0);
  }
  return sum;
};

const calcLeaf = (dataProvider, rows) => {
  const innerCalc = (dataProvider, rows) => {
    const leafSum = {};
    for (let row of rows) {
      if (dataProvider.getChildCount(row) > 0) {
        const childSum = innerCalc(dataProvider, dataProvider.getChildren(row));
        for (let key of Object.keys(childSum)) {
          if (childSum[key] > 0) {
            dataProvider.setValue(row, key, childSum[key]);
            leafSum[key] = (leafSum[key] || 0) + childSum[key];
          }
        }
      } else {
        if (!(dataProvider.getRowState(row) === "deleted" || dataProvider.getRowState(row) === "createdAndDeleted")) {
          for (let key of sumFields) {
            leafSum[key] = (leafSum[key] || 0) + (dataProvider.getValue(row, key) || 0);
          }
        }
      }
    }
    return leafSum;
  };
  const checkStates = dataProvider.checkStates;
  dataProvider.checkStates = false;
  innerCalc(dataProvider, rows);
  dataProvider.checkStates = checkStates;
};

const { treeView, dataProvider } = RealGrid.createTree("realgrid", {
  columns,
  header: {
    height: 40
  },
  display: {
    rowHeight: 36,
    emptyMessage: "No data to display."
  },
  footer: {
    height: 40
  },
  stateBar: {
    width: 16
  },
  onInitialized: (tree) => {
    fetch('/data/en/treedata-calc.json')
      .then(response => response.json())
      .then(data => {
        tree.getDataSource().setObjectRows(data, "rows", "", "");
        tree.expandAll();
      });
  }
}, {
  fields
});

treeView.onDataLoadComplated = (tree) => {
  const ds = tree.getDataSource();
  calcLeaf(ds, ds.getChildren(-1));
  
  for (var i = 0; i < sumFields.length; i++) {
    tree.columnByName(sumFields[i]).footer.valueCallback = testNumFooterCallback;
    tree.columnByName(sumFields[i]).styleCallback = (tree, cell) => {
      const dataRowId = cell.index.dataRow;
      return {
        editable: (tree.getDataSource()).getChildCount(dataRowId) <= 0,
        styleName: "right-column"
      };
    };
  }
};

dataProvider.onRowUpdated = (ds, row) => {
  const ancestors = ds.getAncestors(row);
  calcLeaf(ds, [ancestors[ancestors.length - 1]]);
};

dataProvider.onRowCountChanged = (ds) => {
  calcLeaf(ds, ds.getChildren(-1));
};

window.treeView = treeView;
window.dataProvider = dataProvider;
```


Change the value with setValue() at each time so that the recalculated value can be reflected in the parent node at the time of inquiry or value change.

```js
const sumFields = ["calc1", "calc2"];
const testNumFooterCallback = (tree, column, footerIndex, footer, value) => {
    const ds = tree.getDataSource();
    const rows = ds.getChildren(-1);
    let sum = 0;
    for (let row of rows) {
        sum += (ds.getValue(row, column.name) || 0)
    }

    return sum;
}

const calcLeaf = (dataProvider, rows) => {
    const innerCalc = (dataProvider, rows) => {
        const leafSum = {}
        for (let row of rows) {
            if (dataProvider.getChildCount(row) > 0) {
                const childSum = innerCalc(dataProvider, dataProvider.getChildren(row));
                for (let key of Object.keys(childSum)) {
                    if (childSum[key] > 0) {
                        dataProvider.setValue(row, key, childSum[key]);
                        leafSum[key] = (leafSum[key] || 0) + childSum[key]
                    }
                }
            } else {
                // softDeleting  hideDeletedRows값에 따라 조정 필요.
                if (!(dataProvider.getRowState(row) === "deleted" || dataProvider.getRowState(row) === "createdAndDeleted")) {
                    for (let key of sumFields) {
                        leafSum[key] = (leafSum[key] || 0) + (dataProvider.getValue(row, key) || 0)
                    }
                }
            }
        }
        return leafSum;
    }
    const checkStates = dataProvider.checkStates;
    dataProvider.checkStates = false;
    innerCalc(dataProvider, rows);
    dataProvider.checkStates = checkStates;
}

treeView.onDataLoadComplated = (tree) => {
    const ds = tree.getDataSource();
    calcLeaf(ds, ds.getChildren(-1));
    // Reconnect to refresh.
    for(var i = 0; i < sumFields.length; i++){
        tree.columnByName(sumFields[i]).footer.valueCallback = testNumFooterCallback;

        tree.columnByName(sumFields[i]).styleCallback = (tree, cell) => {
            const dataRowId = cell.index.dataRow;
            return {
                editable : (tree.getDataSource()).getChildCount(dataRowId) <= 0,
                styleName: "right-column"
            }
        }
    }
}

treeProvider.onRowUpdated =(ds, row) => {
    const ancestors = ds.getAncestors(row);
    calcLeaf(ds, [ancestors[ancestors.length-1]]);
}

// insert remove
treeProvider.onRowCountChanged = (ds) => {
    calcLeaf(ds, ds.getChildren(-1));
}
```