Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Avoid mutating a prop directly in VueJS 2

First of all, I am just starting playing with VueJS, so this cannot be a VueJS version thing as suggested here

It might be a duplicate of :

  • How to solve [Vue warn]: Avoid mutating a prop directly since the value will be overwritten on vue.js 2? - the difference is that I am only trying to set the values with v-bind, once.
  • Avoid mutating a prop directly since the value will be overwritten - This also looks somehow similar, but the solution didn't worked for me.
  • What's the correct to modify VUE component via javascript? - this solution looks pretty much what I have in my case
  • vuejs update parent data from child component

My problem starts with my Html looking like this:

<div id="app">
  <div class="row">
    <div class="form-group col-md-8 col-md-offset-2">
      <birthday-controls
       :birthDay="birthDay"
       :birthMonth="birthMonth"
       :birthYear="birthYear">
      </birthday-controls>
    </div>
  </div>
</div>

and JS:

Vue.component('birthday-controls', {

    template: `<div class="birthday">
    <input type="text" name="year"  placeholder="yyyy" v-model="birthYear" size="4" maxlength="4"/>
    <input type="text" name="month" placeholder="mm" v-show="validYear" v-model="birthMonth" size="3" maxlength="2"/>
    <input type="text" v-show="validYear && validMonth" name="day" placeholder="dd" v-model="birthDay" size="2" maxlength="2"/>
  </div>`,

    props: ['birthDay', 'birthMonth', 'birthYear'],

    computed: {
        validYear: function() {
            return (this.birthYear > new Date().getFullYear()-100 && this.birthYear < new Date().getFullYear()-14)
        },
        validMonth: function() {
            return (this.birthMonth > 0 && this.birthMonth <= 12)
        },
        validDay: function() {
            return (this.birthDay > 0 && this.birthDay <=31) //I have to add more checking here for february, leap years and ....
        }
    }

});

new Vue({
    el: '#app',

    data: function() {
        return {
            birthDay: "",
            birthMonth: "",
            birthYear: ""
        }
    },

});

I have prepared codepen here: http://codepen.io/AngelinCalu/pen/OpXBay

However, the second answer from here: vuejs update parent data from child component makes me realise that I'm missing something

In that example it sets an this.$emit('increment') inside one of the methods, and triggers that on specific event.

In this other example: Update a child's data component to the father component in vue.js using .vue webpack(vue2) , the answer suggest adding a watch to emit the change.

  watch: {
    val() {
      this.$emit('title-updated', this.val);
    }
  }

Now I'm even more confused! What is the right (or best) way to deal with this problem?

Note: If I remove from the initial html :

   :birthDay="birthDay"
   :birthMonth="birthMonth"
   :birthYear="birthYear"

It still works as expected, but I'm still getting that Vue warn, however, if I'm following the method from here: https://stackoverflow.com/a/41901150/2012740, it stops working, but I'm getting no error.

My Updated code: https://jsfiddle.net/angelin8r/647m7vdf/

To conclude: I need the functionality from the beginning but without the [Vue warn]

This is what I got in my initial example:

[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "birthYear"

like image 745
Angelin Calu Avatar asked Mar 05 '17 21:03

Angelin Calu


People also ask

How do I fix avoid mutating a prop directly in Vue?

Error message: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value.

Can we mutate props in Vue?

This rule applies when a prop of a Vue component is mutated inside the component. If a prop is mutated, Vue outputs a warning message because the mutated prop value will be overwritten whenever the parent component re-renders.

Is it okay to mutate props?

Short answer is: NO. Long answer is: It will not work.

What is two-way binding in Vue?

The v-model directive makes two-way binding between a form input and app state very easy to implement. One can bind a form input element and make it change the Vue data property when the content of the field changes.


1 Answers

The warning is the result of setting v-model to the value of your properties. The reason is because if you change birthYear, birthMonth, or birthDay outside the component, then whatever the value is currently inside the component will be immediately overwritten.

Instead, capture a copy.

Vue.component('birthday-controls', {

    template: `<div class="birthday">
    <input type="text" name="year"  placeholder="yyyy" v-model="internalBirthYear" size="4" maxlength="4"/>
    <input type="text" name="month" placeholder="mm" v-show="validYear" v-model="internalBirthMonth" size="3" maxlength="2"/>
    <input type="text" v-show="validYear && validMonth" name="day" placeholder="dd" v-model="internalBirthDay" size="2" maxlength="2"/>
  </div>`,

    props: ['birthDay', 'birthMonth', 'birthYear'],

    data(){
      return {
        internalBirthDay: this.birthDay,
        internalBirthMonth: this.birthMonth, 
        internalBirthYear: this.birthYear
      }
    },

    computed: {
        validYear: function() {
            return (this.internalBirthYear > new Date().getFullYear()-100 && this.internalBirthYear < new Date().getFullYear()-14)
        },
        validMonth: function() {
            return (this.internalBirthMonth > 0 && this.internalBirthMonth <= 12)
        },
        validDay: function() {
            return (this.internalBirthDay > 0 && this.internalBirthDay <=31) //I have to add more checking here for february, leap years and ....
        }
    }

});

You did this almost exactly in your fiddle, but you did not correct your computed values.

computed: {
    validYear: function() {
        return (this.birthYear > new Date().getFullYear()-100 && this.birthYear < new Date().getFullYear()-14)
    },
    validMonth: function() {
        return (this.birthMonth > 0 && this.birthMonth <= 12)
    },
    validDay: function() {
        return (this.birthDay > 0 && this.birthDay <=31) //I have to add more checking here for february, leap years and stuff
    }
},

should be

computed: {
    validYear: function() {
        return (this.var_birthYear > new Date().getFullYear()-100 && this.var_birthYear < new Date().getFullYear()-14)
    },
    validMonth: function() {
        return (this.var_birthMonth > 0 && this.var_birthMonth <= 12)
    },
    validDay: function() {
        return (this.var_birthDay > 0 && this.var_birthDay <=31) //I have to add more checking here for february, leap years and stuff
    }
},
like image 133
Bert Avatar answered Oct 09 '22 22:10

Bert