Skip to content

Validation

Mev-Rael edited this page Feb 26, 2016 · 1 revision

Form can be divided to many steps (tabs).

Recommended way styling form tabs is Bootstrap 4 nav tabs and there is BunnyJS TabList component to work with tab contents, tab panels and associated nav and links.

.tab-content must have an ID attribute.

Validate with TabList when user can switch to any tab

If user can change step to any tab listen to on_focus event, check if invalid input is in another tab and swith tab manually. Example:

Validate.validate(document.getElementById('add_edit_user_form'), {
  on_focus: function(form_input, form_group) {

    // if form input is inside tab pane, check and switch to required tab pane
    var parent = form_group.parentNode;
    if (parent.classList.contains('tab-pane')) {
      if (!parent.classList.contains('active')) {
        TabList.changeTab(parent.getAttribute('id'));
      }
    }

    // focus  and scroll to visible part of invalid input
    // if we are using .fade class to animate tab switching wrap into setTimeout()
    setTimeout(function() {
      Validate.focus(form_input);
    }, 200);

  }
});

Validate with TabList, custom ajax validator and form validation by steps

Sometimes only parts of form should be validated not all form. For example:

  • when only one row should be checked,
  • or only one step
  • or if user can't change tabs at any time
  • or there are no associated <nav> with tab links to switch tab form UI for .tab-content.

In that case form should be manually validated step by step using Validate.validateSection(). In the example below there is also a custom ajax valdiation:

<form id="steps_form" ...>

<div id="steps_tab_content" class="tab-content"> <!--  adding ID to tab-content  -->

  <div class="tab-pane active" id="step1" role="tabpanel"> <!--  first section with id="step1" and also .active  -->
    <form group and input ...>
    <!-- each input must be within form group element which contains only one input, only one label and can contain only
    one container for error message. We are using default Bootstrap 4 markup here -->
    <div id="email_group" class="form-group">  <!-- id added to this form group because we will add custom ajax validator for this input -->
      <input type="email" name="email">  <!--  below is example how to valdiate email via ajax and check if email is already registered -->
    </div>

    <button id="btn_step1" type="button" class="btn btn-primary">Go to step 2</button> <!--  Creating button at the end of each section and later will add event listener on click  -->
  </div>

  <div class="tab-pane" id="step2" role="tabpanel"> <!--  next section with id="step2" and without .active (hidden)  -->
    <input ...>
    <input ...>

    <button id="btn_step2" type="button" class="btn btn-primary">Continue</button>  <!--  step 2 button  -->
  </div>

  <div class="tab-pane" id="step3" role="tabpanel">  <!--  last section with id="step3"  -->
    <input ...>
    <input ...>

    <button id="btn_finish" type="button" class="btn btn-primary">Finish</button>    <!--  and last button  -->
  </div>
</div>

</form>

And now custom JS to validate form by sections:

// when user clicks on "Go to step 2" button at the end of first section
document.getElementById('btn_step1').addEventListener('click', function() {

  // validateSection() returns true when section is valid
  if (Validate.validateSection(document.getElementById('step1'))) {
    // when first section is valid AJAX request is made to check if email exists

    // first need to get form group element and input element.
    // getInputElementsByFormGroup(form_group_id_or_el) will return an object with keys: formGroup, input and label
    // to make custom validator we need formGroup and input
    var email_input_elements = Validate.getInputElementsByFormGroup('email_group');

    // making an AJAX request to check if e-mail already registered
    Ajax.get('/api/v1/users/check-email/' + encodeURI(email_input_elements.input.value), function(data) {
      var $data = JSON.parse(data);
      if ($data.status !== 'success') {

        // email already exists, create error message
        // setErrorMessage(form_group_el, msg)
        Validate.setErrorMessage(email_input_elements.formGroup, 'E-mail "' + email_input_elements.input.value + '" is already registered!');
        // changing focus and scrolling to invalid input
        Validate.focus(email_input_elements.input);

      } else {

         // email is not registered and our last custom validator is passed, change to next tab.
         TabList.changeTab('step2');
      }
    });

  }
});

// second button will be shown only after first section passed because it is inside hidden section by default
// and becomes visible whentab is changed
// Adding event listener when user clicks on second step button
document.getElementById('btn_step2').addEventListener('click', function() {
  // when we don't have cusotm ajax validators it's very easy
  if (Validate.validateSection(document.getElementById('step2'))) {
    // if section 2 is valid, go to next section
    TabList.changeTab('step3');
  }
});

// and the last section event listener, since we don't have a submit button in form
// we need to manually submit form when last section is valid.
document.getElementById('btn_finish').addEventListener('click', function() {

  if (Validate.validateSection(document.getElementById('step3'))) {
    document.getElementById('steps_form').submit();
  }
});