0

I have a VueJS data store like this...

nodes: {
    id: '001',
    name: 'name1',
    text: 'test1'
    children: [
        {
            id: '002',
            name: 'name2',
            text: 'test2'
        },
        {
            id: '003',
            name: 'name3',
            text: 'test3'
            children: [
                    {
                        id: '0002',
                        name: 'name02',
                        text: 'test02',
                        children: [
                                {
                                    id: '0002',
                                    name: 'name02',
                                    text: 'test02'
                                }
                        ]
                    }
            ]
        },
        {
            id: '004',
            name: 'name4',
            text: 'test4'
        }
    ]
}

Note: children's level (deep) is UNLIMITED


I need to select each by its id and add/update its sibling value.

Example: "Select id: 003 and add a text2: hello '

nodes: {
    id: '001',
    name: 'name1',
    text: 'test1'
    children: [
        {
            id: '002',
            name: 'name2',
            text: 'test2'
        },
        {
            id: '003',
            name: 'name3',
            text: 'test3',
            text2: 'hello'
        },
        {
            id: '004',
            name: 'name4',
            text: 'test4'
        }
    ]
}

I managed to do the add/update part using a method which call:

this.$set(this.nodes, 'text2', 'hello')

I'm stuck at selecting by id part. Can anyone figure out how to do so?

PS: I'm new to VueJS.

Bablu Ahmed
  • 4,412
  • 5
  • 49
  • 64
stackminu
  • 791
  • 2
  • 9
  • 24
  • "children's level (deep) is UNLIMITED" Does this mean that it might contain nested arrays, in addition to the objects you've shown? If so, please show an example of what that looks like in your data. If not, this is a duplicate of https://stackoverflow.com/questions/7364150/find-object-by-id-in-an-array-of-javascript-objects – Daniel Beck Feb 10 '19 at 12:39
  • @DanielBeck Unfortunately Yes, it contains nested childrens... But not in addition to the object I mentioned. I just UPDATED the question's sample arrray. – stackminu Feb 10 '19 at 12:41
  • OK, please include an example; there are lots of ways that could be structured, which would affect the answers. – Daniel Beck Feb 10 '19 at 12:43
  • @DanielBeck Updated! ID is unique as well. – stackminu Feb 10 '19 at 12:48

2 Answers2

1

If there is no order, you can use:

const nodes = {
    id: '001',
    name: 'name1',
    text: 'test1',
    children: [
        {
            id: '002',
            name: 'name2',
            text: 'test2'
        },
        {
            id: '003',
            name: 'name3',
            text: 'test3',
        },
        {
            id: '004',
            name: 'name4',
            text: 'test4'
        }
    ]
}

const operation = node => node.text2 = 'hello'

// recursive function applying the "operation" function on every node with the specified ID.
const applyOnId = (node, id, op) => {
  if(node.id === id) {
    op(node)
  } else if(node.children) {
    node.children.map(c => applyOnId(c, id, op))
  }
}

// test
console.log('before', nodes)
applyOnId(nodes, '002', operation) 
console.log('after', nodes)

Note that it will iterate through all nodes.

Nomeho
  • 156
  • 1
  • 5
0

Pretty much Nomeho's approach on doing it recursively only this one's using for...of statement which should have some performance benefit over .map() or .forEach() (when data gets bigger). Feel free to go with the other approach if performance is not of concern.

Also, showing you how to dynamically add new props on these nodes with Vue.$set.

const nodes = {
  id: '001',
  name: 'name1',
  text: 'test1',
  children: [
    {
      id: '002',
      name: 'name2',
      text: 'test2'
    }, 
    {
      id: '003',
      name: 'name3',
      text: 'test3',
      children: [
        {
          id: '0002',
          name: 'name02',
          text: 'test02',
          children: [
            {
              id: '0002',
              name: 'name02',
              text: 'test02'
            }
          ]
        }
      ]
    }, 
    {
      id: '004',
      name: 'name4',
      text: 'test4'
    }
  ]
};

new Vue({
  el: '#app',

  data() {
    return {
      form: {
        id: '003',
        name: 'test2',
        value: 'hello'
      },
      
      nodes,
      lastChangedNode: {}
    }
  },
  
  methods: {
    selectNode(id, node) {
      if (node.id === id) {
        return node;
      }

      if (node.children && node.children.length) {
        for (let child of node.children) {
          let x;

          if (x = this.selectNode(id, child)) {
            return x;
          }
        }
      }
    },
    
    addDynamicItem() {
      let node = this.selectNode(this.form.id, this.nodes);
      
      if (this.lastChangedNode = node) {
        this.$set(node, this.form.name, this.form.value);
      }
      else {
        this.lastChangedNode = {
          error: 'No matching ID found'
        }
      }
    }
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

<div id="app">
  <input v-model="form.id" placeholder="Enter a node ID" />
  <input v-model="form.name" placeholder="New property name" />
  <input v-model="form.value" placeholder="New property value" />
  <button @click="addDynamicItem">Add property</button> 
  
  <h3>Last changed node</h3>
  <pre>{{lastChangedNode}}</pre>
  
  <h3>All nodes</h3>
  <pre>{{nodes}}</pre>
</div>
Yom T.
  • 8,760
  • 2
  • 32
  • 49