[Vuejs]-VueJS 2.4 basic wrapper component

3👍

v-model is just syntax sugar for :value and @input. It works differently for native input elements than it does for components; it’s not as simple as just adding v-on="$listeners" v-bind="$props" to your component’s template. It won’t work.

If you want v-model to work, you’ll need to handle the input event within the component and emit the changed value:

<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),
      });
    },
  },
});

The above is kind of a hack to make v-model work correctly, otherwise you could just do this:

<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>
  `,
});

Note that class and style cannot be proxied to the inner <select> because they’re not props, they’re handled specially by Vue and will be applied to the root element of the component’s template.

EDIT: Actually you can proxy class and style but it requires writing the render function manually:

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)]);
  },
});

Leave a comment