当输入元素位于插槽中时,属性的反应性在 Vue3 中不起作用
2024-01-16
175
我正在尝试实现一个用于验证表单的包装器类。这个想法是,这个 Form 类接收一个带有处理程序的数据对象,该处理程序执行必要的检查并填充错误列表。此类还有一个方法可以返回特定字段的错误列表。
此外,我还创建了一个容器组件,其中有一个插槽用于显示字段的标签。此组件定义了一个插槽,其中包含使用 v-model 指令绑定值的表单元素(例如输入)。
我的期望是,当用户更新输入字段时,如果出现错误,验证错误占位符会在字段更改时更新。但是当输入在插槽中时,不会发生这种情况。如果我将输入放在插槽之外或在另一个位置显示绑定值(例如 div 元素),则错误占位符会更新。
我创建了一个 最小示例 演示了该问题。
示例代码:
// Container.vue
<template>
<div>
Component with slot
<div><slot></slot></div>
</div>
</template>
// App
<script setup lang="ts">
import { ref } from 'vue'
import Container from './Container.vue';
type ErrorRecord = {key: string, error: string}
class DataForm {
errors: ErrorRecord[] = []
data = {
_desc: '',
get description() {return this._desc},
set description(val) { this._desc = val; if (this.handler) { this.handler(val) } },
handler: undefined as unknown as (val: any) => void
}
constructor() {
this.data.handler = this.handler.bind(this)
}
handler(value: any) {
if (String(value).length < 5) {
this.errors = [{key: 'description', error: `Too Short`}]
}
else {
this.errors = []
}
}
getErrors(path: string) {
return this.errors.find(err => err.key == path)?.error
}
}
const data1 = ref(new DataForm())
const data2 = ref(new DataForm())
const data3 = ref(new DataForm())
</script>
<template>
<h1>Error not updated when input is in slot: </h1>
<Container>
<input type="text" v-model="data1.data.description">
</Container>
<div> {{ data1.getErrors('description') }} </div>
<h1>Error updated when value is used somewhere else: </h1>
<Container>
<input type="text" v-model="data2.data.description">
</Container>
<div>{{ data2.data.description }}</div>
<div> {{ data2.getErrors('description') }} </div>
<h1>Error updated when input is not in slot: </h1>
<input type="text" v-model="data3.data.description">
<div> {{ data3.getErrors('description') }} </div>
</template>
2个回答
我更改了你的 最小示例 ,由于您在使用 ref 或 react 之前在 DataForm 类中做了很多事情,因此 vue 不会帮助您更新 dom,您需要先创建一个 ref,然后更新它。 此外,我给您留了一个问题,为什么数据使用 react 而不是 ref
moon
2024-01-17
将此作为答案发布,因为就我个人而言,原因并不明显。虽然从 JS/Vue 的角度来看这非常简单。我的主要经验是 C#。因此,我遵循了 OOP 模式。但在 Vue 中,反应对象是代理,所以在我的代码中,我引用了非反应性 this 。重写导致问题的代码以遵循建议的模式解决了问题。所以对于那些像我一样来自其他编程语言的人来说,这是一个很好的例子,不要盲目遵循我们在其他语言中习惯的模式。
SO 上的另一个例子: 链接 。
非常感谢@estus-flask。 链接 至更正后的工作示例。
工作代码:
// App.vue
<script setup lang="ts">
import { ref, reactive } from 'vue'
import Container from './Container.vue';
type ErrorRecord = {key: string, error: string}
const createForm = () => {
var o = reactive({
errors: [],
data:{
_desc: '',
get description() {return o._desc},
set description(val) { o._desc = val; o.data.handler(val) },
handler(value: any) {
if (String(value).length < 5) {
o.errors = [{key: 'description', error: `Too Short`}]
}
else {
o.errors = []
}
},
},
getErrors(path: string) {
return o.errors.find(err => err.key == path)?.error
}
})
return o
}
const data1 = createForm()
const data2 = createForm()
const data3 = createForm()
</script>
<template>
<h1>Error not updated when input is in slot: </h1>
<Container>
<input type="text" v-model="data1.data.description">
</Container>
<div> {{ data1.getErrors('description') }} </div>
<h1>Error updated when value is used somewhere else: </h1>
<Container>
<input type="text" v-model="data2.data.description">
</Container>
<div>{{ data2.data.description }}</div>
<div> {{ data2.getErrors('description') }} </div>
<h1>Error updated when input is not in slot: </h1>
<input type="text" v-model="data3.data.description">
<div> {{ data3.getErrors('description') }} </div>
</template>
Anton Tikhonov
2024-01-18