0

The problem here is simple to ilustrate, I'm executing a method on the parent component, which changes the value of a property of my object products (also belonging to the parent data), this is working fine.

But i'm passing this object as a prop to a child component and watching it deeply <- this is failing, as you can see the alert('changed'); is never executed:

Vue.component('child', {
 props: ['prods'],
  watch: {
   prods: {
     handler: function(new_val, old_val) {
       alert('watched');
      },
      deep: true
    }
  }
})

new Vue({
  el: '#app',
  template: '<div>{{products[0].value}}<br><button v-on:click="incre">increment prod 0</button></div>',
  data: {
   products: [
     {id: 1, name: 'prod1', value: 20},
      {id: 2, name: 'prod2', value: 40},
    ],
  },
  methods: {
    incre: function() {
      this.products[0].value += 20;
    }
  }
})
<script src="https://unpkg.com/vue@2.4.4/dist/vue.js"></script>
<div id="app">
  <child v-bind:prods="products"></child>
</div>
https://jsfiddle.net/dmbgmxzh/6/

(Update):
this works: https://jsfiddle.net/dmbgmxzh/5/
this doesn't: https://jsfiddle.net/dmbgmxzh/6/
This is strange...

Hula Hula
  • 553
  • 8
  • 20
  • Unfortunately, it works as expected. And `deep: true` is unnecessary for array. –  Nov 11 '17 at 12:51

1 Answers1

2

As an alternative, you could pass product as a prop to your child component, watching each product separately. Like this:

Vue.component('child', {
    props: ['product'],
    watch: {
        product: {
            handler: function (new_val, old_val) {
                alert('watched');
            },
            deep: true
        }
    }
})
new Vue({
    el: '#app',
    data: {
        products: [
            {
                id: 1,
                name: 'prod1',
                value: 20
            },
            {
                id: 2,
                name: 'prod2',
                value: 40
            },
    ],
    },
    methods: {
        incre: function () {
            this.products[0].value += 20;
        }
    }
})
<script src="https://unpkg.com/vue@2.4.4/dist/vue.js"></script>
<div id="app">
    <div>{{products[0].value}}<br><button v-on:click="incre">increment prod 0</button></div>
    <child :product="product" v-for="product in products"></child>
</div>

See this post also.

Andrei Roba
  • 2,156
  • 2
  • 16
  • 33
  • Thank you, but I don't want to have several `child`s, the child on this project is a google map, and I just change the latitude/longitude of the marker inside it (in this case the `products` object) – Hula Hula Nov 11 '17 at 12:49
  • then just pass in `` instead, it should work the same – Andrei Roba Nov 11 '17 at 12:52
  • This seens stupid it works this way: https://jsfiddle.net/dmbgmxzh/5/ . Bur not working this way: https://jsfiddle.net/dmbgmxzh/6/ – Hula Hula Nov 11 '17 at 13:08
  • an explanation why OP's code doesn't work would be nice – samayo Nov 11 '17 at 13:39
  • from what I see in your second fiddle (/6) you are already using a template in your vue instance which kind of overrides your html content, so if you want to define rendering by html I would suggest you move it entirely to html like so https://jsfiddle.net/dmbgmxzh/7/ or if it's still not good enough, maybe using [slots](https://vuejs.org/v2/guide/components.html#Content-Distribution-with-Slots) would help – Andrei Roba Nov 11 '17 at 14:37