Using Vee-validate to disable button until form is

2020-07-06 06:28发布

问题:

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: []
    }

回答1:

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



回答2:

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



回答3:

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.



回答4:

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();
}


回答5:

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">


回答6:

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



回答7:

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>