如何将@blur 传递给子组件 Vuejs

How to pass @blur into child component Vuejs

我创建了一个自定义输入组件。

我需要将 @blur 从 vee-validate 表单传递到我的自定义输入组件。

在普通 html 输入标签中效果很好。我不知道我们如何将 @blur 传递到自定义输入组件中。

示例 1 工作正常,它在模糊输入后触发了验证。

 <template>
  <form @submit="onSubmit">
    <input @blur="emailBlur" v-model="email" type="text" autocomplete="off" name="email" placeholder="email">
    <button type="submit" :disabled="isSubmitting">Submit</button>
  </form>
 </template>

带有我的自定义输入组件的示例 2:

// src/components/Input.vue
<template>
  <div class="mt-2">
    <label :for="name" class="h5">Name</label>
    <input
      :type="type"
      :value="modelValue"
      @change="$emit('update:modelValue', $event.target.value)"
      :id="name"
      :placeholder="placeholder"
      />
  </div>
</template>

<script>
export default {
  name: 'Input',
  props: ["modelValue", 'name', 'type', 'placeholder'],
  setup(props) {
    console.log('props :>> ', props); // not receive the @blur 
  }
}
</script>

父组件(App.vue):

<template>
  <form>
   <Input @blur="emailBlur" v-model="email" type="text" name="email" placeholder="Custom input email" />
    <button type="submit" :disabled="isSubmitting">Submit</button>
  </form>

</template>

export default {
  name: 'App',
  components: {
    Input
  },
  setup() {
     // the vee validation values v-model to template
  }
}

将处理程序作为 props 从父组件发送不是一个好习惯,而是需要从子组件触发处理程序(存在于父组件中)。通过这样做,您将拥有一个优势,您可以根据不同父组件中的要求绑定不同的模糊处理程序

为此,您可以按照以下方法进行

自定义输入组件

// src/components/Input.vue
<template>
  <div class="mt-2">
    <label :for="name" class="h5">Name</label>
    <input
      :type="type"
      :value="modelValue"
      @change="$emit('update:modelValue', $event.target.value)"
      :id="name"
      :placeholder="placeholder"
      @blur="$emit('blur')" //Change added
      />
  </div>
</template>

<script>
export default {
  name: 'Input',
  props: ["modelValue", 'name', 'type', 'placeholder'],
  emits: ['blur', 'update:modelValue'], // change added
}
</script>

注:

对于所有没有参数的 v-models,确保将道具和事件名称分别更改为 modelValue 和 update:modelValue

例如:

Parent.vue

<ChildComponent v-model="pageTitle" />

在Child.vue中应该是

export default {
  props: {
    modelValue: String // previously was `value: String`
  },
  emits: ['update:modelValue'],
  methods: {
    changePageTitle(title) {
      this.$emit('update:modelValue', title) // previously was `this.$emit('input', title)`
    }
  }
}

您正在创建的通常称为“透明包装器”组件。您希望此包装器在几乎所有方面都像普通 input 组件一样工作,以便该组件的用户可以使用它 原样 普通 input (但它不是)

在您的情况下,您希望将 @blur 事件侦听器附加到包装器。但问题是 blur 是本机浏览器事件,即不是 Vue 事件。

当您为未在 emits option (or v-bind an attribute that is not specified in component's props), Vue will treat it as Non-Prop Attribute 中指定的事件在组件上放置事件侦听器时。这意味着它采用所有此类事件侦听器和非道具属性并将其放置在组件的 根节点 上(在您的情况下为 div

但幸运的是,有一种方法可以告诉 Vue“嘿,不要把它们自动放在根目录下,我知道放在哪里”

  1. 在您的组件上使用 inheritAttrs: false option
  2. 将所有非道具属性(包括事件侦听器)放在您想要的元素上 - input 在这种情况下 - 使用 v-bind="$attrs"

现在你甚至可以删除一些道具 - 例如 placeholder(如果你愿意),因为如果你直接在你的组件上使用它,Vue 将它放在 input 上,这就是你想要...

另外 处理 @change 事件不是最优的 - 您的组件允许指定 typedifferent input types has different events。绕过它的好技巧是不要显式传递 value 和绑定事件,而是将 v-modelcomputed 一起使用(参见下面的示例)

const app = Vue.createApp({
  data() {
    return {
      text: ""
    }
  },
  methods: {
    onBlur() {
      console.log("Blur!")
    }
  }
  
})

app.component('custom-input', {
  inheritAttrs: false,
  props: ["modelValue", 'name', 'type'],
  emits: ['update:modelValue'],
  computed: {
    model: {
      get() { return this.modelValue },
      set(newValue) { this.$emit('update:modelValue', newValue) } 
    }
  },
  template: `
  <div class="mt-2">
    <label :for="name" class="h5">{{ name }}:</label>
    <input
      :type="type"
      v-model="model"
      :id="name"
      v-bind="$attrs"
    />
  </div>
  `
})

app.mount("#app")
<script src="https://unpkg.com/vue@3.2.19/dist/vue.global.js"></script>
<div id='app'>
  <custom-input type="text" name="email" v-model="text" placeholder="Type something..." @blur="onBlur"></custom-input>
  <pre>{{ text }}</pre>
</div>