Search code examples
javascriptvue.jsvuejs3v-model

Why does @change or @input work with custom v-model component?


Let's say I've created a custom component Comp.vue that basically is an wrapped input element with a model-value prop and @update="$emit('update:modelValue', $event.target.value).

It might look like this:

<template>
  <div>
  <label :for="name" class="block relative">
    <div>
        {{ label }}
    </div>
        <input
          :name="name"
          :type="type"
          :value="modelValue"
          @input="$emit('update:modelValue', $event.target.value)"
        />
  </label>
    </div>
</template>

<script>

export default {
  props: {
    label: {
      required: true,
      type: String,
    },
    modelValue: {
      required: false,
      default: "",
      type: [String, Number],
    },
    type: {
      default: "text",
      type: String,
    },
    name: {
      required: false,
      type: String,
      default: "",
    },
  },
  emits: ["update:modelValue"],
};
</script>

Now I've imported that component in App.vue.

Why does this work:

<Comp @change="doSth1()" />
<Comp @input="doSth2()" />

My custom Comp.vue never emits any of those events.

I've created a custom playground that logs those events: Playground Link


Solution

  • UPDATE 2 to clarify the listening on native DOM events

    The native DOM events are normally propagated to parent elements.

    So, all normal HTML elements inside your Vue component are generating native DOM events and you can listen to them outside your component.

    That's why beforeinput event is triggering by the component if you have an input element inside it.

    Check the JavaScript: Understanding the Event Propagation for more details.


    Since input and change are native DOM events, Vue listens to them automatically.

    Please check the Vue docs: Directives, v-on and HTMLElement, input events

    When used on a normal element, it listens to native DOM events only. When used on a custom element component, it listens to custom events emitted on that child component.

    It looks like Vue still listens to native HTML input events by custom components. See my UPDATE 2 above.

    For example, there is also an beforeinput event which is also fired automatically and listened by Vue. So you can add a directive for it also.

    @beforeinput="beforeinputTriggered()"
    

    UPDATE 1 to answer @Thibault

    The changing of modelValue themself does not fire 'input/change' events. If we remove the HTML input element in the Comp.vue, then the 'beforeinput/input/change' events will be not fired.

    Here is the proof: same component with a +1 button fires only the @update:modelValue event.

    enter image description here