Vue - listen props changes (object), watch not triggering

15,633

Solution 1

it' easy. you need add deep property to watch object.

props: ['myprop'],
  watch: { 
    myprop: {
        deep: true,
        handler: function(newVal, oldVal){
           console.log('Prop changed: ', newVal, ' | was: ', oldVal)
        }
      }
    }
  }

You don't need to use something like

'myprop.id'

Solution 2

First need a prop for parent component and a local data for select v-model:

<select v-model="localid">
        <option value="1">1</option>
        <option value="2">2</option>
        <option value="3">3</option>
      </select>

and your child component's prop and data is:

data() {
      return {
        localid:null
      }
    },
props: ['pid']

if a component prop changed and he parent want to know this change you can use .sync modifier. First update your prop in your component. watch your localid data and then update your pid prop with your new data:

watch:{
     localid:function(){
     this.$emit('update:pid', this.localid)
    }
}

Now parent know that pid prop is changed and your parent component's data will change with prop's new data. Your parent component:

<div id="app">
  <child :pid="myObj.id"></child>
  <button @click="text = 'Another text'">Change text</button>
</div>

More info: https://vuejs.org/v2/guide/components-custom-events.html#sync-Modifier

Solution 3

try like that

{
  ...
  props: ['myprop'],
  watch: { 
    'myprop.id': {
      handler(newVal, oldVal) {
        console.log('Prop changed: ', newVal, ' | was: ', oldVal)
      },
      deep: true
    }
  ...
}

Solution 4

There is only one problem in your code:

"text = 'Another text'"

doesn't work because text was not declared. So you might want to change it to

"myObj = { id: 'Another text' }"

I suppose this is what you want. Then it's reactive, the text changes to 1 or 2 or 3 when you select and to "Another text" when you click and your watcher fires and logs the old and the new value

Share:
15,633
lukasamd
Author by

lukasamd

Updated on June 13, 2022

Comments

  • lukasamd
    lukasamd almost 2 years

    I have similiar problem to this: VueJs 2.0 - how to listen for `props` changes but in my case, I send object from parent to child, also with passing down few times to use benefits of object references in JS.

    There is an example, but working fine:

    <div id="app">
      <child :myprop="myObj"></child>
      <button @click="text = 'Another text'">Change text</button>
    </div>
    
    <script>
    new Vue({
      el: '#app',
      data: {
        myObj: {
           id: null
        }
      },
      components: {
        'child' : {
          template: `<div>
          <p>{{ myprop.id }}</p>
          <select v-model="myprop.id">
            <option value="1">1</option>
            <option value="2">2</option>
            <option value="3">3</option>
          </select>
          </div>`,
          props: ['myprop'],
          watch: { 
            'myprop.id': function(newVal, oldVal) { // watch it
              console.log('Prop changed: ', newVal, ' | was: ', oldVal)
            }
          }
        }
      }
    });
    </script>
    

    https://jsfiddle.net/8hqwg6Ln/

    In my case, I set v-model on select to "parentObj.id" and then add watcher for this. The goal is to change other parentObj property after changing id - user select id, after that I search this id in my array and set parentObj.price, which is v-model to other component (modified input) on the same level as select field.

    When I change slection, watcher is not triggered. If I add @change with my custom method, it works, but with delay (I see this in vue dev tools, must refresh to get current data), also, it doesn't work for second input component - it doesn't see changes parentObj.price.

    What is the best approach to use complex collection with passing down and modify this one collection with many different components? I think, I can add "copies" of properties to local level (so, create data with id and price on child), add watchers, and use this.$set to update received props. But... is it good option, maybe here is something better?

  • lukasamd
    lukasamd over 5 years
    Unfortunately, doesn't work. Also with immediate as true still doesn't work.
  • lukasamd
    lukasamd over 5 years
    This text is only paste from other example, sorry. The problem is in myObj.id and changing it using select in child component.
  • v-moe
    v-moe over 5 years
    I see, maybe you should think about using Vuex, (or simply an object that gets passed to your components as data instead of prop if you think Vuex is overkill). Then every component can change the shared state without depending on the parent. See vuejs.org/v2/guide/state-management.html (it does not create copies, by the way, it's "references used by multiple components")
  • user9645
    user9645 over 3 years
    This worked for me - I edited the code snippet to fix the syntax.