Using Vee-validate to disable button until form is filled out correctly
Asked Answered
L

10

14

I want to disable my submit button until my form is filled out correctly, this is what I have so far:

<form>
   <input type="text" class="form-control" v-validate="'required|email'" name="email" placeholder="Email" v-model="userCreate.userPrincipalName" />
   <span v-show="errors.has('email')">{{ errors.first('email') }}</span>
   <button v-if="errors.any()" disabled="disabled" class="btn btn-primary" v-on:click="sendInvite();" data-dismiss="modal" type="submit">Send Invite</button>
   <button v-else="errors.any()" class="btn btn-primary" v-on:click="sendInvite();" data-dismiss="modal" type="submit">Send Invite</button>
</form>

The above only prints an error message and disables my submit button after I've started inputting a value. I need it to be disabled from the start, before I start interacting with the input, so that I cannot send an empty string.

Another question is if there is a better way than using v-ifto do this?

EDIT:

 userCreate: {
        customerId: null,
        userPrincipalName: '',
        name: 'unknown',
        isAdmin: false,
        isGlobalAdmin: false,
        parkIds: []
    }
Labrie answered 8/11, 2017 at 11:25 Comment(0)
L
10

One way to disable a button until all the values you need are filled, is to use a computed property that will return bool if all values are assigned or not

Example:

Create a computed property like this:

computed: {
  isComplete () {
    return this.username && this.password && this.email;
  }
}

And bind it to the html disabled attribute as:

<button :disabled='!isComplete'>Send Invite</button

This means, disable the button if !isComplete is true

Also, in your case you don't need two if/else-bound buttons. You can use just one to hide/show it based on if the form is completed or has any errors:

<button :disabled="errors.any() || !isCompleted" class="btn btn-primary" v-on:click="sendInvite();" data-dismiss="modal" type="submit">Send Invite</button>

This button will be disabled until all fields are filled and no errors are found

Lunch answered 8/11, 2017 at 11:31 Comment(3)
THIS ANSWER IS TOTALLY WRONG!!!!!!! What's the point of using vee-validate if the computed property has to do all the validations all over again??? The computed property, by the way, is only checking for non null values, but it's not checking for valid email format or any of all the other possible rules!!!!Glassine
@Merlevede. It's extremely helpful to call out an answer as "totally wrong" and then not provide any help or details. What is the right way to do it?Evolution
This answer was given back when vee-validate was unstable and extremely bloated that I wanted to provide an alternative, I urge anyone using vee-validate to refer to other answers using the frameworkLunch
F
16

Probably simpliest way is to use ValidationObserver slot for a form. Like this:

<ValidationObserver v-slot="{ invalid }">
  <form @submit.prevent="submit">
    <InputWithValidation rules="required" v-model="first" :error-messages="errors" />
    <InputWithValidation rules="required" v-model="second" :error-messages="errors" />
    <v-btn :disabled="invalid">Submit</v-btn>
  </form>
</ValidationObserver>

More info - Validation Observer

Fife answered 25/4, 2020 at 21:33 Comment(1)
I believe this is the simplest way. Should be accepted answer.Stein
R
13

Setting up the button to be :disabled:"errors.any()" disables the button after validation. However, when the component first loads it will still be enabled.

Running this.$validator.validate() in the mounted() method, as @im_tsm suggests, causes the form to validate on startup and immediately show the error messages. That solution will cause the form to look pretty ugly. Also, the Object.keys(this.fields).some(key => this.fields[key].invalid); syntax is super ugly.


Instead, run the validator when the button is clicked, get the validity in the promise, and then use it in a conditional. With this solution, the form looks clean on startup but if they click the button it will show the errors and disable the button.

<button :disabled="errors.any()" v-on:click="sendInvite();">
    Send Invite
</button>
sendInvite() {
    this.$validator.validate().then(valid=> {
        if (valid) {
            ...
        }
    })
}

Validator API

Reneerenegade answered 1/3, 2019 at 15:34 Comment(0)
L
10

One way to disable a button until all the values you need are filled, is to use a computed property that will return bool if all values are assigned or not

Example:

Create a computed property like this:

computed: {
  isComplete () {
    return this.username && this.password && this.email;
  }
}

And bind it to the html disabled attribute as:

<button :disabled='!isComplete'>Send Invite</button

This means, disable the button if !isComplete is true

Also, in your case you don't need two if/else-bound buttons. You can use just one to hide/show it based on if the form is completed or has any errors:

<button :disabled="errors.any() || !isCompleted" class="btn btn-primary" v-on:click="sendInvite();" data-dismiss="modal" type="submit">Send Invite</button>

This button will be disabled until all fields are filled and no errors are found

Lunch answered 8/11, 2017 at 11:31 Comment(3)
THIS ANSWER IS TOTALLY WRONG!!!!!!! What's the point of using vee-validate if the computed property has to do all the validations all over again??? The computed property, by the way, is only checking for non null values, but it's not checking for valid email format or any of all the other possible rules!!!!Glassine
@Merlevede. It's extremely helpful to call out an answer as "totally wrong" and then not provide any help or details. What is the right way to do it?Evolution
This answer was given back when vee-validate was unstable and extremely bloated that I wanted to provide an alternative, I urge anyone using vee-validate to refer to other answers using the frameworkLunch
R
3

To check whether a form is invalid or not we can add a computed property like this:

computed: {
    isFormInValid() {
      return Object.keys(this.fields).some(key => this.fields[key].invalid);
    },
},

Now if you want to start checking immediately before user interaction with any of the fields, you can validate manually inside mounted lifecycle hooks:

mounted() {
   this.$validator.validate();
}
Retirement answered 21/12, 2018 at 11:41 Comment(0)
Q
2

or using computed

computed: { 
    formValidated() { 
        return Object.keys(this.fields).some(key => this.fields[key].validated) && Object.keys(this.fields).some(key => this.fields[key].valid);
        } 
}

and use

button :disabled="!formValidated" class="btn btn-primary" v-on:click="sendInvite();" data-dismiss="modal" type="submit">
Quadratics answered 28/6, 2018 at 12:59 Comment(1)
formValidated will be true if one of the fields is valid && validated which is not desiredRetirement
E
2

Another way is to make use of v-validate.initial

<input type="text" class="form-control" v-validate.initial="'required|email'" name="email" placeholder="Email" v-model="userCreate.userPrincipalName" />

This will execute the validation of the email input element after the page is loaded. And makes that your button is disabled before interacting with the input.

Eldoneldora answered 26/11, 2018 at 20:0 Comment(0)
P
1

For the current version 3 (As at the time of writing).

Step 1

Ensure form fields can be watched.

Step 2

Get a reference to the validator instance: <ValidationObserver ref="validator">.

Step 3

Trigger validation silently whenever the form fields change.

Here's an example:

     export default {
        data() {
            return {
                form: {
                    isValid: false,
                    fields: {
                        name: '',
                        phone: '',
                    }
                }
            }
        },

        watch: {
            'form.fields': {
                deep: true,
                handler: function() {
                    this.updateFormValidity();
                }
            }
        },
        methods: {
            async updateFormValidity() {
                this.form.isValid = await this.$refs.validator.validate({
                    silent: true // Validate silently and don't cause observer errors to be updated. We only need true/false. No side effects.
                });
            },
        }
    }
<button :disabled="form.isValid">
    Submit
</button>
Proboscidean answered 30/4, 2020 at 17:4 Comment(1)
Shouldn't it be this.form.isValid = ... instead of this.form.validatorIsValid = ...?Spindry
M
1

For anyone here looking for an answer v4.x version(Vue 3.0)

You can use either use the useForm composition API or the Form component

For the useForm() composition API

const { meta } = useForm({ validationSchema: schema })

For the Form component using slot

<Form v-slot="{ meta }"
       :validation-schema="schema"
       @submit="onSubmit"
    >
      {{ meta.valid }}
</Form>
Mystify answered 14/11, 2023 at 22:4 Comment(0)
C
1

for vuejs version 3, and vee-validate version ^4, i achieved this by simply, using

<template>
....
<button :disabled='!meta.valid'>Submit</button>
....
</template>
<script lang='ts' setup>
import {useForm} from 'vee-validate';

const {meta} = useForm()

Make sure to use that button inside form.

Casimiracasimire answered 29/2 at 21:59 Comment(0)
O
0

You can add computed properties

  ...
  computed: {
      isFormValid () {
          return Object.values(this.fields).every(({valid}) => valid)
      }
  }
  ...

and it bind to the button:

<button :disabled="!isFormValid" class="btn btn-primary" type="submit">Send Invite</button>

i try this on vee-validate version ^2.0.3

Outbrave answered 24/11, 2020 at 11:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.