Search code examples
javascripthtmlvue.jsvuejs2

how to count the depth of childrens of an array - Vue.js


I would like the calculate depth method to calculate the depth correctly and instead it is calculating in reverse assigning the first element with depth 2, the second with depth 1 and the third with depth 0, perhaps I need to create a new method where it receives the revisionData and then see item by item. And does not calculate for any reason a depth greater than 2.

<template>
      <acc-data-table-v2
            :data="revisionData"
            :columns="columns"
            primaryColumn="id"
            treeRowKey="id"
            row-key="id"
            :treeProps="{children: 'children'}"
            :row-class-name="tableRowClassName"
            >

            <template v-slot:subflowsRevisionsDescriptionTemplate="slotProps">
                <div>
                        <div :style="{ marginLeft: calculateMarginLeft(calculateDepth(slotProps.scope.row)) }">
                            <div v-if="slotProps.scope.row.step_id">
                                {{ slotProps.scope.row.step.description }} 
                            </div>
                            <div v-if="!slotProps.scope.row.step_id">
                                {{ slotProps.scope.row.description }} 
                            </div>
                        </div>
                </div>
            </template>
      </acc-data-table-v2>
</template>


<script>
export default {
  data() {
    return {
      revisionData: [
        {
          id: 1, //depth: 0
          name: 'Parent 1',
          children: [
            {
              id: 11, //depth: 1
              name: 'Child 1.1',
              children: [
                {
                  id: 111, //depth: 2
                  name: 'Grandchild 1.1.1'
                },
              ]
            },
            {
              id: 12, //depth: 1
              name: 'Child 1.2'
            }
          ]
        },
        {
          id: 2, //depth:0
          name: 'Parent 2',
          children: [
            {
              id: 21, //depth: 1
              name: 'Child 2.1'
            }
          ]
        }
      ]
    };
  },
  methods: {
        calculateMarginLeft(depth) {
            const marginLeft = depth * 12 + 'px';
            return marginLeft;
        },
        calculateDepth(item) {
            let depth = 0;
            let parent = item;
            while (parent.children && parent.children.length > 0) {
                depth++;
                parent = parent.children[0];
            }
            return depth;
        },
  }
};
</script>

Solution

  • This is a classic tree depth problem.

    Update your code like the following:

    • create a computed value revisionDataWithDepth that is a copy of revisionData with an additional field depth
    • pass this revisionDataWithDepth to your :data
    • to get left margin call calculateMarginLeft(slotProps.scope.row.depth)
    <template>
          <acc-data-table-v2
                :data="revisionDataWithDepth"
                :columns="columns"
                primaryColumn="id"
                treeRowKey="id"
                row-key="id"
                :treeProps="{children: 'children'}"
                :row-class-name="tableRowClassName"
                >
    
                <template v-slot:subflowsRevisionsDescriptionTemplate="slotProps">
                    <div>
                            <div :style="{ marginLeft: calculateMarginLeft(slotProps.scope.row.depth) }">
                                <div v-if="slotProps.scope.row.step_id">
                                    {{ slotProps.scope.row.step.description }} 
                                </div>
                                <div v-if="!slotProps.scope.row.step_id">
                                    {{ slotProps.scope.row.description }} 
                                </div>
                            </div>
                    </div>
                </template>
          </acc-data-table-v2>
    </template>
    
    
    <script>
    export default {
      data() {
        return {
          revisionData: [
            {
              id: 1, //depth: 0
              name: 'Parent 1',
              children: [
                {
                  id: 11, //depth: 1
                  name: 'Child 1.1',
                  children: [
                    {
                      id: 111, //depth: 2
                      name: 'Grandchild 1.1.1'
                    },
                  ]
                },
                {
                  id: 12, //depth: 1
                  name: 'Child 1.2'
                }
              ]
            },
            {
              id: 2, //depth:0
              name: 'Parent 2',
              children: [
                {
                  id: 21, //depth: 1
                  name: 'Child 2.1'
                }
              ]
            }
          ]
        };
      },
      computed: {
        revisionDataWithDepth: function() {
          // create local function that creates a copy of revisionData with depth included
          function withDepth(revisions, currentDepth) {
            if (!revisions || !revisions.length) {
              return [] // or return undefined
            }
        
            const revisionsWithDepth = [];
            for (const revision of revisions) {
              const revWithDepth = { ...revision };
              revWithDepth.depth = currentDepth;
              revWithDepth.children = withDepth(revision.children, currentDepth + 1);
              revisionsWithDepth.push(revWithDepth);
            }
            return revisionsWithDepth;
          }
          return withDepth(this.revisionData, 0);
        }
      }
      methods: {
            calculateMarginLeft(depth) {
                const marginLeft = depth * 12 + 'px';
                return marginLeft;
            },
      }
    };
    </script>
    

    Tip: you can even use a one-liner for withDepth:

    function withDepth(revisions, depth) {
      return (revisions ?? []).map(revision => ({ ...revision, depth, children: withDepth(revision.children, depth + 1) }))
    }