我正在将我的应用程序从vue2转换为vue3,但在我的形式的一个方面停滞不前。
我正在使用SFC用于Form Element组件(FormInput,FormTextArea,FormCheckBox等),并使用插槽将它们传递到形式的容器组件(FormGroup,FormTab等)中,例如:
<ssi-form>
<ssi-form-tabs>
<ssi-form-tab title="tab1">
<ssi-form-input title="name" ... />
<ssi-form-checkbox title="active" ... />
</ssi-form-tab>
</ssi-form-tabs>
</ssi-form>
这些父容器需要查看一些计算的属性孩子形成元素,将错误消息拉到顶部。
在VUE2中,我使用已安装的生命周期挂钩(带有选项API)读取插槽并访问计算的属性,例如:
mounted: function() {
const vm = this;
this.$slots.default.forEach((vNode) => {
const vueComponent = vNode.componentInstance;
vueComponent.$on("invalid", vm.onInvalid);
if (vueComponent.errorCount === undefined) return;
this.childFormElements.push(vueComponent);
});
},
使用此设置,我可以从每个孩子中获取 errorcount
从每个孩子中的计算属性插槽,因此我可以将错误汇总到表单的最高级别。
现在,我要切换到VUE3,似乎 componentInstance
不存在。我尝试使用 OnMounted
指令设置类似的逻辑,但是当我访问插槽元素时,我找不到任何方法来查看其 errorcount
计算属性
onMounted(() => {
slots.default().forEach((vNode) => {
console.log(vNode);
});
});
:不包含计算属性。我认为当我阅读 Define Expose
时,我发现了一些有用的东西,但是即使公开了 errorcount
属性,也没有出现。
这是我试图使用的文本输入的&lt; script&gt;
:
<script setup lang="ts">
import { ref, defineProps, defineEmits, computed } from "vue";
let props = defineProps<{
label: string,
id: string,
modelValue: string|number,
type?: string,
description?: string,
min?: string|number,
max?: string|number,
pattern?: string,
message?: string
}>();
let emit = defineEmits(["input", "update:modelValue", "invalid"]);
let state = ref(null);
let error = ref("");
const input = ref(null);
function onInput(event: Event) {
validate();
emit('update:modelValue', event.target.value)
}
// methods
function validate() {
let inputText = input.value;
if (inputText === null) return;
inputText.checkValidity();
state.value = inputText.validity.valid;
error.value = inputText.validationMessage;
}
const errorCount = computed(() => {
return state.value === false ? 1 : 0;
});
defineExpose({errorCount})
</script>
因此,问题是 - 父组件如何读取 errorcycount
从放置在插槽中的组件中的属性?
I'm in the process of converting my app from Vue2 to Vue3, but have gotten stalled out on one aspect of my forms.
I'm using SFCs for form element components (FormInput, FormTextArea, FormCheckbox, etc.), and passing them into form container components (FormGroup, FormTab, etc) using slots, like so:
<ssi-form>
<ssi-form-tabs>
<ssi-form-tab title="tab1">
<ssi-form-input title="name" ... />
<ssi-form-checkbox title="active" ... />
</ssi-form-tab>
</ssi-form-tabs>
</ssi-form>
Those parent containers need to view some computed properties of the child form elements to pull error messages to the top.
In Vue2, I used the mounted lifecycle hook (with the options API) to read the slots and access the computed properties, like this:
mounted: function() {
const vm = this;
this.$slots.default.forEach((vNode) => {
const vueComponent = vNode.componentInstance;
vueComponent.$on("invalid", vm.onInvalid);
if (vueComponent.errorCount === undefined) return;
this.childFormElements.push(vueComponent);
});
},
Using this setup, I could grab the errorCount
computed property from each child in the slot, so I could aggregate errors going up to the top level of the form.
Now that I'm switching to Vue3, it seems like componentInstance
doesn't exist. I tried setting up similar logic using the onMounted
directive, but when I access the slot elements, I can't find any way to see their errorCount
computed property:
onMounted(() => {
slots.default().forEach((vNode) => {
console.log(vNode);
});
});
The logged object does not contain the computed property. I thought I found something useful when I read about defineExpose
, but even after exposing the errorCount
property, nothing comes up.
Here is the <script setup>
from the SFC for the text input that I'm trying to work with:
<script setup lang="ts">
import { ref, defineProps, defineEmits, computed } from "vue";
let props = defineProps<{
label: string,
id: string,
modelValue: string|number,
type?: string,
description?: string,
min?: string|number,
max?: string|number,
pattern?: string,
message?: string
}>();
let emit = defineEmits(["input", "update:modelValue", "invalid"]);
let state = ref(null);
let error = ref("");
const input = ref(null);
function onInput(event: Event) {
validate();
emit('update:modelValue', event.target.value)
}
// methods
function validate() {
let inputText = input.value;
if (inputText === null) return;
inputText.checkValidity();
state.value = inputText.validity.valid;
error.value = inputText.validationMessage;
}
const errorCount = computed(() => {
return state.value === false ? 1 : 0;
});
defineExpose({errorCount})
</script>
So the question is - how can a parent component read the errorCount
property from a component placed into a slot?
发布评论
评论(2)
没有充分的理由不应使用内部属性,尤其是因为它们不属于公共API,并且可以改变其行为或无通知而被删除。一个最好的解决方案是可以通过公共API实现目标的解决方案。
这里的解决方案是 process插槽在容器的呈现器组件中。 vNode对象是渲染元素的模板,并且可以在这一点上进行转换,例如,可以添加来自容器组件范围的REF。
如果需要访问儿童组件的实例以添加事件侦听器,则它们此时:
其中
ref
is ref函数维护了一系列儿童参考,尽管不需要添加事件侦听器的唯一目的。Internal properties shouldn't be used without a good reason, especially because they don't belong to public API and can change their behaviour or be removed without notice. A preferable solution is the one that can achieve the goal by means of public API.
Here the solution is to process slots in container component's render function. Vnode objects are the templates for rendered elements and they can be transformed at this point, e.g. a ref from the scope of container component can be added.
If the instances of child components need to be accessed to add event listeners, they can be added to a vnode at this point:
Where
ref
is ref function that maintains a collection of children refs, although it's not needed for the sole purpose of adding event listeners.我相信这是不可行的,因为它已经被认为是不良练习 以来,“ Nofollow Noreferrer”>。
DefineExpose
在这种情况下确实有帮助,但是由于您说提供 /注射可能是更好的方法?
I believe this is not doable, since it has already been considered a bad practice since Vue 2.
defineExpose
does help in such a situation, but since you saidmaybe Provide / Inject could be a better approach?