Vue 3从插槽访问子组件

问题描述

我目前正在进行自定义验证,并且希望在可能的情况下访问子组件并在其中调用方法。

表格包装器

<template>
    <form @submit.prevent="handleSubmit">
        <slot></slot>
    </form>
</template>

<script lang="ts">
import { defineComponent } from 'vue';

export default defineComponent({
    setup(props,{ slots }) {
        const validate = (): boolean => {
            if (slots.default) {
                slots.default().forEach((vNode) => {
                    if (vNode.props && vNode.props.rules) {
                        if (vNode.component) {
                            vNode.component.emit('validate');
                        }
                    }
                });
            }

            return false;
        };

        const handleSubmit = (ev: any): void => {
            validate();
        };

        return {
            handleSubmit,};
    },});
</script>

当我致电slot.default()时,我会得到正确的子组件列表,并可以看到它们的道具。但是,vNode.component始终是null

我的代码基于example,但它适用于vue 2。

如果有人可以帮助我,那太好了,或者甚至有可能做到。

解决方法

在您提供的链接中,Linus提到使用$on$off来做到这一点。这些已在Vue 3中删除,但是您可以使用recommended mitt库。

一种方法是向子组件调度submit事件,并在子组件收到validate时发出submit事件。但是也许您无权将其添加到子组件中?

JSFiddle Example

<div id="app">
    <form-component>
        <one></one>
        <two></two>
        <three></three>
    </form-component>
</div>
const emitter = mitt();

const ChildComponent = {
    setup(props,{ emit }) {
        emitter.on('submit',() => {  
            console.log('Child submit event handler!');

            if (props && props.rules) {
                emit('validate');
            }
        });
    },};

function makeChild(name) {
    return {
        ...ChildComponent,template: `<input value="${name}" />`,};
}

const formComponent = {
    template: `
        <form @submit.prevent="handleSubmit">
            <slot></slot>
            <button type="submit">Submit</button>
        </form>
    `,setup() {
        const handleSubmit = () => emitter.emit('submit');
        return { handleSubmit };
    },};

const app = Vue.createApp({
    components: {
        formComponent,one: makeChild('one'),two: makeChild('two'),three: makeChild('three'),}
});

app.mount('#app');
,

我发现了另一种受类星体框架启发的解决方案。

  1. 表单组件的provide()绑定和取消绑定功能。
    bind()将validate函数推入数组并存储在Form组件中。
  2. 输入组件从父Form组件注入bind和unbind函数。
    使用self validate()函数和uid运行bind()
  3. 通过“提交”按钮进行表单监听提交事件。
    运行所有这些validate()数组,如果没有问题,则发出('submit')

表单组件

import {
  defineComponent,onBeforeUnmount,onMounted,reactive,toRefs,provide
} from "vue";
export default defineComponent({
  name: "Form",emits: ["submit"],setup(props,{ emit }) {
    const state = reactive({
      validateComponents: []
    });
    provide("form",{
      bind,unbind
    });
    onMounted(() => {
      state.form.addEventListener("submit",onSubmit);
    });
    onBeforeUnmount(() => {
      state.form.removeEventListener("submit",onSubmit);
    });
    function bind(component) {
      state.validateComponents.push(component);
    }
    function unbind(uid) {
      const index = state.validateComponents.findIndex(c => c.uid === uid);
      if (index > -1) {
        state.validateComponents.splice(index,1);
      }
    }
    function validate() {
      let valid = true;
      for (const component of state.validateComponents) {
        const result = component.validate();
        if (!result) {
          valid = false;
        }
      }
      return valid;
    }
    function onSubmit() {
      const valid = validate();
      if (valid) {
        emit("submit");
      }
    }
  }
});

输入组件

import { defineComponent } from "vue";
export default defineComponent({
  name: "Input",props: {
    rules: {
      default: () => [],type: Array
    },modelValue: {
      default: null,type: String
    }
  }
  setup(props) {
    const form = inject("form");
    const uid = getCurrentInstance().uid;
    onMounted(() => {
      form.bind({ validate,uid });
    });
    onBeforeUnmount(() => {
      form.unbind(uid);
    });
    function validate() {
      // validate logic here
      let result = true;
      props.rules.forEach(rule => {
        const value = rule(props.modelValue);
        if(!value) result = value;
      })
      return result;
    }
  }
});

用法

<template>
  <form @submit="onSubmit">
    <!-- rules function -->
    <input :rules="[(v) => true]">
    <button label="submit form" type="submit">
  </form>
</template>

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...