Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

VeeValidate attach method: A field is missing a "name" or "data-vv-name" attribute when fields do have name

I have a parent component rendering a personal details component and am injecting the parent's validator scope. if I use the v-validate directive and this.$validator.validateAll() or this.$validator.validate('field_name') this works fine.

However, I need to validate some fields independently, but when using this.$validator.attach('first_name', 'required') for example I am getting the following warning [vee-validate] A field is missing a "name" or "data-vv-name" attribute. I have tried moving the call to attach the validators into the click handler, in case the input element isn't fully rendered when calling it from mounted() but still get the same problem. I have also tried both name or data-vv-name attributes independently.

Parent.ts

import { Vue, Component } from 'vue-property-decorator';

import PersonalDetailsComponent from './PersonalDetails';

@Component({
    template: `
        <div class="container">
            <personal-details-component></personal-details-component>

            <div class="row">
                    <Button :onClick="handleButtonClick" :buttonText="'Validate'"></Button>
            </div>
        </div>
    `,
    components: {
        PersonalDetailsComponent,
    },
    $_veeValidate: {validator: 'new'}
})
export default class ClaimComponent extends Vue {

    mounted() {
        this.attachValidators();
    }

    handleButtonClick() {
        this.$validator.validateAll();
    }

    attachValidators() {
        console.log(document.getElementsByName('first_name')); // Finds the element

        this.$validator.attach('first_name', 'required');
        this.$validator.attach('surname', 'required');
        this.$validator.attach('email', 'required');
    }

}

PersonalDetails.ts

import {Vue, Component, Inject} from 'vue-property-decorator';

import {Validator} from 'vee-validate';

@Component({
    template: `
    <div class="row">
        <div class="col-12">
            <form class="material-form">
                <div class="group w-third">
                    <input v-model="first_name" type="text" name="first_name" required>
                    <span class="highlight"></span>
                    <span class="bar"></span>
                    <label>First name</label>
                    <span class="form-text error-text">{{ errors.first('first_name') }}</span>
                </div>
                <div class="group w-third">
                    <input v-model="surname" type="text" name="surname" required>
                    <span class="highlight"></span>
                    <span class="bar"></span>
                    <label>Surname</label>
                    <span class="form-text error-text">{{ errors.first('surname') }}</span>
                </div>
                <div class="group w-third">
                    <input v-model="email" type="text" name="email" required>
                    <span class="highlight"></span>
                    <span class="bar"></span>
                    <label>Email address</label>
                    <span class="form-text error-text">{{ errors.first('email') }}</span>
                </div>
            </form>
        </div>
    </div>
    `
})
export default class PersonalDetailsComponent extends Vue {
    @Inject('$validator') public $validator!: Validator;

    first_name: string = '';
    surname: string = '';
    email: string = '';

}
like image 551
Matthew Moss Avatar asked Dec 02 '25 23:12

Matthew Moss


2 Answers

I think you might be thinking about it wrong. Ideally the validation logic should be within the child component, not the parent. But you should still be able to use the parent to run the validations. As you are injecting the validator instance from the parent, you should be able to update your child component to the following:

export default class PersonalDetailsComponent extends Vue {
    @Inject('$validator') public $validator!: Validator;

    first_name: string = '';
    surname: string = '';
    email: string = '';

    public mounted() {
        this.$validator.attach('first_name', 'required');
        this.$validator.attach('surname', 'required');
        this.$validator.attach('email', 'required');
    }
}

Then you can remove the attachValidators method from the parent. This should attach those validation rules to the validator instance being provided by the parent. So in theory the parent can run this.$validator.validateAll(); and it should validate based on the rules within the child component.

like image 76
George Hanson Avatar answered Dec 05 '25 15:12

George Hanson


I've managed to achieve what I needed - validation of particular fields - by using the data-vv-validate-on attribute along with a custom event. This way I can fire an event on whichever inputs need validating when the button is clicked.

Parent component updated to:

@Component({
    template: `
        <div class="container">
            <personal-details-component ref="personalDetails"></personal-details-component>

            <div class="row">
                    <Button :onClick="handleButtonClick" :buttonText="'Validate'"></Button>
            </div>
        </div>
    `,
    components: {
        PersonalDetailsComponent,
    },
    $_veeValidate: {validator: 'new'}
})
export default class ClaimComponent extends Vue {
    $refs!: {
        personalDetails: PersonalDetailsComponent
    }

    handleButtonClick() {
        this.$refs.personalDetails.validateInput();
    }
}

And PersonalDetails component:

@Component({
    template: `
    <div class="row">
        <div class="col-12">
            <form class="material-form">
                <div class="group w-third">
                    <input v-model="first_name" ref="firstName" type="text" name="first_name"
                    v-validate="'required'" 
                    data-vv-validate-on="validateStep" required>
                    <span class="highlight"></span>
                    <span class="bar"></span>
                    <label>First name</label>
                    <span class="form-text error-text">{{ errors.first('first_name') }}</span>
                </div>
                <div class="group w-third">
                    <input v-model="surname" ref="surname" type="text" name="surname" 
                    v-validate="'required'"
                    data-vv-validate-on="validateStep" required>
                    <span class="highlight"></span>
                    <span class="bar"></span>
                    <label>Surname</label>
                    <span class="form-text error-text">{{ errors.first('surname') }}</span>
                </div>
                <div class="group w-third">
                    <input v-model="email" ref="email" type="text" name="email" 
                    data-vv-validate-on="validateStep" v-validate="'required|email'" 
                    required>
                    <span class="highlight"></span>
                    <span class="bar"></span>
                    <label>Email address</label>
                    <span class="form-text error-text">{{ errors.first('email') }}</span>
                </div>
            </form>
        </div>
    </div>
    `
})
export default class PersonalDetailsComponent extends Vue {
    @Inject('$validator') public $validator!: Validator;
    $refs!: {
        firstName: HTMLInputElement,
        surname: HTMLInputElement,
        email: HTMLInputElement
    }

    first_name: string = '';
    surname: string = '';
    email: string = '';

    public validateInput() {
        this.$refs.firstName.dispatchEvent(new Event('validateStep'));
        this.$refs.surname.dispatchEvent(new Event('validateStep'));
        this.$refs.email.dispatchEvent(new Event('validateStep'));
    }
}

I still don't understand why the attach method was not working, but this alternative does work.

like image 24
Matthew Moss Avatar answered Dec 05 '25 15:12

Matthew Moss



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!