Thursday, March 24, 2016

Using FontAwesome and Glyphicons with Oracle JET

Oracle JET includes an excellent list of common icons in the Framework Images Recipe. To use one, add the oj-fwk-icon and icon specific class to any element (<i> is a common element). For example:

<i class="oj-fwk-icon oj-fwk-icon-magnifier"></i>

The Oracle JET Cookbook contains great examples of using the oj-fwk-icons with common Oracle JET components. Sometimes, however, I find I need more icons. Two of my favorite icon libraries are FontAwesome and Glyphicons (the Bootstrap ones). To use either of these libraries, first include the library's CSS. Second, replace the oj-fwk-icon and oj-fwk-icon-* classes with the appropriate library specifc class. For example, to use the FontAwesome magnifier, use fa fa-search. I put together a very short jsFiddle showing how to use FontAwesome with some of the more common Oracle JET elements such as ojButton and ojMenu:

Filtering Oracle JET ArrayTableDataSource

I recently wrote about Filtering Table Data with Knockout Computeds. I'm not sure I shared anything earth shattering as others have already written about that topic. My real motivation for writing that post was to provide a foundation to compare/contrast filtering a plain HTML table against an Oracle JET ojTable that is based on an oj.ArrayTableDataSource (not to be confused with oj.CollectionTableDataSource, which wraps an oj.Collection and has its own where method and cookbook example).

Let's jettify the Filtering Table Data with Knockout Computeds example. Instead of <input type="text">, we'll use ojInputText. Instead of a Bootstrap table, we'll use ojTable. Here is the jsFiddle:

As you can see, the code is fairly similar. A couple of key differences to note are the rawValue parameter and the lack of a computed. Instead of using the value and valueUpdate parameters, the ojInputText component uses value and rawValue. The ojInputText value parameter acts just like the standard knockout parameter, tracking the final updated value after losing focus. The rawValue parameter tracks changes as you type, which is similar to the standard input with the valueUpdate parameter. The other difference between these examples is that this example didn't use a computed observable, but rather the oj.ArrayTableDataSource.reset() method to replace the table's array when filtering.

As with the Knockout computed example, we can debounce this example so that the array filter code doesn't run with every key press, but only after a predetermined pause. I included the rate limiting extension in the JavaScript, but commented it out for example purposes. When filtering large data sets, it might make sense to switch from observing rawValue to observing the value parameter. Another option is to include a Search button that triggers the filter code on click. You can find an example of this here.

Note: Filtering arrays requires an ES6 compatible browser or some type of polyfill library. The examples include Lazy.js as a polyfill so you can run the jsFiddle example in a wider range of web browsers.

Wednesday, March 23, 2016

Filtering Table Data with Knockout Computeds

Before learning to love Knockout, I was an AngularJS fan. AngularJS has this really cool feature called filters that let you pipe a collection through a filter, filtering results based on the value of a field. The AngularJS filter page has a great inline example. You can filter with Knockout, but it isn't quite as simple. The key to filtering in Knockout is the computed observable. I put together an example:

This example contains a search field in the upper right corner as well as a table of employee names. If the filter field is empty, then the table should display all employees. If the filter field contains a value, then the table should display only employees with names containing the search value. When you look at the JavaScript for this example, you will see:

  1. An array of employees (the raw, unobserved data),
  2. An observable for the search value, and
  3. A computed for the filtered table

Drilling into the computed observable (filteredEmployees), we see that the function immediately returns the list of all employees if the nameSearch observable has no value. If it has a value, then it returns a filtered array of matching results.

I put together 2 examples: One with ES6 Array and String extensions and a Lazy.js version. The example above is the Lazy.js version. ES6 is great, but jsPerf tests show better results for ko.utils, underscore, Lazy.js, or just about any other non-native library. I also hesitate to use the ES6 Array.prototype.filter for browser compatibility reasons. Just in case you are interested, Here is the ES6 version. Why Lazy.js instead of just ko.utils.arrayFilter? I am a big fan of Lazy.js's function composition rather than the traditional chained intermediate array concept (even though this example doesn't exactly chain enough array methods together to see a performance improvement from Lazy.js).

On the HTML View side, the search field's data-bind attribute uses the valueUpdate parameter. This causes Knockout to update the ViewModel on some other event besides the change event. That way users can see changes as they type. What this means is as you type, the filter code will run, filtering the results displayed in the table. We have a small data set, so you won't notice, but on a larger data set, this could have serious performance implications because each key press would iterate over the Employees array. We can limit how often knockout recomputes the computed observable by debouncing, or rate limiting, updates of nameSearch field like this:

self.nameSearch.extend({
    rateLimit: {
      timeout: 500,
      method: "notifyWhenChangesStop"
    }
  });

You can see an example here. Notice that the update is a little choppier, meaning the table filters a half second after you stop typing. A half second may be a little too long between updates. The important part is that the code recalculates the computed AFTER the specified event pauses for a predetermined interval.

Fiddling Around with Oracle JET

I learn new ideas, technologies, etc by example. First I am presented with a challenge (a new project, new idea, etc) and then I search for ways others have built similar solutions. While looking through search results, I almost always pause when I see a stackoverflow response. I think I learn more from stackoverflow than I do from any other source on the Internet. Often times while reading a stackoverflow answer, I'll find myself in a jsFiddle, manipulating someone else's example in an attempt to understand the solution. Wouldn't it be great to find Oracle JET fiddles demonstrating ideas that are not in the JET cookbook?

A key to building a jsFiddle that references a library is accessibility to that library. Many of Oracle JET's dependencies are hosted on Content Delivery Networks (CDNs), making them accessible to jsFiddle. At this time, Oracle JET itself is not available on a CDN. Oracle JET is, however, accessible via GitHub. This is good news because there is a site named RawGit that serves GitHub project files through MaxCDN. If you want to access the Oracle JET Alta skin for example, enter https://github.com/oracle/oraclejet/blob/master/dist/css/alta/oj-alta-min.css into the file name field and RawGit will give you a URL such as https://cdn.rawgit.com/oracle/oraclejet/master/dist/css/alta/oj-alta-min.css.

I went through the Oracle JET 2.0 bower file and created a RequireJS configuration based on JET's dependencies. This is step #1 in creating a jsFiddle. Here is what that CDN version looks like (note: I used the RawGit dev URL, not the production URL, for jsFiddle):

requirejs.config({
  // Path mappings for the logical module names
  paths: {
    'knockout': '//cdnjs.cloudflare.com/ajax/libs/knockout/3.4.0/knockout-min',
    'jquery': '//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min',
    "jqueryui": "//cdnjs.cloudflare.com/ajax/libs/jqueryui/1.11.4/jquery-ui",
    "jqueryui-amd": "//rawgit.com/jquery/jquery-ui/1-11-stable/ui",

    "promise": "//cdn.lukej.me/es6-promise/1.0.0/promise.min",
    "hammerjs": "//cdnjs.cloudflare.com/ajax/libs/hammer.js/2.0.4/hammer.min",
    "ojdnd": "//rawgit.com/oracle/oraclejet/master/dist/js/libs/dnd-polyfill/" +
        "dnd-polyfill-1.0.0.min",
    "ojs": "//rawgit.com/oracle/oraclejet/master/dist/js/libs/oj/debug",
    "ojL10n": "//rawgit.com/oracle/oraclejet/master/dist/js/libs/oj/ojL10n",
    "ojtranslations": "//rawgit.com/oracle/oraclejet/master/dist/js/libs/oj/" +
        "resources",
    "text": "//cdnjs.cloudflare.com/ajax/libs/require-text/2.0.12/text.min",
    "signals": "//cdnjs.cloudflare.com/ajax/libs/js-signals/1.0.0/js-signals.min",

  },
  // Shim configurations for modules that do not expose AMD
  shim: {
    'jqueryui-amd': {
      exports: "$",
      deps: ['jquery']
    },
    'jquery': {. The 
      exports: ['jQuery', '$']
    }
  },
  config: {
    ojL10n: {
      merge: {
        //'ojtranslations/nls/ojtranslations': 'resources/nls/menu'
        // The following addes en-US to the r.js bundle
        //'ojtranslations/nls/ojtranslations': '../../oj/resources/nls/en-US/localeElements'
      }
    }
  }
});

I combined all of this into a new jsFiddle.

I didn't think it was very exciting to just put the RequireJS config and Alta CSS into the fiddle without any content. I wanted a simple example that showed how RequireJS resolved dependencies using the relative ojs paths so I borrowed from the Input Date and Time cookbook entry.

Please fiddle and fork to your heart's content!

NOTE: The jsFiddle is a living example, meaning I make changes to the base fiddle on occasion to keep it current. The code displayed in this post, however, won't change. For example, the jsFiddle currently uses cdn.rawgit where as the code in this post uses the development rawgit server. When in doubt, trust the fiddle.

Update 25-Apr-2016: Oracle JET 2.0.1 was just released. Without any changes, the original jsFiddle nicely began using 2.0.1. As I was considering releases, it occurred to me that a future release of JET may require different dependencies. The JET libraries would be always current, but the dependencies (knockout, hammer, jQuery, etc) may be become irrelevant. With that in mind, I updated the fiddle for this post to point specifically to the 2.0.0 commit from February 29, 2016. I will add another jsFiddle for 2.0.1 and announce it as a blog post.

Thursday, March 17, 2016

Sort-of Validation Messages -- How to use Oracle JET Custom Messages

Let's say you have a required field on a form and a user doesn't enter anything into that field. That empty field deserves a validation error message. Oracle JET's built-in validation handles this scenario quite nicely. Let's consider another scenario: A user enters an invalid user name and password. Is this a validation issue or some other type of error? Could you write an Oracle JET validator for credentials validation? One of the key differences between the required validation and a credentials validation is the asynchronous data-driven nature of credentials validation. Validating credentials likely requires an Ajax request. What do you do while waiting for a response? Can you write a validator that waits for an Ajax response before returning true/false (Yes, but technically it would be a blocking SJax (synchronous) request and nobody likes blocking synchronous requests)? Does it even matter?

I am a fan of Oracle JET's inline messages. When writing the failure logic for a login page, my first question is, "How can I reuse those cool inline Oracle JET messages to tell the user that authentication failed?" I can't exactly write an authentication validator because of the asynchronous nature of credential validation. On form submit, I won't know if the credentials are valid until I receive a response. By that time, validation is over. An alternative is to notify the user through Oracle JET's inline messages. Keeping with the authentication example, let's say you have an ojInputPassword component and you want to display an error message under the password (inline) when authentication fails. To that ojInputPassword element, add a messagesCustom attribute and bind it to an observable. For example:

<input id="password" name="password" data-bind="ojComponent: {
    component: 'ojInputPassword', value: password,
    messagesCustom: authenticationMessages,
    invalidComponentTracker: $component.tracker}" type="password"
    required />

In your ViewModel you will have a corresponding observable definition for authenticationMessages like so:

self.authenticationMessages = ko.observableArray([]);

Then when that Ajax authentication request returns a failure, you can insert an object into that authenticationMessages observable like this:

self.authenticationMessages.push({
  summary: "Authentication failed",
  detail: "Invalid user ID or password",
  severity: oj.Message.SEVERITY_TYPE.ERROR
});

The invalidShown field of our oj.invalidComponentTracker (named tracker, see Validating Oracle JET Input Before Submission Take II) will now return true (tracker.invalidShown === true). Here is a sample showing the inline error message as well as the tracker object and authenticationMessages array.

So now our user knows that his or her credentials are invalid and can enter some new credentials. What about that visible error message? Since we didn't use a custom validator to display the message, it isn't going to disappear based on user input. Rather, we need to control the presence (or absence) of that error message. It is my thought that once the user changes the user ID or password, that error message should disappear and only reappear if the new credentials are invalid. I see two ways to make this happen:

  1. Bind an event handler to the change event of both the user ID and password fields
  2. Subscribe to the user ID and Password observables

Either is acceptable, but I prefer the subscription route. Here is what that might look like:

var resetAuthenticationFailure = function() {
  self.authenticationMessages.removeAll();
};

self.userid.subscribe(resetAuthenticationFailure);
self.password.subscribe(resetAuthenticationFailure);

In the code sample above (above the image), do you see the severity attribute of the new message object? The messagesCustom isn't just for error messages. Done right, you can use it for information and confirmation messages as well. To see a "kitchen sink" example of messagesCustom, visit the Help, Hints and Messaging: Inline Multiple Messages recipe of the Oracle JET Cookbook.

Validating Oracle JET Input Before Submission Take II

For a blogger, I would say the best part about blogging is learning something new from a reader. My latest schooling comes from Oracle JET expert JB and involves Oracle JET's built-in validation. I learned a lot about the jQuery syntax for validating unvisited form fields and confirming validation before submission while writing the post Validating Oracle JET Input Before Submission. This is great... if you are using jQuery. If you are using knockout, however, there is another way: oj.InvalidComponentTracker. Scroll down the oj.editableValue document until you see the Binding Attributes section. Every oj.editableValue (which includes ojInputText, ojInputPassword, ojSelect, and just about every other input type) has an invalidComponentTracker attribute. Just set each validated input field's invalidComponentTracker attribute to the same observable and then you can test that one observable from your submit function. The App Level Validation: Dynamic Form Example cookbook recipe is a great example. Here are the basics: in your ViewModel, add:

self.tracker = ko.observable();

For each validated input in the view, add the invalidComponentTracker attribute like this:

<input id="password" required data-bind="ojComponent: {
    component: 'ojInputPassword', value: user.password,
    invalidComponentTracker: tracker}" type="password">

Oracle JET will will set the tracker observable to an instance of oj.InvalidComponentTracker. In your submit handler you can test for invalidHidden and invalidShown messages. If either of those fields return true, then you know the form contains invalid input. For visited fields, validation failure messages should be visible. For unvisited fields, you can make validation messages visible by invoking the oj.InvalidComponentTracker showMessages method.

Here is an example of the JavaScript I now use in my form submit routine to ensure validation was successful prior to submission:

var tracker = self.tracker();
if (tracker.invalidHidden || tracker.invalidShown) {
  tracker.showMessages();
  tracker.focusOnFirstInvalid();
  return;
}

Tip: Be sure to add 'ojs/ojknockout-validation' to your ViewModel's define/require block.

Wednesday, March 16, 2016

Hash-style Routing with Oracle JET

Oracle JET is a modular toolkit which includes a significant list of "tools." As developers we are welcome to use one, some, all, or none of the Oracle JET features. For example, I am a big fan of the knockout enabled JET Data Visualizations. Another tool in the Oracle JavaScript Extension Toolkit is the Router. While certainly a nice feature, supporting query string and path-style routing, I prefer hash-style routing.

Routing a knockout-based SPA is fairly trivial. First, we need an observable to hold the current route, the value being the name of a knockout component. Next, we need a view that contains a placeholder for that knockout component. The final piece is some JavaScript to listen for URL changes to update this observable. I prefer Crossroads, but there are many others. Crossroads expects a URL pattern (the route) and a callback to invoke when the current URL matches that route. Here is a sample AMD module that:

  • Stores the currently selected route in a member named currentRoute,
  • A list of all routes in a member named routes, and
  • Contains a method for activating routing. This method is responsible for adding each route as well as setting up each route's callback (which just updates the currentRoute observable)
define(["knockout",
  "crossroads",
  "hasher",
  "jquery"
], function(ko, crossroads, hasher, $) {
  'use strict';

  var router = {
    routes: undefined,
    currentRoute: ko.observable({}),

    activate: function(routes) {
      router.routes = routes;
      ko.utils.arrayForEach(routes, function(route) {
        crossroads.addRoute(route.url, function(requestParams) {
            router.currentRoute(ko.utils.extend(requestParams, route.params));
        });
      });

      var parseHash = function(newHash) {
        crossroads.parse(newHash);
      };

      crossroads.normalizeFn = crossroads.NORM_AS_OBJECT;
      hasher.initialized.add(parseHash);
      hasher.changed.add(parseHash);
      hasher.init();
    }
  };

  return router;
});

To use this router, our main ViewModel needs to setup an array of route ⇒ component mappings and maintain a pointer to the currentRoute member. Here is what that ViewModel might look like:

require(['knockout',
  'router',
], function(ko, router) {
  'use strict';

  // the routes
  var routes = [{
    url: '',
    params: {
      component: 'home',
    }
  }, {
    url: 'about',
    params: {
      component: 'about'
    }
  }, {
    url: 'parameter-example/{id}',
    params: {
      component: 'parameters'
    }
  }, {
    url: 'query-example{?query}',
    params: {
      component: 'querystring',
    }
  }];

  // register the components identified by the routes
  ko.utils.arrayForEach(routes, function(r) {
    var name = r.params.component;
    if (!ko.components.isRegistered(name)) {
      ko.components.register(name, {
        require: "components/" + name + "/viewModel"
      });
    }
  });

  // configure router
  router.activate(routes);

  // start the app
  ko.applyBindings({
    route: router.currentRoute
  });
});

Note: see that small loop in there that iterates over each component identified by the routes and registers each route as a Knockout component? If all of our components follow the same pattern, we can actually push a custom loader onto the knockout loader stack that will resolve any component by convention rather than configuration. Here is an example:

// Custom configration-based loader. Loads components by naming convention
ko.components.loaders.push({
  getConfig: function(name, callback) {
    callback({
      require:  "components/" + name + "/viewModel"
    });
  }
});

Now we just need a view that exposes the current route's component:

<!DOCTYPE html>

<html lang="en-us">
  <head>
    <title>Sample</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!-- core CSS -->
    <!-- build:css(.) styles/vendor.css -->
    <!-- bower:css -->
    <link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.min.css" />
    <!-- endbower -->
    <!-- endbuild -->
    <!-- build:css(.tmp) styles/main.css -->
    <link rel="stylesheet" href="styles/main.css" />
    <!-- endbuild -->

    <!-- RequireJS bootstrap file -->
    <script data-main="js/main.min" src="bower_components/requirejs/require.min.js"></script>

  </head>
  <body>

    <div class="container">
      <!-- Route-specific content. Routes are assigned in main.js and defined
      as Knockout components -->
      <main
            data-bind="component: { name: route().component }"></main>
    </div>
  </body>
</html>

Assuming we have View/ViewModel combinations in folders named home, about, parameters, and querystring, we can visit URLs like http://localhost:9000/, http://localhost:9000/#/about, http://localhost:9000/#/parameter-example/101, and http://localhost:9000/#/query-example?first=Curtis&last=Feitty and see results.

With our routes available in a separate module, we could take this example a step further by introducing a few more attributes to our routing metadata that we could use in a header component to create a global navigation bar. For example, if we added isGlobal attributes to each route that should appear in a navigation list, then we could use a filter to list all global routes in a navigation bar. Likewise, by adding an iconClass attribute, we could use glyphicons or fontawesome glyphs to display an image next to each link. And, since the router AMD module maintains the state of the current route in an observable, we could setup a computed to highlight the active route. Here is a screenshot of what that might look like:



Synchronizing ojSelect Dependent Value List Observables

Dependent value lists are lists of values that change based on some other field. Country and state are common examples. If Country has no value, then the list of states (or provinces, territories, etc) should be empty. After selecting a country, the options within state should change to match relevant options for that country. Knockout computeds offer a great mechanism for maintaining dependent lists. Here are a couple of good examples of using computeds to maintain dependencies:

But what happens when the dependent list's selected value becomes invalid after changing the underlying list of options? For example, what value should the "state" field contain when the country changes, rendering the current "state" selection invalid? What about the data model? Should the underlying observable share the same value as what is shown on the screen? When the options list of a select changes and the model's value is not in the list of options (an invalid value), the default Knockout behavior is to update the model to contain the first (selected) option in the list (see valueAllowUnset). This may or may NOT be the right approach, which is why knockout allows us to change its behavior through the valueAllowUnset parameter. Oracle JET's ojSelect takes the opposite approach. When the options list changes, invalidating the selected option, ojSelect does NOT write back to the model. While this may be desirable (as shown in the valueAllowUnset parameter), it may lead to a situation where the display on the screen does not match the underlying data model. In the following recording, notice that the country starts as Canada and the State is Newfoundland. After switching to Country: United States, the state switches to Alabama. This seems reasonable because Canada does not contain the state Alabama and the United States does not contain a state named Newfoundland. What isn't obvious by this recording, however, is that the change to state doesn't affect the bound observable.

One method to keep the screen and the data model in sync is to subscribe to the optionChange event. When the options list changes, and the selected option is not in the list, ojSelect will trigger the optionChange event (because the selected option changed), but not write back to the data model. Your subscription handler can choose to update the data model observable with the newly selected option. Here is some sample HTML showing the optionChange attribute:

<select id="state" data-bind="ojComponent: {component: 'ojSelect',
    options: stateList, value: stateSelected,
    placeholder: '', optionChange: stateOptionChangedHandler}" required></select>

... and the stateOptionChangeHandler JavaScript:

self.stateOptionChangedHandler = function(event, data) {
  if (data.option === "value") {
    var value = data.value[0];
    var observable = self.selectedState;

    // only set if the option value change didn't update the observable
    // we want the underlying data to match the screen
    if (value !== observable()) {
      console.log("setting value from options handler", value, observable());
      observable(value);
    }

  }
};

Now replay the recording above. Notice the output in the console window? The recording above uses the optionChange handler presented here to write back to the observable when the option changes by some mechanism other than the user actually selecting a new value. What you see printed in the console window is the new value (Alabama) followed by the old observable value (Newfoundland and Labrador).

Chances are you will have multiple dependent value lists. Who wants to repeat that code for every list? Here is my generic library function:

var valueOptionChangeHandler = function(observable, event, data) {
  if (data.option === "value") {
    var value = data.value[0];

    // only set if the option value change didn't update the observable
    // we want the underlying data to match the screen
    if (value !== observable()) {
      console.log("setting value from options handler", value, observable());
      observable(value);
    }

  }
};

I can then "curry" an observable into a new function that I use as my optionChange handler like this:

self.stateOptionChangedHandler = ojsHelper.valueOptionChangeHandler
  .bind(undefined, self.selectedState);

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).

Unwrapping Oracle JET's ojSelect value binding

The ojSelect component is a very powerful alternative to the HTML <select> element. It has a long list of impressive features including a type-ahead search box (for long lists), pill-like multi-select, and the ability to include images in the options list. If you are building web applications connected to Oracle applications (like me), then you can't help but appreciate the prepackaged Alta skin as well.

One of the issues I struggled with when switching from the traditional HTML <select> element to ojSelect was the value binding. The single <select> element returns a single value (or whatever the selected row in the option binding represents) whereas ojSelect single select returns an array. Even though the ojSelect value array has just one element, it is still an array. If my data model doesn't expect an array, then this can cause problems when binding the data model to an ojSelect in the view layer. Here are a couple of options I have used to work around the ojSelect array value:

  1. Bind to a temporary observable within the ViewModel and then marshal content from that temporary observable into the data model on save.
  2. Use a read/write computed observable to maintain state between the ViewModel and the Model.

One reason for using a 2-way data binding architecture, such as knockout, is so I don't have to copy values between the view and the model, so option #1 is not a favorite of mine. Option #2 is similar in that it uses a temporary observable in the ViewModel, but it is a little different in that I don't have to specifically transfer data between the Model and the ViewModel. Rather, it is more like connecting some plumbing and letting knockout stream data between the two. Here is what that might look like:

require(['ojs/ojcore', 'knockout', 'jquery', 'ojs/ojknockout',
  'ojs/ojselectcombobox'
], function(oj, ko, $) {
  // make ko accessible to the console for ko.dataFor($0) inspection
  window.ko = ko;

  $(document).ready(
    function() {
      var data = {
        browser: ko.observable()
      };

      function ValueModel() {
        var self = this;

        // expose data to the view so we can bind other hypothetical values
        self.data = data;
        self.val = ko.pureComputed({
          read: function() {
            var val = self.data.browser();

            // 'required' validation doesn't work if the value is [undefined].
            // it only identifies empty as undefined (no array), so this
            // function doesn't wrap in array syntax if the value is undefined
            if (val === undefined) {
              return undefined;
            } else {
              return [val];
            }
          },
          write: function(value) {
            if (!!value) {
              self.data.browser(value[0]);
            }
          }
        });
      }
      ko.applyBindings(new ValueModel(), document.getElementById('form1'));
    }
  );
});

Note: This code fragment was specifically written for testing in the Oracle JET Cookbook. You can test it by pasting the fragment into the JavaScript block of the Oracle JET Cookbook ojSelect recipe page. After pasting, click the "Apply Changes" button. Select a value from the ojSelect list and notice the cookbook example still displays the observable with array notation. This is because the ViewModel is bound to the pureComputed observable, which returns an array. The underlying data model, however, contains the raw, unwrapped value. You can see the value stored in the data model by:

  • Right-clicking the ojSelect or "Current selected value..." paragraph and choosing "Inspect" from the context menu.
  • Switch to the console window and type ko.dataFor($0).data.browser()

This should display the unwrapped observable value without array notation.

I use this pureComputed wrapper for each of my ojSelect single-value select lists. Rather than replicate that code for every single ojSelect, I have a RequireJS module that exposes a method I can then use to create these computedObservables. Here is what that module contains:

define(["knockout"], function(ko) {
  'use strict';

  var wrapObservable = function(observable) {
    return ko.pureComputed({
      read: function() {
        var val = observable();

        // 'required' validation doesn't work if the value is [undefined].
        // it only identifies empty as undefined (no array), so this function
        // doesn't wrap in array syntax if the value is undefined
        if (val === undefined) {
          return undefined;
        } else {
          return [val];
        }
      },
      write: function(value) {
        if (!!value) {
          observable(value[0]);
        }
      }
    });
  };

  return {
    // ojSelect expects array values, so this method wraps single values in
    // array syntax
    wrapObservableForOJSelect: wrapObservable,
  };
});

I can then create ViewModel computeds using the following:

self.browser = ojsHelper.wrapObservableForOJSelect(data.browser);
self.os = ojsHelper.wrapObservableForOJSelect(data.os);
//...