How to use v-model on component in vue 3 script setup
Asked Answered
S

4

26

I want to add a v-model on a component but I got this warning:

[Vue warn]: Component emitted event "input" but it is neither declared in the emits option nor as an "onInput" prop.

Here is my code:

// Parent.vue
<template>
  <h2>V-Model Parent</h2>
  <Child v-model="name" label="Name" />
  <p>{{ name }}</p>
</template>

<script setup>
import { ref } from 'vue'
import Child from './Child.vue'

const name = ref('')
</script>
// Child.vue
<template>
  <input
    class="input"
    type="text"
    :placeholder="props.label"
    :value="props.value"
    v-on:input="updateValue($event.target.value)"
  />
</template>

<script setup>
import { defineProps, defineEmit } from 'vue'
const props = defineProps({
  label: String,
  value: String
})
const emit = defineEmit('input')

function updateValue(value) {
  emit('input', value)
}
</script>

I was trying to reproduce this tutorial but I'am stuck and got no idea what I am missing.

I want to display {{ name }} in the Parent.vue component. Do you got an idea how to solve this?

Splashdown answered 21/3, 2021 at 21:48 Comment(0)
G
45

Version >= 3.3 (stable in version 3.4)

You can directly use defineModel macro (stable in v3.4):

Child.vue :

<script setup>
const modelValue = defineModel()
</script>

<template>
  <input v-model="modelValue" />
</template>

Version < 3.3

In vue 3 value prop has been changed to modelValue and the emitted event input to update:modelValue:

Parent.vue

<Child v-model="name" label="Name" />

Child.vue

<template>
  <input
    class="input"
    type="text"
    :placeholder="props.label"
    :value="props.modelValue"
    v-on:input="updateValue($event.target.value)"
  />
</template>

<script setup>

const props = defineProps({
  modelValue: String
})

const emit = defineEmits(['update:modelValue'])

function updateValue(value) {
  emit('update:modelValue', value)
}
</script>
Guttering answered 21/3, 2021 at 22:8 Comment(7)
This gives typing issues when used with TS, any workaround?Jadotville
which typing issues?Guttering
@input="$emit('update:modelValue', $event)" This should be: @input="(e)=> $emit('update:modelValue', e.target.value)" , but TS complains of the latterJadotville
did you facing this error? #71102319 @Boussadjra BrahimAlluvion
import statement is not needed - vuejs.org/api/sfc-script-setup.html#defineprops-defineemits for typescript to go errorless read this - thisdevbrain.com/how-to-use-v-model-in-vue-3Parsee
@Parsee I removed the import statement, thanksGuttering
hey @BoussadjraBrahim Unless I am doing something wrong, this doesn't seem to work with multiple checkboxes and the v-model on the parent is always a single value of whatever the last checked checkbox was. Do you know what might be missing?Shepperd
W
15

I like to use with computed as well

<template>
  <div>
    <input v-model="model">
  </div>
</template>

<script setup>
import { computed } from 'vue'

const props = defineProps({
  modelValue: {
    type: [String, Number],
    default: ''
  }
})

const emit = defineEmits(['update:modelValue'])

const model = computed({
  get () {
    return props.modelValue
  },

  set (value) {
    return emit('update:modelValue', value)
  }
})
</script>
Wavell answered 29/12, 2021 at 16:33 Comment(0)
H
2

I have the similar issues and finally I got it work. Here are one solution for one or multiple checkbox for Vue 3 and TypeScript.

solution : for one or multiple checkbox

CheckBox Component:

        <template>
          <input
            type="checkbox"
            :value="inputValue"
            :disabled="isDisabled"
            v-model="model"
            :class="[defaultClass, inputClass, checkboxClass]"
          />
        </template>
        
    <script lang="ts">
        import { defineComponent, PropType } from 'vue';
        
        export default defineComponent({
          components: {},
        
          props: {
            inputValue: {
              type: String,
              required: false,
              default: '',
            },
        
            modelValue: {
              type: [Object, Boolean] as PropType<String[] | Boolean>,
              required: false,
              default: (() => ({})) || false,
            },
        
            isDisabled: {
              type: Boolean,
              required: false,
              default: false,
            },
        
            checkboxClass: {
              type: String,
              required: false,
              default: '',
            },
          },
        
          data() {
            return {
              defaultClass: 'h-4 w-4 rounded text-primary shadow-sm',
            };
          },
        
          emits: ['update:modelValue'],
        
          computed: {
            model: {
              get() {
                return this.modelValue;
              },
              set(value) {
                this.$emit('update:modelValue', value);
              },
            },
        
            inputClass() {
              if (this.isDisabled) {
                return 'bg-dark-17 border-dark-13';
              }
              return 'bg-dark-23 border-dark-10 hover:bg-dark-25 focus:border-primary';
            },
          },
        });
        </script>

import CheckBox and use it

import CheckBox in other components;

    <div>
      <div v-for="(option, index) in options" :key="index">
        <div
          class="flex items-center justify-between p-6 py-4 border-b border-b-dark-13"
        >
          <div class="w-10">
            <Checkbox :inputValue="option.name" v-model="selectedOptions" />
          </div>
        </div>
      </div>
    </div>
    
      data() {
        return {
          selectedOptions: [],
        };
      },
Hyaline answered 28/3, 2022 at 20:12 Comment(2)
This solution is not in the vue 3 script setup as requested by the question. Do you have an example of this with setup? as the chosen answer doesn't work for me.Shepperd
@ToddPadwick, Its working for me with script setup typescriptCephalochordate
W
0

The only thing that worked for me (vue 3.3.x) After days and hours of searching: Parent:

<component v-model:model-value="someModel">

Child:

const emit = defineEmits<{
  (event: 'update:modelValue', payload: string): void;
}>();
const props = defineProps({
  modelValue: String
})
<template>
    <input type="text"
           :value="props.modelValue"
           @input="$emit('update:modelValue', $event.target.value)">
</template>

For some components I even had to do something like this: (BTW: vue properties are read-only, this is a way to copy the value and make it muteable)

const props = defineProps(['selected'])
const selected = ref()
watch(() => props.selected, (newValue) => {
  selected.value = newValue;
}, {deep: true, immediate: true});

Whitefish answered 12/10, 2023 at 18:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.