13

I'm trying to create a modal but I'm getting this error only when I close it:

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

found in

---> <PanelDesconectarModal> at resources\assets\vue\PanelDesconectarModal.vue
       <VNavigationDrawer>
         <PanelDrawer> at resources\assets\vue\PanelDrawer.vue
           <VApp>
             <PanelRoot> at resources\assets\vue\PanelRoot.vue
               <VApp>
                 <Root>

PanelDesconectarModal.vue

<template>
    <v-dialog v-model="value" max-width="350">
        <v-card :dark="($theme === 'dark')">
            <v-card-title class="headline">Desconectar</v-card-title>
            <v-divider></v-divider>
            <v-card-text>Você tem certeza que deseja desconectar-se?</v-card-text>
            <v-card-actions>
                <v-spacer></v-spacer>
                <v-btn flat @click.native="closeDialog">Cancelar</v-btn>
                <v-btn :color="$color" flat="flat" @click.native="desconectar">Desconectar</v-btn>
            </v-card-actions>
        </v-card>
    </v-dialog>
</template>

<script>
    export default {
        name: 'panel-desconectar-modal',
        props: ['value'],
        methods: {
            closeDialog() {
                this.value = false;
            },
            desconectar() {
                this.closeDialog();

                window.location = this.$route + '/panel/desconectar';
            }
        }
    }
</script>

Using ProgressDesconectarModal.vue, showDesconectar is a data variable

<panel-desconectar-modal :value="showDesconectar"></panel-desconectar-modal>
Rafael de Azeredo
  • 453
  • 4
  • 8
  • 14
  • I already [answered](https://stackoverflow.com/a/48503369/1981247) similar question, but can't link it because for some reason OP didn't vote on it. – Traxo Mar 16 '18 at 08:22

4 Answers4

24

This happens because you have props value in your v-model.

Do not do that, as that will mutate the prop(value) when v-model changes (you should only change data values with v-model afaik, but in this case, you don't even need additional data variable).

Since vuejs v2.3.0, it is suggested to emit value to the parent, so that parent changes it, and it is then passed to child component.


So all you have to do is:

in v-dialog component

remove v-model and replace it with :value="value" @input="$emit('input')"

And your function:

closeDialog() {
    this.$emit('input');
}

In panel-desconectar-modal component use v-model="showDesconectar".


This will work because:

<input v-model="something"> is syntactic sugar for:

<input   
    v-bind:value="something"
    v-on:input="something = $event.target.value">

Here is working example pen which I provided in an answer to similar question.

tony19
  • 125,647
  • 18
  • 229
  • 307
Traxo
  • 18,464
  • 4
  • 75
  • 87
  • It worked thank you very much, I'm still learning VueJS and I have not mastered it yet – Rafael de Azeredo Mar 16 '18 at 17:36
  • 1
    I found that with v-dialog it is better to have `@input="$emit('input', $event)"`, so that the parent value will be set to false instead of undefined when the dialog closes – LorTush Jan 09 '19 at 21:54
2

You should not mutate the props in your child component. You can only mutate the object but not primitives. So, you can use data option or a computed property:

data() {
  return {
    childValue: this.value; // initialize props value
  }
}

Now, you can change the childValue:

closeDialog() {
   this.childValue = false;
},

Make sure to use childValue everywhere inside your child component instead of value props.

Bhojendra Rauniyar
  • 83,432
  • 35
  • 168
  • 231
  • I think there is no need for `data`, see my [answer](https://stackoverflow.com/a/49316078/1981247) – Traxo Mar 16 '18 at 08:33
1

This is really the moment to ask yourself "Do I really need a prop? Can I do this with data? Am I here because I mistakenly put some state in Vue component?"

If you're the author of the page and the component, and the component only appears once on the page, there is no good reason to use props. If you need props because the component is repeated for all the lines in an array, make the prop just the array index, so the component can directly modify the source array in the store. Vue components should not contain state, especially state that needs to be shared, and do not enjoy being tightly bound to each other. The parent-child relationship arises out of the chance of their placement in the DOM tree, (children occur inside the markup of parents). This is like a chance meeting in a nightclub. The child may have nothing else to do with the parent. The hierarchy of your source data should be expressed, independently of your markup, in the structure of your store. Your Vue components, where possible, should have an intimate two way relationship with the store, and not talk much to each other :-)

bbsimonbb
  • 27,056
  • 15
  • 80
  • 110
  • However, in OPs case it's possible to use `v-model` like I've suggested. Yes, `v-model` implies `prop`, but most likely parent component will need that data (i.e. `showDesconectar`) because in parent component there is most likely a button that opens modal (so state needs to be shared, and I guess only between those 2 components?). I wonder if you consider that a "bad practice", and is there any working example which shows some alternatives? I didn't quite get what you mean (if you were implying that something OP is doing is so to say bad practice) so I'd appreciate to see how would you do it. – Traxo Mar 16 '18 at 09:57
  • We currently attack our modal dialog component with a method call that returns a promise. The syntax is nice, but it means the state of the dialog is [no longer in the store](https://stackoverflow.com/questions/48748309/vue-where-does-this-code-go). I like your answer. Why should we only use v-model for form inputs. What is $event.target when the event is emitted by a Vue component. I need to fiddle with this! – bbsimonbb Mar 16 '18 at 11:26
  • > `What is $event.target when the event is emitted by a Vue component.` - Note that this is specifically `vuetify.js` component, so my guess is that it depends on the custom component, which event it emits, and when. – Traxo Mar 16 '18 at 11:38
  • Yup. The example works because dialog gets set to "" and "" is false. – bbsimonbb Mar 16 '18 at 11:40
  • One more thought: `Your Vue components, where possible, should have an intimate two way relationship with the store, and not talk much to each other`- afaik its possible for each component to communicate with the store, thus you are essentially saying -> "components should not talk to each other directly at all". So I would disagree with that. I like your point that components should talk to each other via store, but that's perhaps an overkill and possibly confusing in this very straightforward parent-child cases. – Traxo Mar 16 '18 at 11:46
-1

In the relevant Vue doc they don't have examples with default values, so these answers are to be found elsewhere. I needed a solution to create a component with a default value, but the input would always spring back to what it was before when it loses focus, or it gave the "avoid mutating a prop directly" error. Creating a mutable property and setting its value in the created event solved it for me:

data()
{
    return {
        text: null
    };
},

props: {
    properties: Object,
    value: String
},

created()
{
    this.text = this.value;
}
tony19
  • 125,647
  • 18
  • 229
  • 307
Rubem Pechansky
  • 185
  • 2
  • 7