0

I got an vue-warning (which results to as an error on my end coz my code is not working) that says:

[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: "editmode"

With it, tried the suggestion here but can't make it work. Below is my work:

props:{
    editmode:{
        type: Boolean,
        default: false,
    }
},
methods:{
    toggleM(){
        var editmode = this.editmode;
        editmode = !editmode;
        this.editmode = editmode; 
        if(editmode == false){
            //dothis
        }else{
            //dothat
        }
    },
}

TEMPLATE

<template>
<div class="ui-table-container-body">
        <div class="ui-table" v-if="Boolean(items.length) || Boolean(Object.keys(items).length)" v-cloak>
            <ui-table-body ref="body" v-model="items"
                :editmode="editmode"
                >
            </ui-table-body>
        </div>
    </div>
</template>

The line this.editmode = editmode; is the one pointed in my console, is there any way I can surpass this?

  • Depends, Did you want to change the prop value on the parent that passed down the prop? or just set a local value? – CUGreen Apr 26 '18 at 09:36
  • You can't mutate a prop directly. You must use a `data` variable and `watch` the mutations of your prop to apply it to the data and `watch` the mutations of your data to `$emit` event and mutate your prop in the parent component – Victor Castro Apr 26 '18 at 09:37
  • @CUGreen have to change the prop value on the parent that passed down the prop and have to have a copy in local. –  Apr 26 '18 at 09:38
  • @LeCintas How can I possibly do that? (drunk) –  Apr 26 '18 at 09:38

3 Answers3

1

You shouldn't mutate props from the component itself. See the One Way Data Flow section of the guide. You can use a prop as the initial value, and then keep a value in the data section and mutate that:

props: {
    editmode: {
        type: Boolean,
        default: false,
    }
},
data () {
    return {
        emode: this.editmode,
    }
},
methods: {
    toggleM () {
        let editmode = this.emode;
        editmode = !editmode;
        this.emode = editmode; 
        if (editmode == false) {
            // dothis
        } else {
            // dothat
        }
    },
}

Demo

Vue.component('editbox', {
  template: '<div>' +
    '<button @click="toggleM">{{ btext }}</button>' +
    '<input v-if="emode" />' +
    '</div>',
  props: ['editmode'],
  data () {
    return {
      emode: this.editmode,
    }
  },
  computed: {
    btext () {
      return this.emode ? "Text" : "Edit";
    }
  },
  methods:{
    toggleM() {
        this.emode = !this.emode;
    },
  }
})

var app = new Vue({
  el: '#app',
  data: {
    mode: true,
  }
})
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

<div id="app">
  <editbox :editmode="mode" />
</div>
tony19
  • 125,647
  • 18
  • 229
  • 307
Marco Pantaleoni
  • 2,529
  • 15
  • 14
  • you have to refer to `this.emode` (from `data`) in the field `v-if`, not to `editmode`) (the prop). – Marco Pantaleoni Apr 26 '18 at 10:01
  • What do you mean by that? (drunk) –  Apr 26 '18 at 10:02
  • it's demonstrated in the demo snippet – Marco Pantaleoni Apr 26 '18 at 10:09
  • When I added this line `input v-if="emode"`, my table is not displaying anymore :S –  Apr 27 '18 at 01:47
  • I've updated my question with my template. If you can help me see it. Thanks. –  Apr 27 '18 at 02:56
  • your template seems that of the parent, hard to tell why you have the problem with `v-if`. Post a minimal but complete example and I'll try to help (maybe in a different question and let me know in a comment). If this solved your doubt about the prop mutation warning, please consider marking it as accepted. – Marco Pantaleoni Apr 27 '18 at 10:01
  • All of the answers here solved the mutation problem, but all of it also results different errors after. –  Apr 27 '18 at 10:03
  • your new errors probably are due to something else. Setup a minimal but reproducible example exhibiting the new errors, e.g. on [codesandbox](https://codesandbox.io/s/vue), post a new question with the link and I'll try to help you. Let me know the link to the new question (put it in a comment here maybe). – Marco Pantaleoni Apr 27 '18 at 13:31
  • Just a qq, why is it that your **demo** code and above the **demo** codes different? –  Apr 30 '18 at 05:29
  • The first code snippet tries to as similar as possible to your original code , to give you more context, while the demo is simplified and more what I'd do in practice (there is no need for the temporary `editmode` variable, no need for the if, ...) – Marco Pantaleoni Apr 30 '18 at 08:15
  • You mean with this one : *(there is no need for the temporary editmode variable, no need for the if, ...)* is that I don't have to do this : `if (editmode == false) { // dothis } else { // dothat }`? –  Apr 30 '18 at 09:16
  • yes, exactly, and you need only `this.emode` and not the temporary `editmode` – Marco Pantaleoni Apr 30 '18 at 13:55
1

You must use a data variable as a gateway to your prop.

In your component, the code code should look like this:

props:{
    editmode:{
        type: Boolean,
        default: false,
    }
},
data: {
  dataEditMode = false
},
watch: {
    'editmode': {
      handler: 'onEditmodeChanged',
      immediate: true,
    },
    'dataEditMode': {
      handler: 'onDataEditModeChanged'
    }
},
methods:{
    toggleM(){
        var editmode = this.dataEditMode;
        editmode = !editmode;
        this.dataEditMode = editmode; 
        if(editmode == false){
            //dothis
        }else{
            //dothat
        }
    },
    onEditmodeChanged (newVal) {
      this.dataEditMode = newVal
    },
    onDataEditModeChanged (newVal) {
      this.$emit('editmodeChanged', newVal)
    }
}

and the the inclusion of this component in your parent-component should look like this:

<my-component-name :editmode="editmode" @editmodeChanged="(e) => { editmode = e }"></my-component-name>

Victor Castro
  • 1,232
  • 21
  • 40
  • no, mutating properties from the component itself should be avoided. This solution also doesn't solve the problem of the parent re-mutating the property. And the warning is still emitted. – Marco Pantaleoni Apr 26 '18 at 09:52
  • My bad, I just edited the code. It was a mistake: I copied/paste the autor's code – Victor Castro Apr 26 '18 at 10:04
  • @LeCintas I can edit now the fields but now throws up an error in my controller. `"message":"Undefined index: id","status_code":500,......` –  Apr 27 '18 at 05:34
  • @ramedju this is a different problem now: this is an internal server error. I guess you are sending incomplete datas to your server – Victor Castro Apr 27 '18 at 06:56
  • @LeCintas Please see my updated question above on where can I possibly use your suggestions, I'm currently surrounded with cob webs and I don't know already where to proceed. –  Apr 27 '18 at 07:26
  • The template of your application is not handled by your server/controller. The problem is located in your controller in Laravel. What is the complete error message ? – Victor Castro Apr 27 '18 at 07:45
  • @LeCintas Is this a method or should be under `data()` : `data: { dataEditMode = false, },` and this somewhat errors in my compiler. –  Apr 27 '18 at 09:41
  • While I can appreciate the separation of duties in this solution, I still believe if the dev does not care if the prop is changed by the child component and is only interested in one way communication this should be a design decision. The child can change the prop all day long, I don't care. I'm only interested in letting it know if the parent changes it. To add all of this extra code just to stay within the confines of proper "one way" binding seems like overkill to me. Vue.config.silent option kills those warnings (and all others) if you want to get rid of them. – Craig Nov 29 '21 at 20:17
0

I would send back an event to the parent so it could modify its value:

For example (not tested):

Child Component

props:{
    editmode:{
        type: Boolean,
        default: false,
    }
},
methods:{
    toggleM(){
        var editmode = !this.editmode;
        this.$emit('changeEditMode', editmode);
        if (editmode == false){
            //dothis
        } else {
            //dothat
        }
    },
}

Parent

<child-component @changeEditMode="editModeChanged" :editmode="editmode"></child-component>

...

methods:{
    editModeChanged(value){
        this.editmode = value
    },
}
CUGreen
  • 3,066
  • 2
  • 13
  • 22
  • Editmode toggles but I can't edit the fields :S . It doesn't even get into the method `editModeChanged`. –  Apr 27 '18 at 02:06
  • Please see my problem when using the above code here https://stackoverflow.com/questions/50055013/maximum-call-stack-warn-vue –  Apr 27 '18 at 05:11