VueJS 2.4基本包装器组件

时间:2017-12-12 00:33:25

标签: vue.js vuejs2

也许这是一个简单的问题,但我找不到创建基本包装器组件的正确方法。例如,我有一个像这样的选择:

<select v-model="foo" name="bar" v-validate="'required'" v-bind:class="{ invalid: errors.has('bar') }">
    <option value="">Default</option>
    <option value="1">Value 1</option>
    <option value="2">Value 2</option>
</select>

我需要添加一些糖,一个div加上类custom-select。所以我创建了一个custom-select组件来包装原生选择:

Vue.component('custom-select', {
    template: `
        <div class="custom-select">
            <select v-on="$listeners" v-bind="$props">
                <slot></slot>
            </select>
        </div>
    `,
});

我的问题是我的组件没有继承像v-modelclass这样的道具。我想知道是否有全局方式,不必重写组件中的所有道具并让他继承一切?

1 个答案:

答案 0 :(得分:2)

v-model只是:value@input的语法糖。它对本机输入元素的工作方式与对组件的工作方式不同;它并不像将v-on="$listeners" v-bind="$props"添加到您的组件模板那么简单。它不会工作。

如果您希望v-model正常工作,您需要处理组件中的输入事件并发出更改后的值:

<custom-select v-model="selected">
Vue.component('custom-select', {
  template: `
    <div class="custom-select">
      <select v-bind="$attrs" v-on="computedListeners">
        <slot></slot>
      </select>
    </div>
  `,
  computed: {
    computedListeners() {
      return Object.assign({}, this.$listeners, {
        input: e => this.$emit('input', e.target.value),
      });
    },
  },
});

以上是让v-model正常工作的黑客攻击,否则你可以这样做:

<custom-select :value="selected" @input="selected = $event.target.value">
Vue.component('custom-select', {
  template: `
    <div class="custom-select">
      <select v-bind="$attrs" v-on="$listeners">
        <slot></slot>
      </select>
    </div>
  `,
});

请注意,classstyle无法代理内部<select>,因为它们不是道具,它们由Vue专门处理并将应用于组件模板的根元素。

编辑:实际上你可以代理classstyle,但它需要手动编写渲染功能:

Vue.component('custom-select', {
  functional: true,
  render(h, ctx) {
    const on = Object.assign({}, ctx.listeners);
    if (ctx.listeners.input) {
      // Required for v-model to work
      on.input = e => ctx.listeners.input(e.target.value);
    }

    const data = Object.assign({}, ctx.data, { on });
    return h('div', { class: 'custom-select' }, [h('select', data, ctx.children)]);
  },
});