3

I have a dialog component:

<div v-if="dialog">
    <p>Worlds smallest dialog</p>
    <button @click="dialog = false>Close Dialog</button> // Avoid mutating props directly...
</div>
//Instance options:
props: {
    dialog: {
        type: Boolean,
        default: false
    }

}

If I want to use it within a parent component:

<button @click="dialog = true">Open Dialog</button>
<dialog :dialog="dialog"></dialog>
// Data object:
data: {
    dialog: false,
}

This works, I mean the application doesn't break. But I get warned that I should rather declare a data/computed property; which is odd because if I want to mutate 5-7 props, for each of them I will have to declare a data property on the child component too? If the parent has x, y, z data props, then the child should also have them declared if I want to mutate them on the child? Are we not repeating ourselves? How can I prevent this repetition?

Tanmay
  • 3,009
  • 9
  • 53
  • 83
  • 1
    I would also point you to this question: [Vue 2 - Mutating props vue-warn](https://stackoverflow.com/q/39868963/691711) because it covers a lot of the same things I mentioned, including the "anti-pattern" note. – zero298 Aug 31 '18 at 16:03

1 Answers1

3

Both the parent and child should have the data/props declared independently from one another. The idea is that you are separating the concerns of each component. Only the component knows what it actually needs. If you remove or add properties to child or parent, both implementations change since they are directly coupled by data/props. By declaring in both, changing one doesn't immediately break the API of both components.

Additionally, mutating props that are injected into a component directly is considered an anti-pattern for a few reasons. Consider this from the documentation Prop Mutation:

Mutating a prop locally is now considered an anti-pattern, e.g. declaring a prop and then setting this.myProp = 'someOtherValue' in the component. Due to the new rendering mechanism, whenever the parent component re-renders, the child component’s local changes will be overwritten.

One of Vue's mantras is "props down, events up". Events don't care about parental implementation, they fire regardless of whether or not the parent is listening. This further eases the case where the data of a parent changes because you, again, don't have to change the props of a child to prevent API breakage.

In your example, the child dialog that has the <button> shouldn't set whether the dialog is visible or not directly. It should propagate up an event to the parent component and tell the parent "The close button was clicked, consider that as you will." The parent should then make the decision to set the dialog prop to false or not.


Here's an example where the dialog component emits a "close" event and doesn't need to take in a prop at all:

Vue.component('my-dialog', {
  template: `
    <div>
      <p>Worlds smallest dialog</p>
      <button @click="$emit('close')">
        Close Dialog
      </button>
    </div>
  `
})

new Vue({
  el: '#app',
  data() {
    return { dialog: false }
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>

<div id="app">
  <button @click="dialog = true">Show Dialog</button>
  <my-dialog v-show="dialog" @close="dialog = false">
  </my-dialog>
</div>
tony19
  • 125,647
  • 18
  • 229
  • 307
zero298
  • 25,467
  • 10
  • 75
  • 100
  • great answer! if you don't mind, I'll edit in an example (which i think are always helpful) – thanksd Aug 31 '18 at 16:10
  • Thanks a lot both of you. But what if the child component's `div` actually requires a `v-model="dialog"` instead of a `v-if="dialog"`. For the sake of brevity I transformed my code from a [vuetify](https://vuetifyjs.com/en/components/dialogs#usage--1) template into a basic html. – Tanmay Aug 31 '18 at 16:30
  • @Eisenheim Ah, that actually changes things a little bit. You should read this section on using custom components with `v-model`: [v-model with Components](https://vuejs.org/v2/guide/forms.html#v-model-with-Components). You will still use events, but you will use a value bind as well. – zero298 Aug 31 '18 at 17:35