I'm using Angular 15 with PrimeNG.

{
    id: 1,
    data: {
        value: "Lorem ipsum"
    },
    expression: null,
    subElements: [
        {
            id: 2,
            data: {
                value: 2
            },
            expression: "v0 / v1",
            subElements: [
                {
                    id: 4,
                    data: {
                        value: 100
                    },
                    expression: null,
                    subElements: []
                },
                {
                    id: 5,
                    data: {
                        value: 50
                    },
                    expression: "v0 + v1",
                    subElements: [
                        {
                            id: 6,
                            data: {
                                value: 20
                            },
                            expression: null,
                            subElements: []
                        },
                        {
                            id: 7,
                            data: {
                                value: 30
                            },
                            expression: null,
                            subElements: []
                        }
                    ]
                }
            ]
        },
        {
            id: 3,
            data: {
                value: "Lorem ipsum"
            },
            expression: null,
            subElements: []
        }
    ]
}

For example, if value for object with id 7 changes from 30 to 40, the value for its parent object should update to 60, based on the expression in the parent object ("v0 + v1"), and the value for its parent object (id: 2) should update as well, based on its expression ("v0 / v1"). I'm using math.js for calculations. Number of object nesting levels is not fixed, and not all objects have a numerical value. Objects with non numerical values should be ignored. I'm using p-tree to iterate through objects and show inputs for them. This is my input field:

<p-inputNumber
  [(ngModel)]="node.data.value"
  (onInput)="calculateExpression($event, node)"
  [minFractionDigits]="2"
  [maxFractionDigits]="2"
></p-inputNumber>

Nothing I tried is worth mentioning.

1

There are 1 best solutions below

0
trincot On

You could use a post-order recursive iteration through the tree and generate a new one that has all the expressions re-evaluated:

function updateTree(root) {
    if (!root.subElements?.length) return root;
    const subElements = root.subElements.map(updateTree);
    if (!root.expression) return {...root, subElements };
    const scope = Object.fromEntries(subElements.map((node, i) => ["v" + i, node.data?.value]));
    const data = { value: math.evaluate(root.expression, scope) };
    return {...root, subElements, data };
}

// Structure from the question:
const root = {id: 1,data: {value: "Lorem ipsum"},expression: null,subElements: [{id: 2,data: {value: 2},expression: "v0 / v1",subElements: [{id: 4,data: {value: 100},expression: null,subElements: []},{id: 5,data: {value: 50},expression: "v0 + v1",subElements: [{id: 6,data: {value: 20},expression: null,subElements: []},{id: 7,data: {value: 30},expression: null,subElements: []}]}]},{id: 3,data: {value: "Lorem ipsum"},expression: null,subElements: []}]};
// Mutate the value of the node with id 7:
root.subElements[0].subElements[1].subElements[1].data.value = 40; // was 30.
const newRoot = updateTree(root);
console.log(newRoot);
<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjs/11.8.0/math.js"></script>

If you need the original hierchy to be mutated inplace, then:

function updateTree(root) {
    root.subElements?.forEach(updateTree);
    if (!root.expression) return;
    const scope = Object.fromEntries(root.subElements.map((node, i) => ["v" + i, node.data?.value]));
    root.data.value = math.evaluate(root.expression, scope);
}

// Structure from the question:
const root = {id: 1,data: {value: "Lorem ipsum"},expression: null,subElements: [{id: 2,data: {value: 2},expression: "v0 / v1",subElements: [{id: 4,data: {value: 100},expression: null,subElements: []},{id: 5,data: {value: 50},expression: "v0 + v1",subElements: [{id: 6,data: {value: 20},expression: null,subElements: []},{id: 7,data: {value: 30},expression: null,subElements: []}]}]},{id: 3,data: {value: "Lorem ipsum"},expression: null,subElements: []}]};
// Mutate the value of the node with id 7:
root.subElements[0].subElements[1].subElements[1].data.value = 40; // was 30.
updateTree(root);
console.log(root);
<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjs/11.8.0/math.js"></script>