Dynamic Validation Vue Vee Validate

Hello i want to validate dynamic fields using vee validate, Fields having the same name.
For Example

<template>
<!--begin::Wrapper-->
<div class="row">
<div v-for="key in count" :key="key">
<div class="col-md-3" v-for="field in formSchema.fields" :key="field.name">
<div class="fv-row mb-10" >
<label class="form-label required" :for="field.name">{{ field.label }}</label>
<Field :as="field.as"
:id="field.id+`[${key}]`"
:name="field.name"
:class="field.class"
:key="key"
v-mask="field.mask ? field.mask : false"
/>
<ErrorMessage
:name="field.name"
class="fv-plugins-message-container invalid-feedback"
></ErrorMessage>
</div>
</div>
</div>
<div class="row">
<button
type="button" class="btn btn-lg btn-success me-3" @click="add" id="add_more_fields">
Add Location
</button>
</div>
</div>
<!--end::Wrapper-->
</template>

Text formatting options
Submit
Here's a how to add some HTML formatting to your comment:
  • <pre></pre> for JS codes block
  • <pre lang="html"></pre> for HTML code block
  • <pre lang="scss"></pre> for SCSS code block
  • <pre lang="php"></pre> for PHP code block
  • <code></code> for single line of code
  • <strong></strong> to make things bold
  • <em></em> to emphasize
  • <ul><li></li></ul>  to make list
  • <ol><li></li></ol>  to make ordered list
  • <h3></h3> to make headings
  • <a></a> for links
  • <img> to paste in an image
  • <blockquote></blockquote> to quote somebody
  • happy  :)
  • shocked  :|
  • sad  :(

Replies (4)

Hi Syed,

The name attribute value should be unique for each field.

You can use your key variable to make name value unique and then use the name for validation.

:name="`${field.name}-${key}`"

Regards,
Lauris Stepanovs,
Keenthemes Support Team

Deleted comment

<!--STEP 2 START -->

<template>
<!--begin::Wrapper-->
<div class="row">
<div v-for="key in count" :key="key">
<div class="col-md-3" v-for="field in formSchema.fields" :key="field.name+key">
<div class="fv-row mb-10" >
<label class="form-label required" :for="field.name+key">{{ field.label }}</label>
<Field :as="field.as"
:
:name="field.name+key"
:class="field.class"
:key="key"
v-mask="field.mask ? field.mask : false"
/>
<ErrorMessage
:name="field.name+key"
class="fv-plugins-message-container invalid-feedback"
></ErrorMessage>
</div>
</div>
</div>
<div class="row">
<button
type="button" class="btn btn-lg btn-success me-3" @click="add" >
Add Location
</button>
</div>
</div>
<!--end::Wrapper-->
</template>

<script lang="ts">
import { defineComponent,computed } from "vue";
import { Field, ErrorMessage } from "vee-validate";
import {mask} from 'vue-the-mask'
import { useStore } from "vuex";
import { useRouter } from "vue-router";
import { Actions } from "@/store/enums/StoreEnums";
import { Mutations } from "@/store/enums/StoreEnums";
import * as Yup from 'yup';

export default defineComponent({
name: "step-2",
components: {
Field,
ErrorMessage,
},
directives: {
mask: (el, binding) => {
if (!binding.value) {
return;
}
mask(el, binding);
}
},
setup () {

},
data: function () {
const count = 1;
const formSchema = {
fields: [
{
label: 'Shipper Contact Person',
name: `shipper_contact_person`,
id: `shipper_contact_person`,
as: 'input',
class: 'form-control form-control-lg form-control-solid',

},
{
id: `shipper_phone_number`,
label: 'Shipper Phone Number',
name: 'shipper_phone_number',
class: 'form-control form-control-lg form-control-solid',
as: 'input',
mask:"####-#######",

},
{
id: `shipper_email`,
label: 'Shipper Email Address',
name: `shipper_email`,
class: 'form-control form-control-lg form-control-solid',
as: 'input',

},
{
id: `shipper_city`,
label: 'Shipper City',
name: `shipper_city`,
class: 'form-control form-control-lg form-control-solid',
as: 'input',

},
{
id: `shipper_address`,
label: 'Shipper Address',
name: `shipper_address`,
class: 'form-control form-control-lg form-control-solid',
as: 'textarea',

},
{
id: `shipper_brand_name`,
label: 'Brand Name',
name: `shipper_brand_name`,
class: 'form-control form-control-lg form-control-solid',
as: 'input',

},
{
id: `shipper_cnic`,
label: 'CNIC#',
name: `shipper_cnic`,
class: 'form-control form-control-lg form-control-solid',
as: 'input',
mask : "#####-#######-#"
},
],
};
return {
count,
values: {},
formSchema,
}
},
methods: {
add: function(){
this.count++;
},
remove: function () {
this.count--;
},
submit: function(){
// for (var key of Object.keys(this.values)) {
// console.log(key + " -> " + this.values[key])
// }
}


}
});
</script>

<!--STEP 2 END -->


<!--HORIZONTAL VUE START -->

<template>
<!--begin::Card-->
<div class="separator my-2"></div>
<div class="mb-10 text-center">
<!--begin::Title-->
<h1 class="text-dark mb-3">Sign Up</h1>
<!--end::Title-->
<!--begin::Link-->
<div class="text-gray-400 fw-semobold fs-4">
Already have an account?

<router-link to="/sign-in" class="link-primary fw-bold">
Sign in here
</router-link>
</div>
<!--end::Link-->
</div>
<div class="card">
<!--begin::Card body-->
<div class="card-body">
<!--begin::Stepper-->
<div
class="stepper stepper-links d-flex flex-column"

ref="horizontalWizardRef"
>
<!--begin::Nav-->
<div class="stepper-nav py-5 mt-5">
<!--begin::Step 1-->
<div class="stepper-item current" data-kt-stepper-element="nav">
<h3 class="stepper-title">Profile Information</h3>
</div>
<!--end::Step 1-->

<!--begin::Step 2-->
<div class="stepper-item" data-kt-stepper-element="nav">
<h3 class="stepper-title">Shipping Information</h3>
</div>
<!--end::Step 2-->

<!--begin::Step 3-->
<div class="stepper-item" data-kt-stepper-element="nav">
<h3 class="stepper-title">Banking Information</h3>
</div>

<!--end::Step 3-->

<!--begin::Step 4-->
<div class="stepper-item" data-kt-stepper-element="nav">
<h3 class="stepper-title">Documents & Legal</h3>
</div>
<!--end::Step 4-->

<!--begin::Step 5-->
<div class="stepper-item" data-kt-stepper-element="nav">
<h3 class="stepper-title">Login Information</h3>
</div>
<!--end::Step 5-->
</div>
<!--end::Nav-->

<!--begin::Form-->
<form
class="mx-auto mw-600px w-100 pt-15 pb-10"
novalidate="novalidate"

@submit="handleStep"
>
<!--begin::Step 1-->
<div class="current" data-kt-stepper-element="content" >
<Step1></Step1>
</div>
<!--end::Step 1-->

<!--begin::Step 2-->
<div data-kt-stepper-element="content" >
<Step2></Step2>
</div>
<!--end::Step 2-->

<!--begin::Step 3-->
<div data-kt-stepper-element="content" >
<Step3></Step3>
</div>
<!--end::Step 3-->

<!--begin::Step 4-->
<div data-kt-stepper-element="content" >
<Step4></Step4>
</div>
<!--end::Step 4-->

<!--begin::Step 5-->
<div data-kt-stepper-element="content" >
<Step5></Step5>
</div>
<!--end::Step 5-->

<!--begin::Actions-->
<div class="d-flex flex-stack pt-15">
<!--begin::Wrapper-->
<div class="mr-2">
<button
type="button"
class="btn btn-lg btn-light-primary me-3"
data-kt-stepper-action="previous"
@click="previousStep"
>
<span class="svg-icon svg-icon-4 me-1">
<inline-svg src="media/icons/duotune/arrows/arr063.svg" />
</span>
Back
</button>
</div>
<!--end::Wrapper-->

<!--begin::Wrapper-->
<div>
<button
type="button"
class="btn btn-lg btn-primary me-3"
data-kt-stepper-action="submit"
v-if="currentStepIndex === totalSteps - 1"
@click="formSubmit()"
>
<span class="indicator-label">
Submit
<span class="svg-icon svg-icon-3 ms-2 me-0">
<inline-svg src="media/icons/duotune/arrows/arr064.svg" />
</span>
</span>
<span class="indicator-progress">
Please wait...
<span
class="spinner-border spinner-border-sm align-middle ms-2"
></span>
</span>
</button>

<button v-else type="submit" class="btn btn-lg btn-primary">
Continue
<span class="svg-icon svg-icon-4 ms-1 me-0">
<inline-svg src="media/icons/duotune/arrows/arr064.svg" />
</span>
</button>
</div>
<!--end::Wrapper-->
</div>
<!--end::Actions-->
</form>
<!--end::Form-->
</div>
<!--end::Stepper-->
</div>
<!--end::Card body-->
</div>
<!--end::Card-->
</template>

<script lang="ts">
import { computed, defineComponent, onMounted, ref } from "vue";
import { StepperComponent } from "@/assets/ts/components";
import { useForm } from "vee-validate";
import Swal from "sweetalert2/dist/sweetalert2.min.js";
import * as Yup from "yup";
import Step1 from "@/components/wizard/steps/Step1.vue";
import Step2 from "@/components/wizard/steps/Step2.vue";
import Step3 from "@/components/wizard/steps/Step3.vue";
import Step4 from "@/components/wizard/steps/Step4.vue";
import Step5 from "@/components/wizard/steps/Step5.vue";


interface IStep1 {
contact_name: string;
contact_person: string;
phone_no : string;
phone_no2 : string;
address : string;
cnic : string
city : string
ntn_no : string
url : string
account_type : string
reference_type : string
sale_person : string
}

interface IStep2 {
shipper_contact_person : string;
shipper_phone_number : string;
shipper_email : string;
shipper_city : string;
shipper_address : string;
shipper_brand_name : string;
shipper_cnic : string;
}

interface IStep3 {
businessName: string;
businessDescriptor: string;
businessType: string;
businessDescription: string;
businessEmail: string;
}

interface IStep4 {
nameOnCard: string;
cardNumber: string;
cardExpiryMonth: string;
cardExpiryYear: string;
cardCvv: string;
saveCard: string;
}

interface CreateAccount extends IStep1, IStep2, IStep3, IStep4 {}

export default defineComponent({
name: "kt-horizontal-wizard",
components: {
Step1,
Step2,
Step3,
Step4,
Step5,
},
setup() {
const _stepperObj = ref<StepperComponent | null>(null);
const horizontalWizardRef = ref<HTMLElement | null>(null);
const currentStepIndex = ref(0);
const formData = ref<CreateAccount>({
contact_name: "Contact Name",
contact_person: "Contact Person",
phone_no: "Phone Number",
phone_no2: "Phone Number 2",
address: "Address",
city : "City",
cnic : "CNIC#",
ntn_no : "NTN #",
url : "URL",
account_type : "Account Type",
reference_type : "Reference No#",
sale_person : "Sale Person",

shipper_contact_person : "Shipper Contact Person",
shipper_phone_number : "Shipper Phone Number",
shipper_email : "Shipper Email",
shipper_city : "Shipper City",
shipper_address : "Shipper Address",
shipper_brand_name : "Shipper Brand Name",
shipper_cnic : "Shipper CNIC",

businessName: "Keenthemes Inc.",
businessDescriptor: "KEENTHEMES",
businessType: "1",
businessDescription: "",
businessEmail: "corp@support.com",
nameOnCard: "Max Doe",
cardNumber: "4111 1111 1111 1111",
cardExpiryMonth: "1",
cardExpiryYear: "2",
cardCvv: "123",
saveCard: "1",
});

onMounted(() => {
_stepperObj.value = StepperComponent.createInsance(
horizontalWizardRef.value as HTMLElement
);
});

const createAccountSchema = [
Yup.object({
contact_name: Yup.string().required(),
contact_person: Yup.string().required(),
phone_no: Yup.string().required(),
phone_no2: Yup.string().required(),
address: Yup.string().required(),
city: Yup.string().required(),
cnic: Yup.string().required().min(15),
ntn_no : Yup.string().required(),
url : Yup.string().required(),
account_type : Yup.string().required(),
reference_type : Yup.string().required(),
sale_person : Yup.string().required(),

}),
Yup.object({
shipper_contact_person : Yup.string().required(),
shipper_phone_number : Yup.string().required(),
shipper_email : Yup.string().required(),
shipper_city : Yup.string(),
shipper_address : Yup.string(),
shipper_brand_name : Yup.string() ,
shipper_cnic : Yup.string(),
}),
Yup.object({
businessName: Yup.string().required().label("Business Name"),
businessDescriptor: Yup.string()
.required()
.label("Shortened Descriptor"),
businessType: Yup.string().required().label("Corporation Type"),
businessEmail: Yup.string().required().label("Contact Email"),
}),
Yup.object({
nameOnCard: Yup.string().required().label("Name On Card"),
cardNumber: Yup.string().required().label("Card Number"),
cardExpiryMonth: Yup.string().required().label("Expiration Month"),
cardExpiryYear: Yup.string().required().label("Expiration Year"),
cardCvv: Yup.string().required().label("CVV"),
}),
];

const currentSchema = computed(() => {
return createAccountSchema[currentStepIndex.value];
});

const { resetForm, handleSubmit } = useForm<
IStep1 | IStep2 | IStep3 | IStep4
>({
validationSchema: currentSchema,
});

const totalSteps = computed(() => {
if (!_stepperObj.value) {
return;
}

return _stepperObj.value.totatStepsNumber;
});

const handleStep = handleSubmit((values) => {
// resetForm({
// values: {
// ...formData.value,
// },
// });

console.log(formData.value);
for (const item in values) {
// eslint-disable-next-line no-prototype-builtins
if (values.hasOwnProperty(item)) {
if (values[item]) {
formData.value[item] = values[item];
}
}
}

currentStepIndex.value++;

if (!_stepperObj.value) {
return;
}

_stepperObj.value.goNext();
});

const previousStep = () => {
if (!_stepperObj.value) {
return;
}

currentStepIndex.value--;

_stepperObj.value.goPrev();
};

const formSubmit = () => {
Swal.fire({
text: "All is cool! Now you submit this form",
icon: "success",
buttonsStyling: false,
confirmButtonText: "Ok, got it!",
customClass: {
confirmButton: "btn fw-semobold btn-light-primary",
},
}).then(() => {
window.location.reload();
});
};

return {
horizontalWizardRef,
previousStep,
handleStep,
formSubmit,
totalSteps,
currentStepIndex,
};
},
});
</script>

<!--HORIZONTAL VUE END-->

I have attached (step 2) and the (horizontal) vue page, is there way to validate all the dynamic fields inside the step2, I feel like Yup Object is ignoring dynamic fields

Hi Syed,

I think "Form Generator" page describes how you can validate dynamically rendered fields. See https://vee-validate.logaretm.com/v4/tutorials/dynamic-form-generator#prerequisites

Regards,
Lauris Stepanovs,
Keenthemes Support Team

Text formatting options
Submit
Here's a how to add some HTML formatting to your comment:
  • <pre></pre> for JS codes block
  • <pre lang="html"></pre> for HTML code block
  • <pre lang="scss"></pre> for SCSS code block
  • <pre lang="php"></pre> for PHP code block
  • <code></code> for single line of code
  • <strong></strong> to make things bold
  • <em></em> to emphasize
  • <ul><li></li></ul>  to make list
  • <ol><li></li></ol>  to make ordered list
  • <h3></h3> to make headings
  • <a></a> for links
  • <img> to paste in an image
  • <blockquote></blockquote> to quote somebody
  • happy  :)
  • shocked  :|
  • sad  :(
Text formatting options
Submit
Here's a how to add some HTML formatting to your comment:
  • <pre></pre> for JS codes block
  • <pre lang="html"></pre> for HTML code block
  • <pre lang="scss"></pre> for SCSS code block
  • <pre lang="php"></pre> for PHP code block
  • <code></code> for single line of code
  • <strong></strong> to make things bold
  • <em></em> to emphasize
  • <ul><li></li></ul>  to make list
  • <ol><li></li></ol>  to make ordered list
  • <h3></h3> to make headings
  • <a></a> for links
  • <img> to paste in an image
  • <blockquote></blockquote> to quote somebody
  • happy  :)
  • shocked  :|
  • sad  :(