首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在Vue中使用v-bind和v-on绑定自定义命名模型?

如何在Vue中使用v-bind和v-on绑定自定义命名模型?
EN

Stack Overflow用户
提问于 2021-10-20 12:51:33
回答 1查看 1.1K关注 0票数 1

我正在制作一个InputWrapper组件,用于装饰一些BootstrapVue输入组件。重点是围绕给定的输入自动处理验证状态、消息、样式等(在下面的示例中没有显示)。

我想动态地“前进”v-模型。当包装组件使用自定义模型属性和更新事件进行双向绑定时,就会出现问题。

主要想法如下。

InputWrapper.Vue

代码语言:javascript
复制
    <template>
      <div>
        <slot v-bind="wrappedBindings"></slot>
      </div>
    </template>
    
    <script>
    export default {
      props: {
        value: {required: true}
      },

      methods: {
        onInput($event){
          this.$emit('input', $event);
        }
      },
      
      computed: {
        wrappedBindings(){
          return {
            attrs: {
              value: this.value
            },
            on: {
              input: $event => this.onInput($event),
              'update:value': $event => this.onInput($event)
            }
          }
        }
      }
    }
    </script>

使用

代码语言:javascript
复制
    <div>
      <input-wrapper v-model="selectModel" v-slot="{attrs, on}">
        <!-- v-model of b-form-select is :value and @input so this works -->
        <b-form-select v-bind="attrs" v-on="on" ...other stuff...></b-form-select>
      </input-wrapper>

      <input-wrapper v-model="inputModel" v-slot="{attrs, on}">
        <!-- v-model of b-form-input is :value and @update (not @update:value or @input) so this does not work -->
        <b-form-input v-bind="attrs" v-on="on" ...other stuff...></b-form-input>
      </input-wrapper>

      <input-wrapper v-model="checkModel" v-slot="{attrs, on}">
        <!-- v-model of b-form-checkbox is :checked (not :value) and @input so this does not work -->
        <b-form-checkbox v-bind="attrs" v-on="on" ...other stuff...></b-form-checkbox>
      </input-wrapper>
    </div>

My当前和不满意的解决方案

代码语言:javascript
复制
    <div>
      <input-wrapper v-model="inputModel" v-slot="{attrs, on}">
        <b-form-input v-bind="attrs" v-on="on" @update="on.input" 
          ...other stuff...></b-form-input>
      </input-wrapper>

      <input-wrapper v-model="checkModel" v-slot="{attrs, on}">
        <b-form-checkbox v-bind="attrs" v-on="on" :checked="attrs.value"
         ...other stuff...></b-form-checkbox>
      </input-wrapper>
    </div>

这个解决方案允许我做我想做的事情,但它的实现时间更长,而且您总是需要BootstrapVue文档。

另一种解决方案是为每个BsVue输入创建一个自定义组件,但我也需要将每个属性和事件转发到自定义组件。不这样做有很多原因,但主要原因是很难维持。

我的问题如下:如何使用v- bind ="attrs“和v-on="on”动态绑定任何自定义的v-模型属性和事件,而不事先知道它们?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-10-22 19:51:03

这个不容易..。

我如何使用v- bind ="attrs“和v-on="on”来动态绑定任何定制的v-模型属性和事件,而不事先知道它们?

好吧,你不能。

我的第一个想法是以某种方式到达Vue 2中使用的模型选择来自定义这个组件上的v-model的道具名称和事件名。但不幸的是,这在$scopedSlots.default()上是无法访问的(而且以这种方式使用它也是非常无效的)

Imho最好的选择是在开槽组件上使用v-model,让Vue为您做繁重的工作.

..normally在创建input (或某些自定义输入)包装器时,最简单的方法是使用computed“连接”(或转发)内部组件的v-model到包装器的v-model

代码语言:javascript
复制
<template>
  <input :type="type" v-model="model" />
</template>
<script>
export default {
  props: ['value', 'type'],
  computed:{
    model: {
      get() { return this.value }
      set(newValue) { this.$emit('input', newValue) } 
    }
  }
}
</script>

为什么?就因为同样的原因你在问你的问题。您不需要选择使用哪种道具和事件(因为不同的input类型对value使用不同的道具名称并使用不同的事件)--您可以将其留给v-model

但是,在开槽组件上做同样的事情有点棘手。您不能将v-model直接放在<slot>上。它必须放在开槽组件上(在父模板中)。所以,唯一的方法是把上面计算出来的“某物”传递到插槽道具中。这也是个问题。

  1. 插槽道具不能以常规道具不应在组件内部发生变异的方式被插槽内容更改。
  2. 不可能将v-bind计算作为一个整体到一个时隙。如果尝试这样做,时隙组件只接收从getter读取的当前值,而setter则被抛在后面.

为了克服第一个问题,可以使用与普通道具相同的技术--而不是仅仅传递一个值,传递一个对象,并使该值成为它的属性。现在,您可以随意修改该属性的值(许多人认为这很脏,但我认为它非常强大,如果使用得当,可以节省大量样板代码)。

第二个问题可以通过使用Object.defineProperty API来解决,它允许您执行类似Vue computed属性的操作--定义对象的属性,并在读取或写入属性时声明您自己的函数。

以下是最后的解决方案:

代码语言:javascript
复制
// InputWrapper.vue
<template>
  <div>
    <slot v-bind="wrappedBindings"></slot>
  </div>
</template>

<script>
export default {
  props: {
    value: { required: true },
  },

  computed: {
    wrappedBindings() {
      const self = this;
      const bindings = {
        attrs: {
        },
        on: {
        },
        model: {},
      };

      // property MUST be on 2nd level otherwise it wont work thanks to how Vue 2 is implemented
      // https://github.com/vuejs/vue/blob/0948d999f2fddf9f90991956493f976273c5da1f/src/core/instance/render-helpers/render-slot.js#L24
      // https://github.com/vuejs/vue/blob/0948d999f2fddf9f90991956493f976273c5da1f/src/shared/util.js#L205
      Object.defineProperty(bindings.model, "proxy", {
        get() {
          return self.value;
        },
        set(newValue) {
          self.$emit("input", newValue);
        },
        enumerable: true,
        configurable: false,
      });

      return bindings;
    },
  },
};
</script>

以及使用情况:

代码语言:javascript
复制
<input-wrapper v-model="inputModel" v-slot="{ attrs, on, model }">
  <b-form-input
    v-bind="attrs"
    v-on="on"
    v-model="model.proxy"
  ></b-form-input>
</input-wrapper>

这当然不理想(特别是您需要在model.proxy中使用v-model这一事实),但现在我没有看到任何其他方法来实现这样的“通用”包装器(当然,除了选择一个组件库,该组件库坚持使用自定义组件上的v-model“值”/“输入”)。

演示

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/69646326

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档