27

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?

4 Answers 4

49

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>
Sign up to request clarification or add additional context in comments.

7 Comments

This gives typing issues when used with TS, any workaround?
which typing issues?
@input="$emit('update:modelValue', $event)" This should be: @input="(e)=> $emit('update:modelValue', e.target.value)" , but TS complains of the latter
did you facing this error? stackoverflow.com/questions/71101819/… @Boussadjra Brahim
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-3
|
17

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>

Comments

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: [],
        };
      },

2 Comments

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.
@ToddPadwick, Its working for me with script setup typescript
1

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});

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.