Search code examples
javascriptvue.jstwo-way-binding

Two way data binding in Vue: Unable to update the Parent component from the child component


I got the following two components:

Parent:

<template>
  <v-container>
    <v-row class="text-center">
      <v-col cols="12" class="parent">
        <p>Ich bin der Parent component</p>
        <button @click="changeDetail" :name.sync="name">Change Details</button>
        <Child v-bind:name="name"></Child>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
import Child from "./Child";
export default {
  name: "Parent",

  data: () => ({
    name: "test"
  }),
  methods: {
    changeDetail() {
      this.name = "Updated from Parent";
    }
  },
  components: {
    Child
  }
};
</script>

Child:

<template>
  <v-container>
    <v-row class="text-center">
      <v-col cols="12">
        <p>My name is: {{ name}}</p>
        <button @click="resetname">Reset the name</button>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  //   props: ["name"],
  props: {
    name: {
      type: String,
      required: true
    }
  },
  data: () => ({
    newname: "Updated from Child"
  }),
  methods: {
    resetname() {
      this.$emit("update:name", this.newname);
    }
  }
};
</script>

As far as I read here: https://v2.vuejs.org/v2/guide/components-custom-events.html#sync-Modifier, I should use update and sync to pass props from the child back to the parent. However it does not work. I don´t understand what´s wrong here. What am I missing?


Solution

  • It is usually best to not bind your template to the prop but a computed property instead to ensure the data is accessed and modified externally. It will also simplify your code a bit so that you don't have to trigger updates.

    Parent:

    <template>
      <v-container>
        <v-row class="text-center">
          <v-col cols="12" class="parent">
            <p>Ich bin der Parent component</p>
            <button @click="changeDetail">Change Details</button>
            <Child v-bind:name.sync="name"></Child>
          </v-col>
        </v-row>
      </v-container>
    </template>
    
    <script>
    import Child from "./Child";
    export default {
      name: "Parent",
    
      data: () => ({
        name: "test"
      }),
      methods: {
        changeDetail() {
          this.name = "Updated from Parent";
        }
      },
      components: {
        Child
      }
    };
    </script>
    

    Child:

    <template>
      <v-container>
        <v-row class="text-center">
          <v-col cols="12">
            <p>My name is: {{ currentName }}</p>
            <button @click="resetname">Reset the name</button>
          </v-col>
        </v-row>
      </v-container>
    </template>
    
    <script>
    export default {
      //   props: ["name"],
      props: {
        name: {
          type: String,
          required: true
        }
      },
      data: () => ({
         //Be careful with fat arrow functions for data
         //Because the value of *this* isn't the component, 
         //but rather the parent scope.
      }),
      computed: {
        currentName: {
            get() { return this.name },
            set(value) { this.$emit("update:name", value); }
        }
      },
      methods: {
        resetname() {
          this.currentName = "updated from child";
        }
      }
    };
    </script>