【问题标题】:How can a component call a method on a component defined within a slot in Vue.js?组件如何在 Vue.js 的插槽中定义的组件上调用方法?
【发布时间】:2020-10-01 20:15:07
【问题描述】:

在我们的项目中,我们有一个列表组件,它呈现一组项目的列表,其中一个可以一次编辑。它使用插槽来获取要为每个项目呈现的内容。简化,看起来有点像这样:

<template>
  <div>
    <div v-for="item in items">
      <slot name="display" :item="item" v-if="!editing(item)"></slot>
      <slot name="edit" :item="item" v-else></slot>
    </div>
    <button @click="clickDone">Done</button>
  </div>
<template>
<script>
export default {
  props: ["items"],
  methods: {
    clickDone() {
      ...
    }
  }
}

这用于需要显示复杂对象的可编辑列表的另一个组件。看起来有点像这样:

<EditableList :items="listOfComplexObjects">
  <template #edit="item">
    <component :is="item.componentType" v-model="item" />
  </template>
</EditableList>

然后可以为列表中的每个项目呈现许多不同的组件,具体取决于它的类型。

在第一个代码 sn-p 中,您可以看到“完成”按钮。此按钮是列表用来将“可编辑”项目转换为“显示”项目的按钮。我们遇到的问题是我们想在单击此按钮时调用验证。但是,每个项目类型组件的验证都会有所不同。此外,验证需要将项目类型组件中的某些字段标记为无效并在其上显示错误消息。所以这个验证在项目类型组件中最有意义。所以我们正在尝试考虑如何在“完成”按钮上调用插槽内组件上的验证方法。

我们的考虑:

  1. 通过在“组件”上使用 ref,我们可以使用 this.$parent.refs.refName.validate 之类的东西从可编辑列表中调用验证器。这样做的问题是它破坏了可编辑列表、父组件和列表项组件之间的封装。

  2. 将“完成”按钮移动到列表项组件中。单击完成按钮将调用本地验证,然后发出一个事件,父组件将传递给可编辑列表以操纵其内部状态。这个解决方案是我们目前最好的解决方案,但感觉像是在不可编辑的列表组件中添加了不必要的样板代码。

我们希望我们能做什么:

  • 让每个列表项组件实现一个验证方法,父组件将其作为一种特定于每个项的“插槽”或​​道具传递到可编辑列表中。然后可编辑列表可以在单击完成时调用该验证方法,并且列表项组件可以使用错误消息更新其状态。

如何在 Vue.js 中最轻松正确地实现上述目标?

【问题讨论】:

  • 如果验证失败,您是否希望能够取消转换表单编辑以显示?
  • @Janos 是的,我想要一个像“if (validation) { ... }”这样的条件,其中 ... 是从“编辑”模式更改为“显示”模式的代码

标签: vue.js vue-component


【解决方案1】:

我希望你使用的是 VeeValidate 之类的东西,因为这可能会相对容易地解决这个问题。 有了它,您可以在动态组件中注入验证器并在整个表单上进行验证。 如果您想验证 旁边的表单数据,我还不清楚什么,但这就是您的 this.$parent.refs... 想法所暗示的。

所以查看VeeValidate injections,然后你可以这样处理它:

<script>
  export default {
    inject: [„$validator“],
    props: ["items"],
    methods: {
      clickDone() {
        this.$validator.validateAll().then((result) => {
          if (!result) {
            // todo do optional ui feedback — field error classes will be set automatically
            return false
          }
           ...do editing transition...
        })
      }
    }
}

它本质上合并了两个验证范围(父和子),但引入了紧密耦合。

【讨论】:

  • 感谢您的回答亚诺斯。我们目前没有使用任何验证库。也许我们应该这样做,但是我们发现这个问题在更多的地方蔓延,而不仅仅是验证,所以我们想要一个不管如何处理验证都有效的结果。谢谢。
【解决方案2】:

我们终于找到了解决这个问题的好方法。如果它对其他人有帮助,这就是这样做的方法。

我们维护了一个组件引用的映射,每次添加一个新组件,这个映射都会更新:

<component :is="item.componentType" v-model="item" :ref="item.componentType" />
data: function() {
  return {
    componentMap: {}
  };
},

methods: {
  onNewComponent(componentType) {
    this.$nextTick(function() {
      const componentRefs = this.$refs[componentType];

      if (Array.isArray(componentRefs)) {
        this.$set(this.componentMap, componentType componentRefs);
      } else {
        this.$set(this.componentMap, componentType, [componentRefs]);
      }
    });
  }
}

然后我们可以调用这些组件引用。例如:

for (let componentReference of this.componentMap[componentType ]) {
  valid = componentReference.validate() && valid;
}

我们一开始并没有意识到这项工作的关键是使用this.$nextTick。似乎没有这个,当我们尝试获取引用时它是未定义的,但是通过使用它我们能够访问引用并保存它们以供以后使用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-04-03
    • 2016-05-06
    • 2017-09-12
    • 1970-01-01
    • 1970-01-01
    • 2020-11-11
    • 2020-09-21
    • 2018-04-25
    相关资源
    最近更新 更多