Thursday, March 3, 2016

Validating Oracle JET Input Before Submission

Oracle JET includes a very convenient validation module to validate user input. Even though it validates input immediately, it only displays validation warnings/errors after a user visits a validated component. This is as it should be. Can you imagine visiting a data entry page that immediately displayed all validation errors before you entered your first value? Oracle JET quietly maintains a list of validation messages in the collection messagesHidden. You can see this in action by navigating over to the Validation recipe in the Oracle JET cookbook and then pasting the following into the console:

$("#username").ojInputText("option", "messagesHidden");

The result will look something like the following:

Now enter some data into the username field and replay the last console command (hint: use the up arrow). Notice that the result collection is now empty.

Now, let's say you have a data entry form and you want to ensure validation occurs before a user submits data. What if a user skipped over a required field? Now that the username field has a value, enter the following into the console:

$("#username").ojInputText("isValid");

The console should return true. Now you can confirm validation and cancel submission if the form fails validation, but what about displaying that validation error message for fields that fail validation? Refresh the recipe page so that username has no value and validation warnings are hidden and then type the following into the JavaScript console:

$("#username").ojInputText("validate");

That highly valuable validation error message should reappear.

That is great, but do I need to check the isValid status of every field in my form? Do I need to invoke the validate method on every input field? In a word: YES (Note: if you are using knockout, there is an easier way. See Validating Oracle JET Input Before Submission Take II). But don't worry, you don't have to list every single field. We can let jQuery selectors handle this for us. Here is how:


var validateSelector = function(selector, componentName) {
  // Note: using multi-step selectors for two reasons:
  // #1: make it easier to pass in distinct part of selector
  // #2: sizzle selectors work in reverse, so we want to narrow selected
  // elements BEFORE testing data-bind.
  $(selector)
    .filter("[data-bind]")
    .filter("[data-bind*='" + componentName + "']")
    .each(function() {
      //console.log("Invoking validator on", this);
      $(this)[componentName]("validate");
    });
};

var isValidSelector = function(selector, componentName) {

  var $elements = $(selector)
    .filter("[data-bind]") // has a data-bind attribute
    .filter("[data-bind*='" + componentName + "']");

  // using a for loop to exit immediately if false
  for (var idx = 0, len = $elements.length; idx < len; idx++) {
    if (!$($elements[idx])[componentName]("isValid")) {
      return false;
    }
  }

  // fell through, so return true
  return true;
};


validateSelector("input[type='text']", "ojInputText");
validateSelector("input[type='password']", "ojInputPassword");
validateSelector("select", "ojSelect");

// first selector to fail will end the chain and return false
var formIsValid = isValidSelector("input[type='text']", "ojInputText") &&
  isValidSelector("input[type='password']", "ojInputPassword") &&
  isValidSelector("select", "ojSelect");

I certainly haven't covered every selector possible, but hopefully this is enough to communicate the idea. Of course, you don't want to paste this code into every single ViewModel. Instead, throw it into a RequireJS module and include it in ViewModels that require validation. In fact, why not display messages and check the validation status all at once by writing the module to just expose a single method that does both?

... Or, if you are using knockout, you could skip the whole jQuery selector thing by using an oj.invalidComponentTracker (Thank you JB for showing me this feature).

No comments:

Post a Comment