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

Мой код основан на этом пример, но это для vue 2.

Если бы кто-то мог мне помочь, это было бы здорово, или это вообще возможно.


person lejhbah    schedule 17.10.2020    source источник


Ответы (2)


Я нашел другое решение, вдохновленное фреймворком quasar.

  1. Компонент формы предоставляет () функцию привязки и отмены привязки.
    bind () помещает функцию проверки в массив и сохраняет ее в компоненте формы.
  2. Компонент ввода вводит функцию привязки и отмены привязки из родительского компонента формы.
    запустите bind () с функцией self validate () и uid
  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>
person LHJ    schedule 18.11.2020

В предоставленной вами ссылке Линус упоминает, что для этого используются $on и $off. Они были удалены в Vue 3, но вы можете использовать рекомендованный mitt библиотека.

Один из способов - отправить событие submit дочерним компонентам и заставить их генерировать событие validate при получении submit. Но, может быть, у вас нет доступа, чтобы добавить это к дочерним компонентам?

Пример JSFiddle

<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');
person Matt Deacalion    schedule 25.10.2020