Привязка событий к пользовательскому компоненту

#vue.js #vuejs3

Вопрос:

Использование Vue3

Я создаю пользовательский компонент и хотел бы привязать события к входным данным внутри него. Поэтому в этом случае я хотел бы сделать что-то подобное, как я делаю с v-bind="inputProps" этим . Как это делается, сейчас $listeners удаляются? Как вы, вероятно, видите, я не в состоянии использовать v-bind="$attrs" , поскольку свойства разделены на то, куда они «идут».

Я здесь, чтобы решить проблему, но любые намеки на улучшение всегда являются приятным бонусом.

// c-текст.vue

 <template>
  <input
    v-if="useAsSingleElement"
    v-bind="inputProps"
    :class="['c-text', 'c-text--is-single-element']"
    @input="$emit('input', $event)"
    @blur="$emit('blur')"
    @focus="$emit('focus')">

  <c-form-item-shell
    v-else
    :id="id"
    :is-required="isRequired"
    :label-text="labelText"
    :icon="icon"
    :icon-class="iconClass"
    :errors="errors"
    :information="information">
    <template #element>
      <input
        v-bind="inputProps"
        class="c-text"
        @input="$emit('input', $event.target.value)"
        @blur="$emit('blur')"
        @focus="$emit('focus')">
    </template>
  </c-form-item-shell>
</template>

<script>
import { computed } from 'vue'
import CFormItemShell from '@/components/c-form-item-shell.vue'

export default {
  components: {
    CFormItemShell
  },
  props: {
    useAsSingleElement: { type: Boolean, required: false, default: false },
    name: { type: String, required: true },
    type: { type: String, required: false, default: 'text' },
    placeholder: { type: String, required: false, default: null },
    maxlength: { type: [String, Number], required: false, default: null },
    minxlength: { type: [String, Number], required: false, default: null },
    pattern: { type: String, required: false, default: null },
    readonly: { type: Boolean, required: false, default: false },
    spellcheck: { type: Boolean, required: false, default: false },
    autocomplete: { type: Boolean, required: false, default: true },
    id: { type: String, required: true },
    isRequired: { type: Boolean, required: false, default: false },
    labelText: { type: String, required: false, default: null },
    icon: { type: String, required: false, default: null },
    iconClass: { type: String, required: false, default: null },
    errors: { type: [String, Array], required: false, default: null },
    information: { type: [String, Array], required: false, default: null }
  },
  emits: ['input', 'blur', 'focus'],
  setup (props) {
    const inputProps = computed(() => {
      return {
        id: props.id,
        name: props.name ? props.name : props.id,
        type: props.type,
        placeholder: props.placeholder,
        maxlength: props.maxlength,
        minlength: props.minlength,
        pattern: props.pattern,
        readonly: props.readonly,
        spellcheck: props.spellcheck,
        autocomplete: props.autocomplete ? 'on' : 'off'
      }
    })

    return {
      inputProps
    }
  }
}
</script>
 

Ответ №1:

На основе руководства по миграции :

В виртуальном DOM Vue 3 прослушиватели событий теперь являются просто атрибутами с префиксом on и как таковые являются частью $attrs объекта, поэтому $listeners были удалены.

так что просто проходите свои мероприятия, как onClick onBlur … и

 export default {
  inheritAttrs: false
}
 

Комментарии:

1. Я видел документы, но не знал, как их использовать. Три основные вещи, которые неясны. 1. достаточно ли этого делать <c-text @click="something" /> и не выделять в компоненте? 2. как я могу собрать все события в одно вычисленное? 3. как мне привязать это вычисленное значение к входным данным?

Ответ №2:

Чтобы ответить на мой собственный вопрос и дать более подробный ответ в надежде, что это может помочь кому-либо в будущем. Спасибо Буссаджре Брахиму, чей ответ заставил меня задуматься. Я сделал следующее

  1. добавлен inhertsAttrs: false
    Ссылка на документы

Это делает так , чтобы события (то есть ваши @input и т. @click Д.) в компоненте не применялись к родительскому узлу

  1. добавлен emits: ['update:modelValue', 'blur', 'focus']
    Ссылка на документы

Это должны быть все выбросы, которые вы хотите захватить и выпустить обратно

  1. «Соберите» излучение вручную на мой propsAndListeners объект, как это. Затем привяжите его к входу, например <input v-bind="propsAndListeners" />

Имейте в виду, что вам нужно указать префикс для ваших событий, on поэтому, если у вас есть @click событие, имя onClick которого будет указано в $attrs (@ short is v-on)

 const propsAndListeners = computed(() => {
  return {
    onInput: (event) => emit('update:modelValue', event.target.value)
  }
})
 

В большинстве случаев вы, вероятно, привязали $attrs бы элемент к элементу, но причина в propsAndListeners том, что у меня есть if условие и привязка одних и тех же вещей для ввода в двух местах.

Вот полная часть сценария

 export default {
  components: {
    CFormItemShell
  },
  props: {
    useAsSingleElement: { type: Boolean, required: false, default: false },
    name: { type: String, required: true },
    type: { type: String, required: false, default: 'text' },
    modelValue: { type: [String, Number, Date], required: false, default: null },
    placeholder: { type: String, required: false, default: null },
    maxlength: { type: [String, Number], required: false, default: null },
    minxlength: { type: [String, Number], required: false, default: null },
    pattern: { type: String, required: false, default: null },
    readonly: { type: Boolean, required: false, default: false },
    spellcheck: { type: Boolean, required: false, default: false },
    autocomplete: { type: Boolean, required: false, default: true },
    id: { type: String, required: true },
    isRequired: { type: Boolean, required: false, default: false },
    labelText: { type: String, required: false, default: null },
    icon: { type: String, required: false, default: null },
    iconClass: { type: String, required: false, default: null },
    errors: { type: [String, Array], required: false, default: null },
    information: { type: [String, Array], required: false, default: null }
  },
  emits: ['update:modelValue', 'blur', 'focus'],
  setup (props, { emit }) {
    const propsAndListeners = computed(() => {
      return {
        id: props.id,
        name: props.name ? props.name : props.id,
        type: props.type,
        placeholder: props.placeholder,
        value: props.modelValue,
        maxlength: props.maxlength,
        minlength: props.minlength,
        pattern: props.pattern,
        readonly: props.readonly,
        spellcheck: props.spellcheck,
        autocomplete: props.autocomplete ? 'on' : 'off',
        onInput: (event) => emit('update:modelValue', event.target.value),
        onBlur: (event) => emit('blur', event),
        onFocus: (event) => emit('focus', event)
      }
    })

    return {
      propsAndListeners
    }
  }
}