Search code examples
javascriptvue.jsvuejs2vue-componentvuex

Is mutating a property of a prop with v-model bad practice?


The vue.js docs are quite strict in saying that mutating props inside of the component is bad practice. Also there are two cases described, in which a developer may be tempted to break this rule:

  • When setting a default value
  • When the given prop values should be mutated.

Since I started with vue.js I found myself tempted updating a props property using v-model. Seaching the web I found several discussions which included workarounds or use vuex.

My question is: Is updating a property of a prop using v-model OK or is BAD because it alters parent state. Imo I want to alter the parent state, consider this example

A Crud component, that loads a specific "form" component:

<template>
    <awesome-form @formSubmit="onFormSubmit" :entity="entity"></awesome-form>
</template>

<script>
import AwesomeForm from 'SomeForm'
export default {
  data () {
    return {
      entity: {}
    }
  },
  components: {
    AwesomeForm
  },
  methods: {
    onFormSubmit () {
      axios.post('/backend', this.entity)
    }
  }
}
</script>

The specific form component:

<template>
    <form @submit.prevent="submit">
        <input type="text" name="username" v-model="entity.username" />
        ...
    </form>
</template>
<script>
  export default {
    props: {
      entity: {
        type: Object
      }
    },
    methods: {
      submit () {
        this.$emit('formSubmit')
      }
    }
  }
</script>

This works like intended and makes data binding super easy. But I guess this is considered bad, as it updates parent state. But in the end of the day this is exactly what i want.

Is this really bad and what are the alternatives?

  • Having events for each update and single property (super redundant)
  • Always use vuex ... (really?)

Solution

  • I see that the best practice in your case is using .sync modifier as mentioned in official docs :

    // ignore the following two lines, they just disable warnings in "Run code snippet"
    Vue.config.devtools = false;
    Vue.config.productionTip = false;
    
    Vue.component('awesome-form', {
      props: ['entity'],
      template: `
      <div>
      <form @submit.prevent="submit">
        <input type="text" class="form-control" name="username" v-model="entity.username">
        <input type="email" class="form-control" name="email" v-model="entity.email">
        <input type="submit" class="btn btn-primary" value="submit"/>
        </form>
      </div>`,
    
      methods: {
        submit() {
          this.$emit('form-submit')
        }
      }
    })
    
    
    new Vue({
      el: "#app",
      data() {
        return {
          entity: {
            username: '',
            email: ''
          }
        }
      },
    
      methods: {
        onFormSubmit() {
          console.log(this.entity)
        }
      }
    });
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
    
    <div id="app">
      <awesome-form @form-submit="onFormSubmit" :entity.sync="entity"></awesome-form>
      <p> {{entity}}</p>
    </div>