42

I'm trying to use a data coming from a prop with v-model, the following code works, but with a warning.

<template>
<div>
       <b-form-input v-model="value" @change="postPost()"></b-form-input>
</div>
</template>
<script>
    import axios from 'axios';
    export default {
        props: {
            value: String
        },
        methods: {
            postPost() {
                axios.put('/trajectory/inclination', {
                    body: this.value
                })
                    .then(response => {
                    })
                    .catch(e => {
                        this.errors.push(e)
                    })
            }
        }
    }
</script>

The warning says:

"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"

So I changed and now I'm using a data as the warning says.

<template>
<div>
       <b-form-input v-model="_value" @change="postPost()"></b-form-input>
</div>
</template>
<script>
    import axios from 'axios';

    export default {
        props: {
            value: String
        },
        data() {
            return {
                _value: this.value
            }
        },
        methods: {
            postPost() {
                axios.put('/trajectory/inclination', {
                    body: this._value
                })
                    .then(response => {
                    })
                    .catch(e => {
                        this.errors.push(e)
                    })
            }
        }
    }

So now the code it's not working and the warning says:

"Property or method "_value" is not defined on the instance but referenced during render. Make sure to declare reactive data properties in the data option"

Any idea how to fix the first code to suppress the warning? (or some idea on how to fix the second code?)

Obs.: b-form-input it's not my componente, this is the Textual Input from Boostrap-Vue (Doc for b-form-input)

7 Answers 7

46

Answer is from https://github.com/vuejs/vue/issues/7434

Props are read-only, but you are trying to change its value with v-model. In this case, if you change the input value, the prop is not modified and the value is restored on the next update.

Use a data property or a computed setter instead:

computed: {
  propModel: {
    get () { return this.prop },
    set (value) { this.$emit('update:prop', value) },
  },
},

https://v2.vuejs.org/v2/guide/computed.html#Computed-Setter

Sign up to request clarification or add additional context in comments.

3 Comments

Also didn't Vue2 say that best practise one shouldn't be using $emit?
what an awful reply, just copy-pase from a github issue. how to use this in a class-based component? what is a data property?
@Phil for using computed in class component look at the docs
27

Bert addresses your direct issue, but I think you should also know that your approach is a bit off. Since ultimately you are sending the new value to postPost, you don't really need to modify your local copy. Use the event object that is sent to the change handler to get the current value from the input.

Instead of v-model, just use :value, and don't include the invocation parentheses when specifying the change handler.

<template>
<div>
       <b-form-input :value="value" @change="postPost"></b-form-input>
</div>
</template>
<script>
    import axios from 'axios';
    export default {
        props: {
            value: String
        },
        methods: {
            postPost(event) {
                axios.put('/trajectory/inclination', {
                    body: event.target.value
                })
                    .then(response => {
                    })
                    .catch(e => {
                        this.errors.push(e)
                    })
            }
        }
    }
</script>

3 Comments

Thank you for your help, I think your approach is a good one for getting the modified value. I would like to add something that I found If someone wants to change the value from the "prop" (like a two-way binding) the correct approach will be using the modifier sync link and not the v-model
.sync is very similar to v-model for components. You can use either one. v-model is specifically for value props, while .sync can be used with a prop of any name.
To help anybody, I was facing the same issue. I just changed my var that was inside v-model="" from props array to data.
11

One general workaround is to introduce a data-variable and watch the props to update-variable. This is quite subtle and so easy to get wrong so here's an example with a Vuetify modal using v-model (the same technique, in theory, should work with <input> and others):

<template>
  <v-dialog v-model="isVisible" max-width="500px" persistent>
  </v-dialog>
</template>

<script>
export default {
  name: 'Blablabla',
  props: {
    visible: { type: Boolean, required: true }
  },
  data() {
    isVisible: false
  },
  watch: {
    // `visible(value) => this.isVisible = value` could work too
    visible() {
      this.isVisible = this.$props.visible
    }
  }
}
</script>

Comments

10

_ prefixed properties are reserved for Vue's internal properties.

Properties that start with _ or $ will not be proxied on the Vue instance because they may conflict with Vue’s internal properties and API methods.

Try changing _value to something that doesn't start with an underscore.

2 Comments

Thanks :) I found it in the API docs and added to the answer.
Thank you for help Bert, changing the variable name makes it work like a charm. But as Roy mentioned in his answer maybe my approach using local variables was a bit off and as this question will be available for others maybe will be best for them to follow the other approach.
9

The official Vue docs shows how to use v-model on a custom component: https://v2.vuejs.org/v2/guide/components.html#Using-v-model-on-Components

TL;DR:

You simply need to have a specifically named value prop, and emit an input event which the v-model when you instantiate the component maps for you.

More info on how this works on the link above.

<template>
  <input
    type="text"
    :value="value"
    @input="$emit('input', $event.target.value)"
  />
</template>

<script>
export default {
  name: "Input",
  props: {
    value: String,
  },
};
</script>
<Input v-model="searchText"></Input>

Comments

0

Point your input v-model directive to a data property named value_ (or any other name not starting with prefixes _ or $ which are reserved by Vue). Set the data property's default value to null. Then, add a method getValue() which will set property value_ based on your value prop's value. Finally, call getValue() in Vue's created() lifecycle hook. Like so:

<template>
  <div>
    <b-form-input v-model="value_" @change="postPost()">
    </b-form-input>
  </div>
</template>

<script>
  import axios from 'axios';
  
  export default {
    data: () => ({
      value_: null
    }),
    props: {
      value: String
    },
    methods: {
      postPost() {
        axios.put('/trajectory/inclination', {
          body: this.value_
        })
        .then(response => {
        })
        .catch(e => {
          this.errors.push(e)
        })
      },
      getValue() {
        this.value_ = this.value;
      }
    },
    created() {
      this.getValue()
    }
  }
</script>

Comments

0

You can use a data like below.

<template>
  <input type="text" v-bind:value="value" v-on:input="dValue= $event.target.value" />
</template>
        
 <script>
 export default {
  props: ["value"],
  data: function () {
    return {
      dValue: this.value,
    };
  },
  methods: {
    alertValue() {
     alert("Current Value" + this.dValue);
    },
  },
};
</script>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.