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: []
    }
Green_qaue
  • 3,561
  • 11
  • 47
  • 89

8 Answers8

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

Saulius
  • 1,736
  • 20
  • 29
12

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

brenthompson2
  • 391
  • 3
  • 9
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

samayo
  • 16,163
  • 12
  • 91
  • 106
  • 43
    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!!!! – Merlevede Oct 26 '18 at 19:08
  • 15
    @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? – Ryan Eastabrook Feb 14 '19 at 15:55
  • 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 framework – samayo Feb 09 '22 at 09:53
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">
Amr Eladawy
  • 4,193
  • 7
  • 34
  • 52
jayadevkv
  • 384
  • 3
  • 16
  • `formValidated` will be `true` if one of the fields is `valid` && `validated` which is not desired – im_tsm Dec 21 '18 at 11:23
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.

2

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();
}
im_tsm
  • 1,965
  • 17
  • 29
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>
Emeke Ajeh
  • 933
  • 1
  • 10
  • 16
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

Shay Yzhakov
  • 975
  • 2
  • 13
  • 31