1

I have a submit button that is initially disabled (through v-bind:disabled) and will only be enabled until all form inputs are non-empty and errors are resolved. For each of my input, I have a spinner that will become a check or an x after verifying the validity of the input. This takes a few seconds and I was hoping that the same delay be applied to the enabling of the submit button. At the moment, this is what the form looks like:

enter image description here

This is the form (in pug):

.form-group
  label Email Address
  .input-group
    input.form-control(type="email" name="emailAddress" value=profile.email
      v-model="email"
      v-validate
      data-vv-delay="1000"
      data-vv-rules="required|email"
      data-vv-as="email"
      :class="{ 'input': true, 'is-danger': errors.has('emailAddress') }"
      placeholder="eg. andres@gmail.com")
    .input-group-append
      span.input-group-text
        i.fal.fa-pulse.fa-spinner(v-if="email && emailBusy")
        i.fal.fa-check.text-green(v-if="email && !emailBusy && !isEmailTaken && !errors.has('emailAddress')")
        i.fal.fa-times.text-red(v-if="email && !emailBusy && (isEmailTaken || errors.has('emailAddress'))")
  span.text-danger.text-error(v-show="errors.has('emailAddress')") {{ errors.first('emailAddress') }}
  span.text-danger.text-error(v-if="email && email.length > 0 && isEmailTaken") Email address has already been taken
.form-group
  label Username
  .input-group
    input.form-control(type="name" name="username"
      v-model="username"
      v-validate
      data-vv-delay="1000"
      data-vv-rules="required|verify_username"
      :class="{ 'input': true, 'is-danger': errors.has('username') }"
      placeholder="eg. andres45")
    .input-group-append
      span.input-group-text
        i.fal.fa-pulse.fa-spinner(v-if="username && usernameBusy")
        i.fal.fa-check.text-green(v-if="username && !usernameBusy && !isUsernameTaken && !errors.has('username')")
        i.fal.fa-times.text-red(v-if="username && !usernameBusy && (isUsernameTaken || errors.has('username'))")
  span.text-danger.text-error(v-show="errors.has('username')") {{ errors.first('username') }}
  span.text-danger.text-error(v-if="username && username.length > 0 && isUsernameTaken") Username has already been taken
.form-group
  button.btn.btn-blue(:disabled="errors.any() || isEmailTaken || isUsernameTaken || !username || !email" type="submit")
    i.fal.fa-sign-in.mr-2
    span Complete Sign Up

The vue instance:

var register = new Vue({ 
  el: '#register',
  data: {
    email: email,
    username: null,

    isUsernameTaken: false,
    usernameTimer: null,
    usernameBusy: false,

    isEmailTaken: false,
    emailTimer: null,
    emailBusy: false
  },
  methods: {
    validateEmail: function(email) {
      var self = this;
      var url = '/api/users?email=' + email;
      self.$http.get(url)
        .then(function(res){
          self.isEmailTaken = true;
          self.emailBusy = false;
        }, function(err){
          self.isEmailTaken = false;
          self.emailBusy = false;
        });
    },
    validateUsername: function(username) {
      var self = this;
      var url = '/api/users/' + username;
      self.$http.get(url)
        .then(function(res){
          self.isUsernameTaken = true;
          self.usernameBusy = false;
        }, function(err){
          self.isUsernameTaken = false;
          self.usernameBusy = false;
        });
    }
  },
  watch: {
    username: function(val) {
      var self = this;
      clearTimeout(self.usernameTimer);
      self.usernameBusy = true;
      self.usernameTimer = setTimeout(function() {
        self.validateUsername(val);
      }, 1600);
    },
    email: function(val) {
      var self = this;
      clearTimeout(self.emailTimer);
      self.emailBusy = true;
      self.emailTimer = setTimeout(function() {
        self.validateEmail(val);
      }, 1600);
    }
  }
});
Bargain23
  • 1,863
  • 3
  • 29
  • 50
  • I don't know pug so I can't tell what is going on in your template, but if you are checking for error with a variable, why not bind that to the button? – samayo Mar 31 '18 at 23:09
  • @samayo I am checking for errors given this line `button.btn.btn-blue(:disabled="errors.any() || isEmailTaken || isUsernameTaken || !username || !email" type="submit")`. The problem is the button becomes enabled instantaneously even before the loading spinner turns to a check. If I input a username with a valid format but has already been taken, the button becomes enabled regardless. It will only become disabled again after some time has passed and the API returns an error saying that the username has already been taken. – Bargain23 Mar 31 '18 at 23:13
  • 1
    You can add a new variable inside your data and call it, `isLoading: true`, then add this inside your button for the disabled attribute as: `button.btn-btn-blue(:disabled="!isLoading.... )` now whenever you make a request, inside `$http.get().then(` .. set the `isLoading` to false if you get an OK response, and set it to true inside `..catch` or the second function and set it to `true` which means there will be an error. something like this: https://stackoverflow.com/questions/49231975/hide-delay-password-confirmation-error-in-vee-validate/49236236#49236236 – samayo Mar 31 '18 at 23:23

1 Answers1

0

I’m on mobile so apologies about formatting and lack of code. For me I would probably set up a variable to track the disabled state, say var isFormComplete = false; I would use the vue disabled prop to control the button/form state. :disabled=“!isFormComplete”.

Then I would set up a vue watch or even computed method in the JS which basically will check if each form control is empty or whatever arbitrary value checking you want to do, since it’s dynamic behind the scenes with variables it should be pretty simple to check through each form control and when all conditions are satisfied, set the isFormComplete or whatever you want to call it to true and your control will be enabled.

For extra fun and bonus points, you could set up some generic validation code to be reusable and abstract it out as a vue mixin and have yourself a nifty custom form validation you can reuse. I know this isn’t a traditional answer but since I’m mobile I felt this was more indepth than a comment even though there is a lack of code. You can add a timer as well in the validation method to when all conditions are satisfied delay by however long you want and then set the disabled variable to false.

Kavindra
  • 1,697
  • 2
  • 14
  • 19
Nard Dog
  • 906
  • 1
  • 18
  • 33