VueJS select by id [data]

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.

2 answers

  • answered 2019-02-10 13:04 Nomeho

    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.

  • answered 2019-02-10 16:14 Yom S.

    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>