【问题标题】:Vue 3 access child component from slotsVue 3 从插槽访问子组件
【发布时间】:2021-02-01 01:56:51
【问题描述】:

我目前正在进行自定义验证,如果可能,我希望访问子组件并在其中调用一个方法。

表单包装器

<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。

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

【问题讨论】:

    标签: vue.js vuejs3


    【解决方案1】:

    在您提供的链接中,Linus 提到使用 $on$off 来执行此操作。这些已在 Vue 3 中删除,但您可以使用 recommended mitt 库。

    一种方法是将submit 事件分派给子组件,并让它们在收到submit 时发出validate 事件。但也许您无权将此添加到子组件?

    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');
    

    【讨论】:

      【解决方案2】:

      我找到了另一个解决方案,灵感来自 quasar 框架。

      1. 表单组件提供()绑定和取消绑定功能。
        bind() 将验证函数推送到数组并存储在 Form 组件中。
      2. 输入组件从父表单组件注入绑定和取消绑定功能。
        使用 self validate() 函数和 uid 运行 bind()
      3. 表单从提交按钮监听提交事件。
        遍历所有这些 validate() 数组,如果没有问题则 emit('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>
      

      【讨论】:

        猜你喜欢
        • 2021-01-14
        • 2021-06-06
        • 2020-05-22
        • 2019-08-11
        • 1970-01-01
        • 2018-09-14
        • 2019-09-29
        • 2020-12-24
        • 2021-02-06
        相关资源
        最近更新 更多