在 Vue3 中将数据道具从子组件传递到父组件

问题描述 投票:0回答:1

我正在为我的作品集编写一个个人项目。 我正在制作一个注册网站。

因此用户将填写:个人数据、运输信息,然后将看到回顾。

我有一个视图(Register.vue),我在其中放置了 Vuetify 的步进器以使速度更快。

共有三个步骤:PersonalDataForm(一个单独的组件)、ShippingForm(另一个单独的组件)和回顾。

因此,对于每一步,用户都可以填写表格,然后继续下一步。

我面临的问题是:

  1. 我想禁用“下一步”按钮,除非表单已完成并且我不知道如何处理 vuetify 步进器
  2. 我不知道如何传递道具,所以我无法解决第一个问题。

注册查看

<template>
  <v-stepper color="deep-purple-darken-1" :items="['Dati Anagrafici', 'Indirizzo di spedizione', 'Riepilogo']" class="my-5">
    <template v-slot:item.1>
      <v-card>
        <PersonalDataForm @formDataSubmitted="handlePersonalDataForm" :isFormIncomplete="personalDataFormIsIncomplete"/>
      </v-card>
    </template>

    <template v-slot:item.2>
      <v-card>
        <ShippingForm @formDataSubmitted="handleShippingForm" :isFormIncomplete="shippingFormIsIncomplete"/>
      </v-card>
    </template>

    <template v-slot:item.3>
      <v-card>
        <Recap @completed="submitData" :disabled="isFormIncomplete"/>
      </v-card>
    </template>
  </v-stepper>
</template>

<script setup>
import { ref, computed } from 'vue';
import PersonalDataForm from '@/components/forms/PersonalDataForm.vue';
import ShippingForm from '@/components/forms/ShippingForm.vue';
import Recap from '../components/Recap.vue';

const personalDataFormIsIncomplete = ref(true);
const shippingFormIsIncomplete = ref(true);

const isFormIncomplete = computed(() => {
  return personalDataFormIsIncomplete.value || shippingFormIsIncomplete.value;
});

const handlePersonalDataForm = (formData) => {
  console.log("Received personal data:", formData);
  personalDataFormIsIncomplete.value = false;
};

const handleShippingForm = (formData) => {
  console.log("Received shipping data:", formData);
  shippingFormIsIncomplete.value = false;
};

const submitData = () => {
  console.log('Submitting data...');
};
</script>
```

PersonalDataForm.vue
```
<template>
  <v-container>
    <v-card class="rounded-lg py-4 px-8" elevation="8">
      <form @submit.prevent="handleSubmit">
        <v-row>
          <v-col cols="12" md="6">
            <v-text-field
              v-model="state.firstName"
              :error-messages="v$.firstName.$errors.map((e) => e.$message)"
              label="Nome"
              required
              variant="underlined"
              color="deep-purple-lighten-1"
              @blur="v$.firstName.$touch"
              @input="v$.firstName.$touch"
            ></v-text-field>
          </v-col>

          <v-col cols="12" md="6">
            <v-text-field
              v-model="state.lastName"
              :error-messages="v$.lastName.$errors.map((e) => e.$message)"
              label="Cognome"
              required
              variant="underlined"
              color="deep-purple-lighten-1"
              @blur="v$.lastName.$touch"
              @input="v$.lastName.$touch"
            ></v-text-field>
          </v-col>

          <v-col cols="12" md="6">
            <v-text-field
              v-model="state.birthDate"
              :error-messages="v$.birthDate.$errors.map((e) => e.$message)"
              type="date"
              label="Data di Nascita"
              required
              variant="underlined"
              color="deep-purple-lighten-1"
              @blur="v$.birthDate.$touch"
              @input="v$.birthDate.$touch"
            ></v-text-field>
          </v-col>

          <v-col cols="12" md="6">
            <v-text-field
              v-model="state.birthPlace"
              :error-messages="v$.birthPlace.$errors.map((e) => e.$message)"
              label="Città di Nascita"
              required
              variant="underlined"
              color="deep-purple-lighten-1"
              @blur="v$.birthPlace.$touch"
              @input="v$.birthPlace.$touch"
            ></v-text-field>
          </v-col>

          <v-col cols="12" md="6">
            <v-text-field
              v-model="state.fiscalCode"
              :error-messages="v$.fiscalCode.$errors.map((e) => e.$message)"
              label="Codice Fiscale"
              required
              variant="underlined"
              color="deep-purple-lighten-1"
              @blur="v$.fiscalCode.$touch"
              @input="v$.fiscalCode.$touch"
            ></v-text-field>
          </v-col>
        </v-row>
        <v-btn class="me-4" @click="handleSubmit"> submit </v-btn>

      </form>
    </v-card>
  </v-container>
</template>

<script setup>
import { ref, computed } from 'vue';
import { useVuelidate } from "@vuelidate/core";
import { required } from "@vuelidate/validators";

const state = ref({
  firstName: "",
  lastName: "",
  birthDate: "",
  birthPlace: "",
  fiscalCode: "",
});

const rules = {
  firstName: { required },
  lastName: { required },
  birthDate: { required },
  birthPlace: { required },
  fiscalCode: { required }
};

const v$ = useVuelidate(rules, state.value);

const isFormIncomplete = computed(() => {
  return Object.values(state.value).some(value => !value);
});

function handleSubmit() {
  v$.value.$touch();

  if (!v$.value.$invalid) {
    // Form is valid, proceed with submitting data
    const formData = { ...state.value };
    console.log("Submitting form data:", formData);

    // Here you can call your submit function or perform any necessary action
    // Example: submitFormData(formData);
  } else {
    // Form is invalid, do something (e.g., show error message)
    console.log("Form has validation errors, cannot submit.");
  }
}
</script>
```

ShippingForm.vue
<template>
  <v-container>
    <v-card class="rounded-lg py-4 px-8" elevation="8">
      <form @submit.prevent="handleSubmit">
        <v-row>
          <!-- fare che tipo sia precompilato e non modificabile -->
          <v-col cols="12" md="6">
            <v-text-field
              v-model="state.firstName"
              :error-messages="v$.firstName.$errors.map((e) => e.$message)"
              label="Nome"
              required
              variant="underlined"
              color="deep-purple-lighten-1"
              @blur="v$.firstName.$touch"
              @input="v$.firstName.$touch"
            ></v-text-field>
          </v-col>

          <!-- fare che tipo sia precompilato e non modificabile -->
          <v-col cols="12" md="6">
            <v-text-field
              v-model="state.lastName"
              :error-messages="v$.lastName.$errors.map((e) => e.$message)"
              label="Cognome"
              required
              variant="underlined"
              color="deep-purple-lighten-1"
              @blur="v$.lastName.$touch"
              @input="v$.lastName.$touch"
            ></v-text-field>
          </v-col>

          <v-col cols="12" md="6">
            <v-select
              v-model="state.region"
              :items="regions"
              :error-messages="v$.region.$errors.map((e) => e.$message)"
              label="Regione"
              required
              variant="underlined"
              color="deep-purple-lighten-1"
              @blur="v$.region.$touch"
              @input="v$.region.$touch"
            ></v-select>
          </v-col>

          <v-col cols="12" md="6">
            <v-select
              v-model="state.province"
              :items="provinces"
              :error-messages="v$.province.$errors.map((e) => e.$message)"
              label="Provincia"
              required
              variant="underlined"
              color="deep-purple-lighten-1"
              @blur="v$.province.$touch"
              @input="v$.province.$touch"
            ></v-select>
          </v-col>

          <v-col cols="12" md="6">
            <v-select
              v-model="state.city"
              :items="cities"
              :error-messages="v$.city.$errors.map((e) => e.$message)"
              label="Città"
              required
              variant="underlined"
              color="deep-purple-lighten-1"
              @blur="v$.city.$touch"
              @input="v$.city.$touch"
            ></v-select>
          </v-col>

          <v-col cols="12" md="6">
            <v-text-field
              v-model="state.houseNumber"
              :error-messages="v$.houseNumber.$errors.map((e) => e.$message)"
              label="Civico"
              required
              variant="underlined"
              color="deep-purple-lighten-1"
              @blur="v$.houseNumber.$touch"
              @input="v$.houseNumber.$touch"
            ></v-text-field>
          </v-col>

          <v-col cols="12" md="6">
            <v-text-field
              v-model="state.email"
              :error-messages="v$.email.$errors.map((e) => e.$message)"
              label="E-mail"
              required
              variant="underlined"
              color="deep-purple-lighten-1"
              @blur="v$.email.$touch"
              @input="v$.email.$touch"
            ></v-text-field>
          </v-col>

          <v-col cols="12" md="6">
            <v-text-field
              v-model="state.phoneNumber"
              :error-messages="v$.phoneNumber.$errors.map((e) => e.$message)"
              label="Telefono"
              required
              variant="underlined"
              color="deep-purple-lighten-1"
              @blur="v$.phoneNumber.$touch"
              @input="v$.phoneNumber.$touch"
            ></v-text-field>
          </v-col>
        </v-row>
        <v-btn class="me-4" type="submit" @click="handleSubmit"> submit </v-btn>
      </form>
    </v-card>
  </v-container>
</template>

<script setup>
import { reactive, computed, defineProps } from 'vue';
import { useVuelidate } from "@vuelidate/core";
import {
  email,
  required,
  numeric,
  minLength,
  maxLength,
} from "@vuelidate/validators";

const props = defineProps(['formData']);
const emit = props['onUpdate:formData'];

const initialState = {
  firstName: "",
  lastName: "",
  region: "",
  province: "",
  city: "",
  houseNumber: "",
  email: "",
  phoneNumber: "",
};

const state = reactive({
  ...initialState,
});

const isFormIncomplete = computed(() => {
  return Object.values(state).some(value => !value); // Check if any field is empty
});


const regions = ["Emilia", "Lombardia", "Calabria", "Piemonte"];

const provinces = ["piacenza", "Milano", "Cosenza", "Torino"];

const cities = ["Caorso", "Assago", "Lamezia", "Settimo Torinese"];

const rules = {
  firstName: { required },
  lastName: { required },
  region: { required },
  province: { required },
  city: { required },
  houseNumber: { required, numeric },
  email: { required, email },
  phoneNumber: {
    required,
    numeric,
    minLength: minLength(12),
    maxLength: maxLength(12),
  },
};

const v$ = useVuelidate(rules, state);

const getData = computed(() => {
  return {
    firstName: state.firstName,
    lastName: state.lastName,
    region: state.region,
    province: state.province,
    city: state.city,
    houseNumber: state.houseNumber,
    email: state.email,
    phoneNumber: state.phoneNumber,
  };
});


function handleSubmit() {
  v$.value.$touch();
  emit('formDataSubmitted', formData.value);

  if (!v$.value.$invalid) {
    // Form is valid, proceed with submitting data
    const formData = { ...state };
    console.log("Submitting form data:", formData);

    // Here you can call your submit function or perform any necessary action
    // Example: submitFormData(formData);
  } else {
    // Form is invalid, do something (e.g., show error message)
    console.log("Form has validation errors, cannot submit.");
  }
}
</script>
`
javascript vue.js vuejs3 vuetify.js
1个回答
0
投票

简单的解决方案是为每种表单类型提供布尔标志。如果表单提交有效,则更新相应的布尔标志并显示/禁用下一个按钮。

您需要创建 formDataSubscribed 事件并在成功提交表单时发出此事件。在父组件中监听此事件并更新布尔标志。

<script setup>
const emit = defineEmits(['formDataSubmitted']);

function handleSubmit() {
  ...other code

  if(formValid) {
     emit('formDataSubmitted', formData);
  }
}
</script>

在此处了解有关 Vue 中事件如何工作的更多信息:Vue 事件发射器

© www.soinside.com 2019 - 2024. All rights reserved.