2

I have a computed property that simply formats the date:

computed: {
    items() { 
        return this.licenseItems.map(function(license) {
             license.expires_at = moment(license.expires_at).format('MM/DD/YYYY');
             return license;
        });
    }
}

I pass licenseItems to a form component with the .sync modifier and emit update:field event from the form. In vue dev tools, I can see that licenseItems (data) is properly updated, but items (computed) is still showing the old data so no re-computation was performed.

I have noticed that if I remove the map and just return the licenseItems object from the computed property, it is updated. Is there some issue with Vue's computed property on mapped objects when using the sync modifier?

Devon Bessemer
  • 34,461
  • 9
  • 69
  • 95
  • There's not really enough information here to go on? Can you show how its passed in the parent template and how it's emitted in the child? Are you adding a new property to the license? – Bert Oct 30 '17 at 17:37
  • Are you passing the array, or one element of the array to the component? – Roy J Oct 30 '17 at 17:55
  • Well, both, but my concern is passing `:items.sync="licenseItems"`. The sync works fine, just the computed property isn't updated. – Devon Bessemer Oct 30 '17 at 17:57
  • Must be something specific to my component, I put together http://jsfiddle.net/kvej67xy/2/ to test this functionality and it seems to work fine. – Devon Bessemer Oct 30 '17 at 18:15

1 Answers1

2

You should be aware that you're modifying underlying objects in your computed. From your fiddle:

  computed: {
    mapped: function() {
      return this.items.map(function(item) {
        let original = item.date;
        item.date = moment(item.date).format('MM/DD/YYYY');
        console.log(original + ' => ' + item.date);
        return item;
      });
    }
  }

Your incrementDates function also modifies the underlying objects directly.

Because each element of items is an object, item is a reference to that same object, so your routine updates the members of items itself. If you intend to modify the object values, you should use a watch instead of a computed. If you want to have a proper computed that does not modify data items, you need to deep copy the objects.

In my example below, you can see that the data value and the computed value are distinct, but the computed is based on the data. Also, the update event works with sync to update the value in the parent, rather than the value being updated directly in the component. You can enter any format of date that Date understands to set the value.

new Vue({
  el: '#app',
  data: {
    licenseItems: [{
      expires_at: Date.now()
    }]
  },
  computed: {
    items() {
      return this.licenseItems.map(function(license) {
        const newItem = Vue.util.extend({}, license);

        newItem.expires_at = moment(license.expires_at).format('MM/DD/YYYY');
        return newItem;
      });
    }
  },
  components: {
    myUpdater: {
      props: ['items'],
      methods: {
        doUpdate(event, index) {
          const newObj = this.items.map(item => Vue.util.extend({}, item));
          
          newObj[index].expires_at = new Date(event.target.value);
          console.log("new object", newObj);
          this.$emit('update:items', newObj);
        }
      }
    }
  }
});
<script src="//cdnjs.cloudflare.com/ajax/libs/moment.js/2.19.1/moment.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>
<div id="app">
  <my-updater :items.sync="licenseItems" inline-template>
    <div>
      <input v-for="item, index in items" :value="item.expires_at" @change="doUpdate($event, index)">
    </div>
  </my-updater>
  <div v-for="item in items">
    {{item.expires_at}}
  </div>
</div>
Roy J
  • 42,522
  • 10
  • 78
  • 102
  • Yeah, I actually just realized I excluded the $emit entirely in the fiddle, so that kind of proves your point that I'm modifying the referenced object and not a copy of object. Thanks for putting this together, I was definitely overlooking the fact that map would be using a reference to the original object here. – Devon Bessemer Oct 30 '17 at 19:36