Flo's jQuery validate step

by Florian Bar

HTML

<link rel="stylesheet" href="http://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.1/jquery.validate.min.js"></script>
<script src="http://netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js"></script>
<form id="form1">

    <div id="accordion-wizard" class="accordion"> 
      <h3 v-bind:class="{ 'is-done': currentStep > 1, 'is-active': currentStep == 1 }">
        Step 1
      </h3>
      <div id="step-1" class="collapse show in" data-parent="#accordion-wizard">
        <div class="form-group">
          <div class="form-check">
            <input id="directors-1" class="form-check-input" 
              type="radio" name="directorCount" value="1"  v-model="directorCount">
            <label class="form-check-label" for="directors-1">1</label>
          </div>
          <div class="form-check">
            <input id="directors-2" class="form-check-input" 
              type="radio" name="directorCount" value="2" v-model="directorCount">
            <label class="form-check-label" for="directors-3">2</label>
          </div>
          <div class="form-check disabled">
            <input id="directors-3" class="form-check-input" 
              type="radio" name="directorCount" value="3" v-model="directorCount">
            <label class="form-check-label" for="directors-3">3</label>
          </div>
        </div>

        <button type="button" class="btn btn-primary" v-on:click="goToNextStep()">Next</button>
      </div>
      
      <h3 v-bind:class="{ 'is-done': currentStep > 2, 'is-active': currentStep == 2 }">
        Step 2
      </h3>
      <div id="step-2" class="collapse" data-parent="#accordion-wizard">
          <div class="form-group">
            <label>First Name</label>
            <input class="form-control" name="firstName" type="text" v-model="form.info.firstName" />
 ...

SCSS

body {
  font-family: arial;
  font-size: 14px;
  padding: 20px;
}

.collapse:not(.in) {
  // fix bootstrap accordion bug
  display: none !important;
}

.is-active {
  background-color: blue;
  color: #fff;
}

.is-done {
  background-color: green;
  color: #fff;
}

.form-control.error {
  margin-bottom: 0;
}

.error {
  display: block;
  margin-bottom: 15px;
  font-size: 12px;
  color: red;
}

JavaScript

var app = new Vue({
  el: "#form1",
  data: {
    currentStep: 1, // starts at 1
    directorCount: 1,
    form: {
    	info: { firstName: "", lastName: "", email: "", tel: "" },
      company: { name: "" },
      directors: [
      	new Director()
      ]
    }
  },
  watch: {
    currentStep: function (newStep, prevStep) {
      $("#step-" + newStep).collapse("show");
      //$("#step-" + prevStep).collapse("hide");
    },
    directorCount: function (newDirectorCount, prevDirectorCount) {
      var diff = Math.abs(newDirectorCount - prevDirectorCount);
      
      for (var i = 0; i < diff; i++) {
        if (newDirectorCount > prevDirectorCount) {
        	this.form.directors.push(new Director()); // add director
          addDirectorValidationRules(this.form.directors.length-1);
        } else {
        	this.form.directors.pop(); // remove director
        }
      }
    }
  },
  methods: {
  	goToPrevStep: function() {
       this.currentStep--;
    },
    goToNextStep: function() {
    	// ajax for server side validation
      var stepData = this.form[this.currentStep];
      
      if ($("#form1").valid()) {
        this.currentStep++;
      }
    }
  }
});

function Director() {
	this.firstName = "";
  this.lastName = "";
}

function addDirectorValidationRules(directorIndex) {
  setTimeout(function() {
    $("[name='directorFirstName" + directorIndex + "']").rules("add", {
      required: true,
      messages: {
        required: "Please specify the director's first name"
      }
    });

    $("[name='directorLastName" + directorIndex + "']").rules("add", {
      required: true,
      messages: {
        required: "Please specify the director's last name"
      }
    });
  });
}

$(function() {            
    $("#form1").validate({
        rules: {
        		// Step 1
            directorCount: "required",
        		// Step 2
            firstName: "required",
            lastName: "required",
            email: { required: true, email: true },
       ...