1

I'm working on a Vuejs project. On some point I have an array of objects (data taken from the server) like this:

[
    {
        name: "Book one",
        price: 12,
        active: true
    },
    {
        name: "Small drone X12",
        price: 54,
        active: false
    },
    {
        name: "something else",
        price: 32,
        active: true
    }
]

The data is used to generate dynamic form on the page so the user can edit the data. How can I check if any of the objects has been changed by the user and which one (can be changed one, two or any number of them) so I can show the user a button to update the changed data. I'd like to update only the data from the object(s) that actually has been changed - not all of them.

2 Answers 2

1

You can keep around a calculated JSON string of the objects in a mapped array

const comparison = data.map(JSON.stringify); // in order string based values for direct comparison

Later in code you can compare to those values

data.forEach((obj, index) => {
  // if they do match, skip
  if (JSON.stringify(obj) === comparison[index]) return;

  // they don't match, update comparison value
  comparison[index] = JSON.stringify(obj);

  // update VUE state and other things here
})

This way you only need to update the updated values, and only ever need to calculate comparison values. This works even if the object has certain field updated but is still the same object(which would pass an equality test of object === object even though a value has updated).

Another more involved example revolves around proxying or watching every setter on the objects to fire a change whenever they are updated in place, but for the life of me I still don't understand proxies right...

What about if the Array of Field changes size?

While this works if the data is never changing in size, if the data array changes in size... it will still work because there will be an undefined in the matching index, causing the string comparison to return false. However it would be best to add it to the form then at the end with a simple modification to the above code:

if (comparison[index] === undefined) {
  // add the form to the field, it'll add in order
}
// they don't match, update comparison value
...
Sign up to request clarification or add additional context in comments.

Comments

1

One approach is to listen for change events on pass the object's array index and property name for the modified field. Then assign the value to another array holding the changes.

new Vue({
  el: '#app',
  data: () => ({
    items: [
      {
        name: "Book one",
        price: 12,
        active: true
      },
      {
        name: "Small drone X12",
        price: 54,
        active: false
      },
      {
        name: "something else",
        price: 32,
        active: true
      }
    ],
    changed: []
  }),
  mounted () {
    this.changed = Array(this.items.length).fill({})
  },
  methods: {
    onPropertyChanged (index, prop) {
      this.changed.splice(index, 1, Object.assign({}, this.changed[index], { [prop]: this.items[index][prop] }))
    }
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  <div v-for="(item, index) in items" :key="index">
    <label>Name</label>
    <input v-model="item.name" @change="onPropertyChanged(index, 'name')">
    <label>Price</label>
    <input v-model="item.price" type="number" min="0" @change="onPropertyChanged(index, 'price')">
    <label>Active</label>
    <input v-model="item.active" type="checkbox" @change="onPropertyChanged(index, 'active')">
  </div>
  
  <hr/>
  
  <h4>Changes</h4>
  <pre v-for="(item, index) in changed" :key="`change-${index}`"><code>{{ item }}</code></pre>
</div>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.