Vue.js - How to emit to parent component on change in data object?

13,782

Solution 1

You can keep the data in the parent, provide it to the child as a prop, make the child watch the prop and update its internal state, and finally emit an event to the parent when the internal state of the child has been changed.

parent

<template>
  <ChildComponent v-model="myBooleanVar" />
  ...
</template>

<script>
  data()
  {
    return {
      myBooleanVar: false,
    }
  },
  watch:
  {
    myBooleanVar(newValue, oldValue)
    {
      if (newValue !== oldValue) this.getItDone(newValue);
    }
  },
  methods: 
  {
    getItDone(value) 
    {
      ...
    }
  }
</script>  

child

<template>
  <div class="list-item">
    <input type="checkbox" v-model="checked">
    <label :class="{ checked: checked }">{{ name }}</label>
  </div>
  ...
</template>

<script>
  props:
  {
    value:
    {
      type: Boolean,
      default: false
    }
  }
  data() 
  {
    return {
      checked: this.value,
    }
  },
  watch:
  {
    value(newVal, oldVal)
    {
      // this check is mandatory to prevent endless cycle
      if(newVal !== oldVal) this.checked = newVal;
    },
    checked(newVal, oldVal)
    {
      // this check is mandatory to prevent endless cycle
      if(newVal !== oldVal) this.$emit('input', newVal);
    }
  },
</script>

Solution 2

Maybe you can directly bind the checkbox attribut to the parent attribut using v-bind="$attrs"

Take a look at this answer: https://stackoverflow.com/a/56226236/10514369

Share:
13,782
Spectator6
Author by

Spectator6

Updated on June 14, 2022

Comments

  • Spectator6
    Spectator6 over 1 year

    The use of v-model creates a two-way bind between the view and data model WITHIN a component. And a view interaction can emit an event to a parent component.

    It is possible, though, to have a data model change in a child component emit an event to a parent component?

    Because as long as the user is the one clicking the checkbox, things are fine in both the parent and the child (the data model updates AND the event is emitted). But if I pull up the Vue dev tools and toggle the checkbox within the child component's data, the two-way bind from the v-model will make the appropriate updates within the CHILD component, but nothing ever makes it over to the parent component (I suspect because an event is not emitted).

    How can I make sure the parent component "knows" about the data change in the child component? I assume this must be possible in some way... If not emitting from the child, perhaps there's some way to have the parent component "watch" the child component's data?

    Thank you for any help or guidance! I'll keep reading and looking for an answer in the meantime!

    child component

    <template>
      <div class="list-item" v-on:click="doSomething">
        <input type="checkbox" v-model="checked">
        <label v-bind:class="{ checked: checked }">{{ name }}</label>
      </div>
      ...
    </template>
    
    <script>
      ...
      data: function() {
        return {
          checked: false,
        }
      },
      methods: {
        doSomething() {
          ...
          this.$emit('doSomething', this)
        }
      }
    </script>
    

    parent component

    <template>
      <ChildComponent v-on:doSomething="getItDone"></ChildComponent>
      ...
    </template>
    
    <script>
      ...
      methods: {
        getItDone(target) {
          ...
        }
      }
    </script>  
    

    UPDATE: After playing around a bit more with @IVO GELOV's solution, the issue I'm running into now is that, when multiple Child components are involved, since the Parent's one singular value of myBooleanVar drives the whole thing, checking the box of one child component causes all child components to be checked.

    So it's definitely progress in that both view and data manipulations make it over to the parent, but I'm still trying to figure out how to "isolate" the situation so that just the one Child component that was acted upon gets dragged into the party...

  • Spectator6
    Spectator6 about 3 years
    Hello @IVO GELOV! Thank you! So, just to be sure I follow what this is doing... Rather than have the data value "live" within the Child component and try to pass its state UP to the Parent, this goes about it differently. It has the data value's state "live" within the Parent, passes it into the Child, and then, whenever the value changes in the Child, because of the watch: behavior, any value change gets $emited back to the Parent. So the Parent, in a way, is "sharing" its data to be used by the Child. Do I follow that correctly? Wow! What a beautiful solution!
  • Spectator6
    Spectator6 about 3 years
    Thank you for the link @davidr! Together with Ivo's post, these both do a great job of helping me understand Vue's possibilities!
  • Spectator6
    Spectator6 about 3 years
    @IVO_GELOV is there any way to keep it from being universal to all Child components? Whenever I interact with a single Child, all Child component checkboxes end up being toggled. Added an update to the bottom of my OP.
  • IVO GELOV
    IVO GELOV about 3 years
    When you have multiple children - you should use multiple boolean variables in the parent, one for each child. OF course, unless you intentionally want to drive all the checkboxes simultaneously :)
  • Spectator6
    Spectator6 about 3 years
    Hello IVO! Yes, I changed the entire structure of the app to NOT use the boolean as the primary driver. Now it works like a charm! What made your answer "click" for me was reading the Vue documentation on upgrading from Vue 2 to 3 (even though that's not what I was interested in doing). That showed things in a Parent/Child structure, now the rest of the documentation makes much more sense! Thank you again for your help!