我正在尋找一種良好且可重用的方法來訪問錯誤物件內可能存在的嵌套屬性(嵌套物件和物件陣列),而沒有 typeErrors。
我有一個createCompany包含以下資料的表單/頁面
data() {
return {
company: {
same_billing_address: true,
physical_address: {},
billing_address: {},
contacts: [
{
function: '',
first_name: '',
last_name: '',
phone: '',
gender: 'female',
email: '',
language: 'nl',
date_of_birth: '',
},
],
},
validationErrors: {},
}
}
表格本身看起來像這樣
<form @submit.prevent="createCompany" @keydown.enter="$event.preventDefault()" class="divide-y">
<fieldset class="pb-6">
<header>
<h3 class="mb-3 text-lg leading-6 font-medium text-gray-900">{{ $tc('general', 1) }}</h3>
</header>
<div class="grid grid-cols-12 gap-x-6 gap-y-3">
<div class="col-span-12">
<InputWithButton :label="$tc('enterprise_number', 1)" buttonLabel="Get enterprise data" :onClick="getEnterpriseData" type="text" id="enterprise_number" v-model="company.enterprise_number" :error="validationErrors.enterprise_number" />
</div>
<div class="col-span-6">
<Input :label="$tc('business_name', 1)" type="text" id="companyName" v-model="company.business_name" :error="validationErrors.business_name" />
</div>
<div class="col-span-6">
<Input :label="$tc('legal_entity_type', 1)" type="text" id="companyType" v-model="company.legal_entity_type" :error="validationErrors.legal_entity_type" />
</div>
<div class="col-span-3">
<Input :label="$tc('phone', 1)" type="text" id="phone" v-model="company.phone" :error="validationErrors.phone" />
</div>
<div class="col-span-6">
<Input :label="$tc('email_address', 1)" type="text" id="email" v-model="company.email" :error="validationErrors.email" />
</div>
</div>
</fieldset>
<fieldset class="py-6">
<header>
<h3 class="mb-3 text-lg leading-6 font-medium text-gray-900">{{ $tc('physical_address', 1) }}</h3>
</header>
<div class="grid grid-cols-12 gap-x-6 gap-y-3">
<div class="col-span-8">
<Input :label="$tc('street', 1)" type="text" id="street" v-model="company.physical_address.street" :error="validationErrors.physical_address.street" />
</div>
<div class="col-span-2">
<Input :label="$tc('number', 1)" type="text" id="number" v-model="company.physical_address.number" :error="validationErrors.physical_address.number" />
</div>
<div class="col-span-2">
<Input :label="$tc('addition', 1)" optional type="text" id="addition" v-model="company.physical_address.addition" :error="validationErrors.physical_address.addition" />
</div>
<div class="col-span-8">
<SelectWithSearch :label="$tc('city', 1)" id="billing_address_postal_code_id" v-model="company.physical_address.postal_code_id" :options="cityOptions" displayProperty="display_name" valueProperty="id" :minLengthForDropdown="3" :error="validationErrors.physical_address.zip_city" />
</div>
<div class="col-span-4">
<Input :label="$tc('country', 1)" type="text" id="country" v-model="company.physical_address.country" :error="validationErrors.physical_address.country" />
</div>
</div>
</fieldset>
<fieldset class="py-6">
<header>
<h3 class="mb-3 text-lg leading-6 font-medium text-gray-900">{{ $tc('billing_address', 1) }}</h3>
</header>
<div class="grid grid-cols-12 gap-x-6 gap-y-3">
<div class="col-span-12">
<Checkbox :label="$tc('billing_same_as_physical', 1)" v-model="company.same_billing_address" :error="validationErrors.same_billing_address" />
</div>
<template v-if="!company.same_billing_address">
<div class="col-span-8">
<Input :label="$tc('street', 1)" type="text" id="street" v-model="company.billing_address.street" :error="validationErrors.billing_address.street" />
</div>
<div class="col-span-2">
<Input :label="$tc('number', 1)" type="text" id="number" v-model="company.billing_address.number" :error="validationErrors.billing_address.number" />
</div>
<div class="col-span-2">
<Input :label="$tc('addition', 1)" type="text" id="addition" v-model="company.billing_address.addition" :error="validationErrors.billing_address.addition" />
</div>
<div class="col-span-8">
<SelectWithSearch :label="$tc('city', 1)" id="billing_address_postal_code_id" v-model="company.billing_address.postal_code_id" :options="cityOptions" displayProperty="display_name" valueProperty="id" :minLengthForDropdown="3" :error="validationErrors.billing_address.zip_city" />
</div>
<div class="col-span-4">
<Input :label="$tc('country', 1)" type="text" id="country" v-model="company.billing_address.country" :error="validationErrors.billing_address.country" />
</div>
</template>
</div>
</fieldset>
<fieldset class="py-6">
<div class="flex justify-between mb-3">
<header>
<h3 class="text-lg leading-6 font-medium text-gray-900">{{ $tc('contact', company.contacts.length) }}</h3>
</header>
<button type="button" class="text-sm leading-6 font-medium text-blue-500 flex items-center" @click="addContact">{{ $tc('add', 1) }} {{ $tc('contact', 1).toLowerCase() }}</button>
</div>
<section class="space-y-6">
<div v-for="(contact, contactIdx) in company.contacts" :key="contactIdx">
<h4 v-show="company.contacts.length > 1" class="mb-3 text-sm leading-6 font-medium text-gray-500">
{{ $tc('contact', 1) }} {{ contactIdx 1 }} <span @click="deleteContact(contactIdx)" class="text-blue-500 cursor-pointer select-none">({{ $tc('delete', 1) }})</span>
</h4>
<div class="grid grid-cols-12 gap-x-6 gap-y-3">
<div class="col-span-12">
<RadioButtonGroup :label="$tc('gender', 1)" :options="genderOptions" v-model="contact.gender" :error="contacts[contactIdx].gender" />
</div>
<div class="col-span-6">
<Input :label="$tc('first_name', 1)" type="text" id="first_name" v-model="contact.first_name" :error="contacts[contactIdx].first_name" />
</div>
<div class="col-span-6">
<Input :label="$tc('last_name', 1)" type="text" id="last_name" v-model="contact.last_name" :error="contacts[contactIdx].last_name" />
</div>
<div class="col-span-3">
<Input :label="$tc('phone', 1)" type="text" id="phone" v-model="contact.phone" :error="contacts[contactIdx].phone" />
</div>
<div class="col-span-6">
<Input :label="$tc('email_address', 1)" type="text" id="email" v-model="contact.email" :error="contacts[contactIdx].email" />
</div>
<div class="col-span-3">
<Input :label="$tc('date_of_birth', 1)" type="date" id="date_of_birth" v-model="contact.date_of_birth" :error="contacts[contactIdx].date_of_birth" />
</div>
<div class="col-span-9">
<Input :label="$tc('function', 1)" type="text" id="function" v-model="contact.function" :error="contacts[contactIdx].function" />
</div>
<div class="col-span-3">
<Select :label="$tc('language', 1)" id="languageOfContact" :options="languageOptions" displayProperty="display_name" valueProperty="name" v-model="contact.language" :error="contacts[contactIdx].language" />
</div>
</div>
</div>
</section>
</fieldset>
<fieldset class="pt-6">
<SubmitButton :label="$tc('create_company', 1)" submittingLabel="Creating company..." />
</fieldset>
</form>
在資料發送到后端之前,它會被驗證
async createCompany() {
try {
await CreateCompanyValidationSchema.validate(this.company, { abortEarly: false });
console.log('all good');
} catch (err) {
console.log(err.inner);
err.inner.forEach((error) => {
this.validationErrors = { ...this.validationErrors, [error.path]: error.message };
});
}
}
我Yup用來驗證表單。架構看起來像這樣
export const CreateCompanyValidationSchema = yup.object().shape({
enterprise_number: yup.string(),
business_name: yup.string(),
legal_entity_type: yup.string(),
phone: yup.string().required(),
email: yup.string().required().email(),
language: yup.string().required(),
first_name: yup.string(),
last_name: yup.string(),
date_of_birth: yup.date(),
physical_address: yup.object({
street: yup.string().required(),
number: yup.string().required(),
addition: yup.string(),
zip_city: yup.string().required(),
country: yup.string().required(),
}),
same_billing_address: yup.boolean(),
billing_address: yup.object().when('same_billing_address', {
is: false,
then: yup.object({
street: yup.string().required(),
number: yup.string().required(),
addition: yup.string(),
zip_city: yup.string().required(),
country: yup.string().required(),
}),
}),
contacts: yup.array().of(
yup.object().shape({
gender: yup.string().required().oneOf(['male', 'female', 'other']),
first_name: yup.string().required(),
last_name: yup.string().required(),
phone: yup.string().required(),
email: yup.string().required().email(),
date_of_birth: yup.date().required(),
function: yup.string().required(),
language: yup.string().required().oneOf(['nl', 'fr', 'en']),
})
),
});
The validationErrors data object has a nested object structure (physical_address and billing_address) and a nested array of objects (contacts). The validationErrors object is empty in the beginning. If the nested address fields or the contacts are valid, the validationErrors object will not have any nested properties. But in the form I'm accessing child properties like validationErrors.contacts[contactIdx].phone or validationErrors.billing_address.street. This causes errors because these properties do not exist. What is the best approach to counter this? I'm looking for a reusable solution for multiple forms with this structure.
uj5u.com熱心網友回復:
在 Vue 3 中,可以在模板中直接使用可選鏈(安全導航)運算子來訪問不存在的鍵:
:error="contacts?.[contactIdx]?.first_name"
在 Vue 2 中,這需要將此代碼移動到一個方法中,并可能使用字串指定嵌套屬性的路徑,例如使用 Lodashget或類似的輔助函式,它還允許為不存在的鍵指定默認值。
uj5u.com熱心網友回復:
一種選擇是使用短路來安全地訪問嵌套屬性,僅當它們存在時。
if ( validationErrors && validationErrors.billing_address ) {
console.log(validationErrors.billing_address.street)
}
if 陳述句僅在validationErrors.billing_address定義時才為真,但如果validationErrors未定義則不會拋出錯誤。
如果您認為這種模式太丑陋和重復,您可能對使用庫來幫助您訪問嵌套屬性感興趣。 https://lodash.com/docs/#get
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/380325.html
標籤:javascript forms vue.js vuejs3 yup
上一篇:當div被點擊而不是按鈕時,如何讓表單輸入顯示為查詢引數
下一篇:將值附加到URL
