vue3,封装一个很长的表单,数据如何双向绑定?

我有一个很长的表单A,内容固定,如下。

A.vue
<Form>
    <Select v-model='form.a'/>
    <Input v-model='form.b'/>
    <Select v-model='form.c'/>
    <Input v-model='form.d'/>
    <DatePicker v-model='form.e'/>
    ………………………………
</Form>

A在项目内非常多的地方使用,A1,A2,A3的内部数据独立不用同步。
但是每个使用A的地方都会因为父组件的某些事件而改动A的内部参数。

<template>
  <A v-model="formData"></A>
</template>

<script setup>
  const formData = ref({a,b,c,d...})
  
  // await res = ajax()
  
  webSocket.onMessage(res=>{
    formData.a = res.a
  })
  
</script>

请问这种情况有无办法使formData双向绑定?

现在我的实现方法是

<template>
  <Form>
    <Select v-model='form.a'/>
    <Input v-model='form.b'/>
    <Select v-model='form.c'/>
    <Input v-model='form.d'/>
    <DatePicker v-model='form.e'/>
    ………………………………
  </Form>
<template>

<script setup>
const form = computed(() => {
  return props.modelValue
})
<script>

但是这样违背了单项数据流的原则。所以请问有更好的办法吗?

阅读 2.3k
avatarAI BotBETA

在这种情况下,您可以使用 Vue 3 的 reactivecomputed 函数,以及 watchEffect 函数来实现双向数据绑定。具体来说,您可以将表单数据封装在一个 reactive 对象中,然后将这个对象传递给组件作为属性。然后,您可以使用 watchEffect 函数来监听这个 reactive 对象的变化,并触发相应的行为。

以下是一个示例:

<template>
  <Form :model="formData">
    <!-- form fields go here -->
  </Form>
</template>

<script setup>
import { ref, watchEffect } from 'vue';

const formData = ref({});

watchEffect((onInvalidate) => {
  onInvalidate(() => {
    // handle your validation here, based on formData
  });
});
</script>

在上面的示例中,formData 是一个 reactive 对象,它包含表单的所有字段。然后,我们将 formData 作为属性传递给 Form 组件,并使用 v-model 指令将其与表单字段进行双向绑定。

另外,如果您需要处理一些依赖于表单数据的变化,您可以使用 watchEffect 函数来监听 formData 的变化。在上面的示例中,我们使用 watchEffect 来监听 formData 的变化,并在每次变化时执行一些操作(例如进行表单验证)。

2 个回答

子组件:

<template>
  <Form>
    <Select :model-value="form.a" @update:model-value="updateForm('a', $event)"/>
    <Input :model-value="form.b" @update:model-value="updateForm('b', $event)"/>
    <Select :model-value="form.c" @update:model-value="updateForm('c', $event)"/>
    <!-- 其他字段类似处理 -->
  </Form>
</template>

<script setup>
import { ref, toRefs } from 'vue';

const props = defineProps({
  modelValue: Object
});

const emit = defineEmits();

const form = ref({ ...props.modelValue }); // 初始化 form 数据


const updateForm = (key, value) => {
  form[key] = value;
  emit('update:modelValue', { ...form });
}
</script>

父组件:

<template>
  <A v-model="formData"></A>
</template>

<script setup>
import { ref } from 'vue';

const formData = ref({a: '', b: '', c: '', d: '', e: ''}); // 初始化数据

watchEffect(async () => {
  const res = await ajax();
  formData.a = res.a;
  // ... 其他字段更新
});
</script>

方案1:这个简单 可以直接使用这个就行usevmodel
方案2:
或者直接手动监听

<!-- 父组件 -->
<script setup>
import myComponent from "./components/MyComponent.vue";
import { ref, watch } from "vue";

const form = ref({
  name: "张三",
  age: 18,
  sex: "man",
});

watch(form, (newValue) => {
  console.log(newValue);
});
</script>

<template>
  <div>
    <my-component v-model="form"></my-component>
  </div>
</template>


<!-- 子组件 -->
<template>
  <div>
    <el-input v-model="form.name"></el-input>
    <el-input v-model="form.age"></el-input>
    <el-input v-model="form.sex"></el-input>
  </div>
</template>
<script setup>
import { computed } from "vue";

const props = defineProps({
  modelValue: {
    type: Object,
    default: () => {},
  },
});

const emit = defineEmits(["update:modelValue"]);

const form = computed({
  get() {
    return new Proxy(props.modelValue, {
      get(target, key) {
        return Reflect.get(target, key);
      },
      set(target, key, value,receiver) {
        emit("update:modelValue", {
          ...target,
          [key]: value,
        });
        return true;
      },
    });
  },
  set(newValue) {
    emit("update:modelValue", newValue);
  },
});
</script>


推荐问题