2
\$\begingroup\$

I use JS/JQuery validation for radioboxes, input field etc. Because of AJAX I use one global validation (in a separate file) and 4-5 other files to coverage actions in a relevant registration form pages. These actions are repeated in a few places so I need to refactor it to be in line with DRY.

I have validation for checkbox which I use in global validation (below) and I have to use the same code, four times in a separate file for each registration pages (if I delete just them in those files, global validation doesn't replace it). Is there any way to be in line with DRY principle? can I take the whole global action to variable var globalValidation and refer to him in individual actions like in method in Rails?

global validations

$(document).ready(function () {
  var form = $('#userRegistrationForm');
  var submit = form.find('input[type="submit"]');

  submit.click(function(evt) {
    evt.preventDefault();

    var invalidInput = $('input:invalid');

    if (invalidInput.length === 0) {
      form.submit();
    } else {
      invalidInput.addClass('invalid');
      validateRadios();
    }
  });

  function validateRadios() {
    var radio = $('input[type=radio]');
    var radioInvalid = $('input[type=radio]:invalid');
    var radioMsg = $('.radio-invalid-msg')

    if (radio) {
      if (radioInvalid.length > 0) {
        radioMsg.addClass('invalid');
      }

      radio.click(function () {
        if (radio.is(':checked')) {
          radio.removeClass('invalid');
          radioMsg.removeClass('invalid');
        } else if (radio.is(':not(:checked)')) {
          radio.addClass('invalid');
          radioMsg.addClass('invalid');
        }
      });
    }
  }
});

repeated validation in create.rb

$(document).ready(function () {
  var form = $('#userRegistrationForm');
  var submit = form.find('input[type="submit"]');


  submit.click(function(evt) {
    evt.preventDefault();

    var invalidInput = $('input:invalid');

    if (invalidInput.length === 0) {
      form.submit();
    } else {
      invalidInput.addClass('invalid');
      validateRadios();
    }
  });

function validateRadios() {
  var radio = $('input[type=radio]');
  var radioInvalid = $('input[type=radio]:invalid');
  var radioMsg = $('.radio-invalid-msg')

  if (radio) {
    if (radioInvalid.length > 0) {
      radioMsg.addClass('invalid');
    }

    radio.click(function () {
      if (radio.is(':checked')) {
        radio.removeClass('invalid');
        radioMsg.removeClass('invalid');
      } else if (radio.is(':not(:checked)')) {
        radio.addClass('invalid');
        radioMsg.addClass('invalid');
      }
    });
  }
}

EDIT:

Forms are generated by AJAX, if user reload one page of the form, global validation will take care of validation (replacing validations for a given step).

for example - this is a view for on of the page:

_update_user.html.erb

<%= link_to t('registrations.new.back'), back_registrations_path(step: 1), remote: true, class: "back-btn"%>

<div class="row">
  <div class="col-xs-12 col-sm-12 col-md-12 update-user__header">
    <h3 class="text__blue"><%= t('.whats_your_name') %></h3>
  </div>
</div>
<%= form_for current_user, as: :user, url: user_path, method: :patch, aria_role: "form", html: { id: "userRegistrationForm" }, remote: true do |f| %>
  <div class="row">
    <div class="update-user__input-wrapper">
      <div class="col-sm-6 col-md-6 col-xs-12 update-user__name-input--first">
        <div class="floating-label">
          <%= f.text_field :first_name, class: "update-user__name-input floating-field", placeholder: t('.first_name'), required: true, aria_required: true %>
          <%= f.label :first_name, t('.first_name'), class: "floating-label-placeholder" %>
          <span class="update-user__name-input--invalid-msg">
            <%= t'.obligatory' %></span>
        </div>
      </div>
      <div class="col-sm-6 col-md-6 col-xs-12 update-user__name-input--last">
        <div class="floating-label">
          <%= f.text_field :last_name, class: "update-user__name-input floating-field", placeholder: t('.last_name'), required: true, aria_required: true %>
          <%= f.label :last_name, t('.last_name'), class: "floating-label-placeholder" %>
          <span class="update-user__name-input--invalid-msg">
            <%= t'.obligatory' %></span>
        </div>
      </div>
    </div>
  </div>
  <div class="row">
    <div class="text-gray update-user__gender-wrapper">
      <div class="col-md-12 col-sm-12 col-xs-12">
        <%= t('.whats_your_gender') %>
      </div>
    </div>
    <div class="col-sm-2 col-md-2 col-xs-12">
      <div class="vr-radio-group">
        <div class="vr-radio update-user__radio">
          <label>
            <%= f.radio_button :gender, "female", class: "update-user__radio-input", required: true %>
            <div class="vr-radio__radio"></div>
            <span class="vr-radio__desc update-user__radio-desc text-gray">
              <%= t'.female' %></span>
          </label>
        </div>
        <br>
        <div class="vr-radio update-user__radio">
          <label>
            <%= f.radio_button :gender, "male", class: "update-user__radio-input", required: true %>
            <div class="vr-radio__radio"></div>
            <span class="vr-radio__desc update-user__radio-desc text-gray">
              <%= t'.male' %></span>
          </label>
        </div>
        <span class="radio-invalid-msg">
          <%= t'.obligatory' %></span>
      </div>
    </div>
  </div>
  <div class="row">
    <div class="col-sm-12 col-md-12 col-xs-12 form-submit text-center">
      <%= f.submit t('.submit'), class: "submit-btn" %>
    </div>
  </div>
<% end %>

And here are the validation for this action:

create.js.erb

$(".card-content").html("<%= escape_javascript(render(partial: 'users/update_user') ) %>");

animateSidebar();

$(document).ready(function () {
  var form = $('#userRegistrationForm');
  var submit = form.find('input[type="submit"]');

  submit.click(function(evt) {
    evt.preventDefault();

    var invalidInput = $('input:invalid');
    var firstName = $('#user_first_name').val();
    var lastName = $('#user_last_name').val();

    $("#user-name").text(firstName + " " + lastName);

    if (invalidInput.length === 0) {
      form.submit();
    } else {
      invalidInput.addClass('invalid');
      validateInput();
      validateRadios();
    }
  });

  function validateInput() {
    $('input').change(function () {
      if ($(this).is(':valid')) {
        $(this).removeClass('invalid');
      }
    });
  }

  function validateRadios() {
    var radio = $('input[type=radio]');
    var radioInvalid = $('input[type=radio]:invalid');
    var radioMsg = $('.radio-invalid-msg')

    if (radio) {
      if (radioInvalid.length > 0) {
        radioMsg.addClass('invalid');
      }

      radio.click(function () {
        if (radio.is(':checked')) {
          radio.removeClass('invalid');
          radioMsg.removeClass('invalid');
        } else if (radio.is(':not(:checked)')) {
          radio.addClass('invalid');
          radioMsg.addClass('invalid');
        }
      });
    }
  }
});

I think the best way is to remove validation from create.js, put it all in to global validation, pack it as function formValidation and placing it in four places where I have the same validation. Is it possible? Should I refer to it in some way? e.g. after submit.click ? I don't know if JQuery allows in such a practice.

\$\endgroup\$
  • \$\begingroup\$ Welcome to Code Review. Please show enough context for the code (such as how you generate the forms), so that we can give you specific advice that is more helpful than "Well, just don't write the same code twice!" \$\endgroup\$ – 200_success Jul 10 at 15:28
  • \$\begingroup\$ Hi @200_success thank you for your response! I did not want to flood with unnecessary code, this app is quite big for me. Post is updated, please let me know if you want to see something more. \$\endgroup\$ – mr_muscle Jul 10 at 18:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.