vue.jsvuejs3vue-composition-apitwo-way-binding

Two Way Binding with Ref in Composition API


I'm new to VUE and trying to learn composition API.

I'm trying to write a simple two-way binding for a text box, but the value under the {{xyz}} does not update, am I doing something wrong?

I believe for Vue2 it was just having a v-bind and {{}} for this.

App.vue

<template>
  <div>
    Original Text Box
    <textBox :value="text"></textBox>

    Two Way Binding:
    {{ text }}
  </div>
</template>

<script lang="ts">
import { defineComponent, ref } from 'vue'
import textBox from '../src/components/HelloWorld.vue'

export default defineComponent({
  name: 'HomePage',
  components: {
    textBox
  },

  setup() {
    const text = ref('Inital Value')
    return { text }
  }
})
</script>

<style scoped>
</style>

HellowWorld.vue

<template>
  <div>
    <textarea v-model="props.value"></textarea>
  </div>
</template>

<script lang="ts">
import { defineComponent } from 'vue'

export default defineComponent({
  name: 'TEXTBOX',
  props: { value: String },
  setup(props) {
    console.log(props.value)
    
    return { props }
  }
})
</script>

<style scoped>
</style>

Output: When the app loads

When the app loads

After updating the text value:

enter image description here


Solution

    1. Use v-model and modelValue for the two-way binding.

    2. Don't use props for two-way binding, since they are readonly.

    3. Check the Vue Docs Component v-model to understand how v-model two-way binding works with components.

    4. Do not return { props } from setup()

    Playground

    const { createApp, ref } = Vue;
    
    const TextBox = {
      props: ['modelValue'],
      emits: ['update:modelValue'],
      template: `<div>
        Original Text Box <br/>
        <textarea :value="modelValue" @input="$emit('update:modelValue', $event.target.value)"></textarea><br/>
        Two Way Binding:<br/>
        {{ modelValue }}
      </div>`
    }
    
    const App = { 
      components: { TextBox },
      setup() {
        const text = ref('Inital Value')
        return { text }
      }  
    }
    const app = createApp(App)
    app.mount('#app')
    #app { line-height: 2; }
    [v-cloak] { display: none; }
    <div id="app" v-cloak>  
      <text-box v-model="text"></tetx-box> 
    </div>
    <script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>