How To Watch Nested Props And Data in Vuejs?

1- Intro

Vuejs Provides Props as custom attributes you can use to pass data to a child component.
With these props, we can customize our component however we want.

2- how to pass props to a component “not nested”?

Vue.component('child', {
  props: ['myprop'],  #registering our prop 'myprop'
  template: `
    <div class="person-card">
      <h2>{{ person.name }}</h3>
      <h4>{{ person.age }}</h4>
      <img :src="person.image"/>
    </div> 
  `
})

Now in our app.vue we define some an object ‘person’ which we want to pass to our Child : Component:

new Vue({
  el: "#app",  
  data: {
      persons:{
          name: Alex,
          age: 19,
          image: 'https://.../image.jpg'
      }
  }
})

finally, you only bind the object ‘person’ to our prop :stuck_out_tongue:

 <child :myprop="person"><child>

3- How to watch props “not nested”?

For the same example above ↑
We can easily watch changes in our prop.
to notice the changes we need to define another property ‘propChanged’ besides persons.

new Vue({
  el: "#app",  
  data: {
      persons:{
          name: Alex,
          age: 19,
          image: 'https://.../image.jpg'
       },
       propChanged: false ///when prop changes this becomes true
  }
})

To the same file, we must add a Watcher to watch changes of our prop:

    new Vue({
      el: "#app",  
      data: {
          persons:{
              name: Alex,
              age: 19,
              image: 'https://.../image.jpg'
           },
           propChanged: false ///when prop changes this becomes true
      },
      watch: {
        yourProp (val, oldVal) {
          if (val !== oldVal) this.propChanged = true
        }
      }
    })

Now in case our prop changes, the condition above if (val !== oldVal) is valid, therefore propChanged changes its value to true.

3- How to Pass Nested props?

Problem: In Vuejs, it is possible to have an object as a Prop. But, how to watch all properties when we have deeply nested Props?

Example of nested props:

props: {
    nestedProps: { // nested object.
       fullName: {
            firstName: {
                   type: String, 
                   required: true 
            },
            lastName: {
                  type: String, 
                  required: true 
            }
       },
       otherValue: {
              birth: {
                 type: Number, 
                 required: true 
              age: {
                 type: Number, 
                 required: true 
                 default: 16 // if age was not provided
       }
     },
}

Note: You can ensure your component receives exactly the data want by specifying:

  • type: you can specify the type of what you component is going to receive.
    {type: string | number | constructor | …}
    Example:
propA: Number, // inline defintion
propD: { // object definition 
  type: Number, 
  default: 100
},
  • "default": you can give a Default value to your prop in case no value was passed.
    {type: string | number | function | …}
    Example:

    propE: {
      type: Object,
      default: function () {
            return { message: 'hello' }
          }
    } 
    
  • "required": you can specify if the value is required or not.
    Example:

      propC: {
        type: String,
        required: true
      }
    
  • "validator": you can validate and filter your data however you want using the option which accepts a function.
    Example:

    propF: {
      validator: function (value) {
        // The value must match one of these strings
        return ['success', 'warning', 'danger'].indexOf(value) !== -1
      }
    }
    

Defining our Child component:

Vue.component('child', {
  props: ['nestedProps'],  #registering our prop 'myprop'
  template: `
    <div class="person-card">
      full name is:
        <h2>{{ nestedProps.fullName.firstName }}</h2>
        <h2>{{ nestedProps.fullName.lastName }}</h2>
      birth and age are:
        birth: <p>nestedProps.otherValues.birth</p>
        age: <p>nestedProps.otherValue.age</p>
    </div> 
  `
})

Now we can define the Data object that we want to render:

myNestedData: {
        fullName: {
          firstName: { 'Alex' },
          lastName: { 'Wayne' } 
        },
        otherValue: {
          birth: { 1999 }
          age: { 20 }
         },
}

Now, in our child component, we bind our prop with the data object we want like this:

 <child :nesttedProps="myNestedData"><child>

4- How to Watch Nested props?

it is very simple, for the same example we can do this:

watch: {
  myNestedData {
     handler(newValue, oldValue) {
       // react to changes
     },
     deep: true
  }
}

using deep: true in vue.js allows you to keep track of all root and child elements of your specified property.

Notes:

  • When you got more Data values (e.g: persons ).
    Just use an array to store your data list of persons and their info, then, simply use the v-for Directive to easily render your List.

note: when your app gets complicated and hard to understand consider this Topic:
What Is Vuex And How Does It Work? or check this one for all known techniques: How to pass data between Vuejs components effectively?

<template>
    <div>
        <child v-for="(item, key, index) in myDataArray"
            :item="item"
            :index="index"
            :key="key"">
        </child>
    </div>
</template>
data(){
     return {
         myDataArray: [ 
           {  // person 1
               fullName: {
                      {firstName: Alex}, 
                      {lastName: Wayne}
               }, 
               otherValues: {
                       {age: 15},
                       {birth: 2005}
               }
           }, 
               // person 2
           {
               fullName: {
                      {firstName: Alex}, 
                      {lastName: Wayne}
               }, 
               otherValues: {
                       {age: 15},
                       {birth: 2005}
               }
            }
         ]
 },

finally you must change your child component to be compliant with your array’s data.

Vue.component('child', {
  props: ['nestedProps'],  #registering our prop 'myprop'
  template: `
    <div class="person-card">
      full name is:
        <h2>{{ myDataArray.fullName.firstName }}</h2>
        <h2>{{ myDataArray.fullName.lastName }}</h2>
      birth and age are:
        birth: <p>myDataArray.otherValues.birth</p>
        age: <p>myDataArray.otherValue.age</p>
    </div> 
  `
})

see also:
for an easier way to communicate between components: What Is Vuex And How Does It Work?
for all the techniques see: How to pass data between Vuejs components effectively?

And that’s it hope you enjoyed this tutorial!
cheers :grinning: