Добавление атрибутов Vee-validate / HTML к элементу ввода в слот

в моем приложении у меня много форм, и большинство входов выглядят именно так:

<div class="form-group">
    <label for="language">{{ $t('form.language')}}</label>
    <input type="text" class="form-control" id="language" name="form.language" v-model="language" v-validate.initial="'required'" :data-vv-as="$t('form.language')" />
    <span class="invalid-feedback">{{ errors.first('language') }}</span>
</div>

это повторяется снова и снова. Единственное, что действительно меняется-это имя поля и тип входного сигнала. Иногда это select, а иногда это более сложный компонент вместо простого HTML.

моя идея состоит в том, чтобы создать какой-то компонент фантик. Поэтому мне не нужно копировать все это и просто использовать что-то вроде что:

<form-group name="language">
    <input type="text" v-model="form.language">
</form-group>

Я попытался реализовать его таким образом, но он не работает:

<template>
    <div class="form-group">
        <label :for="name">{{ $t('form.' + name)}}</label>
        <slot class="form-control"
              :id="name"
              :data-vv-name="name"
              v-validate.initial="'required'"
              :data-vv-as="$t('form.'+ name)">
        </slot>
        <span class="invalid-feedback">{{ errors.first(name) }}</span>
    </div>
</template>

<script>
    export default {
        props: ['name']
    }
</script>

у вас есть какие-либо идеи? Проблема в том, что я не могу легко передать миксины и реквизит щелевому элементу/компоненту.

2 ответов


как о scoped слоты (Vue 2.5.0+)?

<!-- form-group.vue -->
<template>
  <div>
    <label />
    <slot v-bind="$props" />
    <span />
  </div>
</template>

выше, все реквизит <form-group> привязан к слоту с помощью v-bind. Вы можете указать только определенные поля: <slot :id="name" :data-vv-name="name" />

<form-group name="age">
  <input type="number" slot-scope="slotProps" v-bind="slotProps" />
</form-group>

здесь <input> можно получить доступ к реквизит слот с помощью slot-scope, давая ему имя (здесь slotProps). slotProps будет содержать все реквизиты <slot> как определено в form-group.vue.

еще несколько примеров:

<form-group name="language">
  <input type="text" slot-scope="sp" v-bind="sp" />
</form-group>

<form-group name="hello" value="friend">
  <span slot-scope="sp">
    {{ sp.name }}: {{ sp.value }}
  </span>
</form-group>

как сказано в комментариях, невозможно передать реквизит из содержимого слота, который является вашим <input>.

поскольку это довольно сложный случай, потребуется использовать функцию рендеринга, которая отправляет пользовательские атрибуты в новый тег при применении набора атрибутов по умолчанию.

Я сделал доказательство концепции здесь: https://codepen.io/anon/pen/Ozadop?editors=1010

Vue.component('my-input', {
  render: function(createElement) {
    const defaultSlot = this.$slots.default[0];
    const domProps = Object.assign({
      value: this.value,
      someProp: 'foobar',
      test: "asdf",
      class: defaultSlot.data.staticClass
    }, defaultSlot.data.attrs);
    return createElement(
      defaultSlot.tag, // tag name
      {
        attrs: domProps,
        props: domProps,
        on: {
          input: (event) => {
            this.value = event.target.value
            this.$emit('input', event.target.value)
          }
        }
      }
    )
  },
  props: {
    name: {
      type: String,
      required: true
    },
    value: {
      type: String,
      required: true
    }
  }
})

new Vue({
  el: "#app",
  data() {
    return {
      name: "Your Name",
      age: 5
    }
  }
});
.red {
  border: 2px solid red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.3/vue.min.js"></script>
<div id="app">
  Hello, {{name}}!! You are {{age}} years old.
  <br/>
  <my-input v-model="name">
    <input customProp="myCustomProp"></input>
  </my-input>

  <my-input v-model="age">
    <input type="number" required class="red" custom="otherCustom"></input>
  </my-input>

</div>

обратите внимание, что для этого потребуется все, что не является атрибутом, для применения к компоненту обертывания Vue с текущим контекстом.

это должно быть адаптировано для поддержки всех ваших потребностей, таких как поля select и аналогичный контент, но я считаю, что это хорошее начало.