Wednesday, July 20, 2016

Using jQuery Interactions with Oracle JET

Draggable, droppable, resizable, selectable, and sortable are jQuery UI interactions that you may want to use when building custom Oracle JET applications. The question is, "How?" Here are three steps:

  1. Find the jQuery UI interaction plugin file,
  2. Import the interaction file into a module (declare it as a dependency), and
  3. Attach the interaction to a component.

Caveat: even though jQuery UI is one of Oracle JET's dependencies, Oracle JET uses only a small subset of the jQuery UI library. With that in mind, this post will describe how to use draggable and droppable with Oracle JET. Just be careful when using jQuery UI interactions with Oracle JET components because JET components weren't designed to be manipulated in this manner. Nevertheless, jQuery UI's interactions are so polished, it is worth a try.

Identify the jQuery UI Interaction File

This sounds like the easy part. Since jQuery UI is one of Oracle JET's dependencies, bower will install it automatically. Depending on how you installed Oracle JET, however, finding jQuery UI files may be difficult (see this thread). If you add Oracle JET to your project through the Bower command (bower install oraclejet --save), Bower will download all of jQuery UI and you will find each jQuery UI component inside your bower components folder (usually bower_components). Since the Oracle JET Yeoman generator also uses bower, the generator will also download all of jQuery UI into your bower components folder. But here is where it gets a little tricky. After Bower finishes, the Yeoman generator will move Oracle JET's real dependencies into /js/libs. A quick review of the files in /js/libs show that certain files, such as droppable, are missing. They are still in the bower_components folder, just not in the jQuery UI location identified in the Oracle JET RequireJS configuration. For Yeoman template users, this can be a little problematic since the RequireJS configuration already contains a declaration for jQuery UI, but that declaration doesn't include every jQuery UI file. For others that are using Bower directly, this is a non-issue. Your Oracle JET RequireJS configuration already points to jQuery UI and that location contains ALL jQuery UI files including source, minified, and combined files. So for bower users, "carry on." Yeoman users will want to copy the appropriate interaction file into the jQuery UI folder identified by the RequireJS configuration.

Import the Interaction File into a Module

Whether your ViewModels are pure knockout components or ojModules, chances are high you are using AMD-style modules through RequireJS. We need to tell RequireJS how to find our interaction plugin. jQuery UI interaction plugins are jQuery plugins, so we will want to import jQuery as well. Here is a sample AMD define block that references draggable and droppable from the standard Oracle JET RequireJS configuration location:

define([
  'jquery',
  'jqueryui-amd/draggable',
  'jqueryui-amd/droppable'
], function($) {

Attach the Interaction to a Component

Let's say we have a list of DOM elements that are supposed to be draggable and a couple more DOM elements that are supposed to be drop targets (droppable). Both draggable and droppable elements need to be enhanced by jQuery UI. Through CSS class selectors, etc, identifying those DOM elements should be trivial. In the asynchronous world of Knockout and Single Page Applications (SPA), the hard part is knowing when those DOM elements will exist. Can't we use $(document).ready? Maybe, but don't count on it. In an SPA, $(document).ready may have fired several views ago. Furthermore, the draggable elements may come from an asynchronous service, which means they may not exist at page load. Instead, we need DOM elements with lifecycle management events. For each enhanced DOM node, we need to know when that DOM node is available. Knockout gives us lifecycle management capability through custom bindings. I have written about ko.bindingHandlers on many occasions. What makes this example different is that it is very simple. Whereas others required init and update handlers, these interactions require only an init handler. Here are example ko.bindingHandlers implementations for draggable and droppable::

ko.bindingHandlers.jmDraggable = {
  init: function(element) {
    $(element)
      .draggable({
        revert: 'invalid',
        helper: "clone"
      });
  }
};

ko.bindingHandlers.jmDroppable = {
  init: function(element) {
    $(element)
      .droppable({
        drop: function(event, ui) {
          $(ui.draggable)
            .detach()
            .css({
              top: 0,
              left: 0
            })
            .appendTo($(this));
        }
      });
  }
};

When implementing a custom binding handler, you are creating your own custom knockout binding. You want to make sure your binding names don't collide with other binding names. With that in mind, I used the jm prefix to distinguish my bindings from other people's bindings. I would then use these bindings in HTML that looked something like this:

<div id="panelPage">
  <div class="oj-flex drag-items draggables oj-margin oj-padding"
       data-bind="jmDroppable, foreach: {data: labels, as: 'label'}">
    <div class="oj-panel oj-margin" data-bind="jmDraggable, css: label">
        <span data-bind="text: label"></span>
    </div>
    
  </div>
  
  <div class="drop-target oj-margin oj-padding"
       data-bind="jmDroppable">
    <span>Drop items here</span>
  </div>
  
</div>

Here is a working example that makes oj-panel draggable:

Tuesday, June 21, 2016

Creating Relational Views with Oracle JET (... or Passing Parameters to Child Views)

When building composite user interfaces, how do you share data between nested views? Nested views are a key element of modular applications. I recently gave advice for determining when to use Modular Views in Knockout with Oracle JET. When breaking a user interface into reusable components, a developer has to consider the relationships between data within those user interface fragments. The WorkBetter Oracle JET Sample application is an example of a modular user experience that shares information between parent and child views. In the WorkBetter example, the sharing is between the main container "root" view and route-related views. Here is a list of ways we can share information between views:

  • Context variables such as $parent, $parents, and $root;
  • Global AMD/RequireJS modules; or
  • Parameters (knockout components or ojModule)

Context Variables ($parent, $parents, and $root)

This is a common approach because of its simplicity. If you are writing a child View and you know the structure of the parent View, then why not just reference the parent context through $parent?

I really, really don't like this option. Before I tell you why, I want to make this clear up front:

There is nothing wrong with $parent or any other context variable.

I use context variables such as $parent all the time inside a single view to reference hierarchical contexts within that same view. What I don't like is using context variables to reference a higher scope outside the current View and ViewModel. Here is why:

Referencing ancestor ViewModels from a child ViewModel creates an implicit, unwritten contract between a child ViewModel and its ancestors.

Any changes to the parent ViewModel will have an impact on the child ViewModel and there is nothing in the parent View or ViewModel alerting other developers to this relationship.

Global AMD/RequireJS Modules

This method has some merit. I use it for sharing configuration-like information, information that is common to the entire application, not view specific. The benefit of this alternative is that it is the least coupled. What makes this approach suboptimal, however, is that we are using globals to pass values when globals are not necessary. Every module has an opportunity to interact with a global variable. This approach is sort of like having a private conversation by pinning messages to a global message board knowing that anyone can read and change the message anytime.

Besides the potential for eavesdropping, I discourage this approach because it does nothing to document the contract between related ViewModels. By definition, there is a relationship, but the relationship is hidden by the use of globals. At least the Context Variables approach identified the relationship through the use of Context Variables.

When using globals in this manner, be careful with the module's exposed interface. Don't allow writing to a variable that should be read only and watch for side effects. Although effective for sharing between ViewModels, there are much better ways.

Parameters

This is my favorite option because it explicitly defines the relationship between ViewModels. The parent determines what data to share with the child view and explicitly passes that data through the params attribute. The child ViewModel explicitly identifies its params through its ViewModel constructor. Here is an example of a parent View that uses the params attribute to share data with a child ViewModel:

<div class="oj-flex oj-margin">
  <!-- ko foreach: {data: employees, as: 'emp'} -->
  <div data-bind="ojModule: { name: 'gMMqrR',
                  params: emp }">
  </div>
  <!-- /ko -->
</div>

The parent View clearly defines emp as data to share with a child ViewModel. Here is the child ViewModel:

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

  var ViewModel = function(employee) {
    var self = this;
    self.employee = employee;
  };

  ViewModel.prototype.selectEmployee =
    function(data, event) {
      console.log("You selected", data.employee.name);
    };

  return ViewModel;
});

The child ViewModel's constructor parameter clearly identifies its data requirements. Here is the codepen if you are interested in fiddling with this solution:

See the Pen Oracle JET 2.0.1 ojModule Params by Jim Marion (@jimj) on CodePen.

Here is the child ojModule codepen:

See the Pen Oracle JET 2.0.1 ojModule Params (submodule) by Jim Marion (@jimj) on CodePen.

Keeping with the best practice identified in Modular Views in Knockout and Oracle JET, I used a child module to encapsulate event handlers within a scope change. This example is rather simplistic with its small View and ViewModel, so ojModule may be overkill. Let's think about what this view would look like if I had not used a separate module. Here is the combined view:

<div class="oj-flex oj-margin">
  <!-- ko foreach: {data: employees, as: 'emp'} -->
  <div class="oj-panel oj-margin"
      data-bind="click: $parent.selectEmployee">
    <i data-bind="text: emp.name"></i>
  </div>
  <!-- /ko -->
</div>

This is not that exciting or unique really. Notice that I had to use $parent in the View. This is a perfectly acceptable use of a context variable. $parent in this scenario allows us to access an event handler method at a higher context than the current context. Now imagine a much larger scenario where you have lots of foreach constructs and related event handlers. Using ojModule to keep handlers directly related to their views within submodules may make code easier to read and comprehend.

→ BEGIN RABBIT TRAIL

The child codepen above demonstrates one more important practice: defining functions as few times as possible. The ViewModel module for the child ojModule defines the selectEmployee click handler as a prototype method rather than defining the function inside the constructor. It may be more common in knockout to use constructor defined functions as follows:

var ViewModel = function(employee) {
  var self = this;
  self.employee = employee;
  self.selectEmployee = function() {
    console.log("You selected", self.employee.name);
  };
};

The problem with this code is that it defines a new self.selectEmployee function for each employee (each iteration of the ko foreach). This would create one new instance of the function object for each element in the array. Think of the performance impact! When using embedded modules in loops, this is an important consideration. This is a key difference between child modules and standard navigational modules and something to consider when creating child modules.

Another way to write this is to use private functions. Here is the same ViewModel, but using a private function definition:

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

  var selectEmployee = function(data, event) {
    console.log("You selected", data.employee.name);
  };

  var ViewModel = function(employee) {
    var self = this;
    self.employee = employee;
    self.selectEmployee = selectEmployee;
  };

  return ViewModel;
});

Either add to the prototype or use a private function OUTSIDE the constructor. I'm not sure it matters. The important point is to minimize creating functions inside loops.

← END RABBIT TRAIL

Passing Methods to Child Modules

Within our ojModule example, let's say we want to track the selected component at the root level. How would you notify the root ViewModel that the selection changed? One way is to pass a callback (or an observable) as a parameter to the child ojModule. Here is what the new View would look like:

<div class="oj-flex oj-margin">
  <!-- ko foreach: {data: employees, as: 'emp'} -->
  <div data-bind="ojModule: { name: 'XKNNPw',
                  params: {
                      employee: emp,
                      onselect: $parent.selectEmployee
                  } }"
       class="employee">
  </div>
  <!-- /ko -->
</div>
<h2>Selected data</h2>
<pre data-bind="text: ko.toJSON(selectedEmployee, null, 2)">

Notice that I again used $parent inside my View to reference a higher scope. Just to make sure I'm clear, there is nothing wrong with using $parent to reference a higher scope within the same View. $parent only becomes problematic when referencing scopes beyond the current View.

Here is the parent/root ViewModel that defines the selectEmployee method.

  var ViewModel = function() {
    var self = this;

    self.employees = employees;

    // stores selected employee
    self.selectedEmployee = ko.observable({});

    // click handler for submodule
    self.selectEmployee = function(employee) {
      self.selectedEmployee(employee);
    };
  };

... and the child module ViewModel:

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

  var ViewModel = function(params) {
    var self = this;
    self.employee = params.employee;
    self.clickHandler = params.onselect;
  };

  return ViewModel;
});

Click an employee in the list below and watch the Selected Data region change. Hint: use the tab key to move between items and the enter or space key to select items.

See the Pen Oracle JET 2.0.2 ojModule Function Params by Jim Marion (@jimj) on CodePen.

Oracle JET 2.0.2 Released

The Oracle JET team is doing an amazing job releasing new features and fixes with another release announced yesterday. With that announcement, I created new "base" versions of the Oracle JET CodePen and jsFiddle.

Monday, June 20, 2016

Oracle JET with CodePen

I was quite comfortable with jsFiddle as a playground and testing site for simple Oracle JET samples. As I moved onto ojModule and ojRouter, which require multiple files, I found jsFiddle somewhat limiting. There may be a way to create fiddles on fiddles, but I haven't found it. While considering how to build ojModule examples in jsFiddle, I stumbled upon this online roundup of code playgrounds which got me thinking about CodePen. Specifically, I was intrigued by the 10 Cool Things You Can Do with CodePen and JavaScript. For example, I can create a CodePen that has JavaScript and HTML and then never run it (View and ViewModel), but instead reference each by URL from another CodePen (one that defines an ojModule). Being able to create a CodePen JavaScript file that I can reference from other CodePens also means I can move the RequireJS configuration into a single file and reference it from other CodePens, eliminating the RequireJS/OJet configuration noise from my examples. This allows me to focus on just the important stuff (the delta).

One other concern I had with jsFiddle is finding fiddles. CodePen allows for tags which means I can Search for all CodePens tags with oraclejet.

With these issues in mind, I ported my Oracle JET Base 2.0.1 Debug fiddle to CodePen. Unlike my prior jsFiddles, these CodePens only contain the "delta" code necessary to implement a solution. The RequireJS configuration is visible from the pen's JavaScript configuration, but is now in a separate pen. Likewise, I no longer have to import the CSS but reference it in the pen's CSS settings. Before becoming familiar with CodePen, I found these "invisible" dependencies confusing. Now that I understand CodePen, I find them really helpful because they allow me to remove the configuration noise and focus on the pen's main idea.

Relevant CodePens

Thursday, June 16, 2016

A Sassy Oracle JET Yeoman Generator (2.0.x)

As a consumer of the Oracle JET Yeoman Generator, one thing I noticed is that generated projects do not include a Sass compilation step. Here is how I enable Sass in a "Basic" Oracle JET Yeoman project. This is meant to be more of a Getting Started guide than a definitive reference. Since the following reference uses compass the first step is to install compass. This old article from the Sass Way should help you get Sass and Compass installed. After installing Compass open a terminal and navigate to your project's root directory. Within that directory, invoke the following command:

npm install grunt-contrib-compass --save-dev

With dependencies in place, it is time to add Sass to the Oracle JET build files. First, let's create a compass.js configuration. Create a new file in scripts/grunt/config named compass.js. To this new file, add the following compass configuration. Compass has many valuable options, but this is enough to get started:

/* jshint -W097, node: true */
"use strict";

module.exports = {
  options: {
    sassDir: 'styles',
    cssDir: '.tmp/styles',
  },
  app: {
    options: {
      cssDir: 'styles'
    }
  },
  server: {
    options: {
      sourcemap: true,
      cssDir: 'styles'
    }
  }
};

I made up the "app" and "server" target names myself. Feel free to name them whatever makes sense to you (for example, serve or release, etc). If you choose different names, however, remember them because you will reference them later.

The delivered Oracle JET Grunt file contains two primary tasks: serve and build. Let's work on the `grunt serve` task first. As you probably know, the serve task launches a web server, listens for changes to web files, and then reloads the running browser app when file changes are detected. The watch step of the serve task is responsible for triggering other steps and then reloading the browser's content. Open scripts/grunt/config/watch.js and modify it as follows:

module.exports =
{
  compass: {
    files: ['styles/{,**/}*.{scss,sass}'],
    tasks: ['compass:server']
  },
  // to watch for changes in file and to perform livereload
  livereload:
  {
    files:
    [
      "css/!(libs)/**/*.css",
      "js/!(libs)/**/*.js",
      "js/{,*/}*.js",
      "css/{,*/}*.css",
      "**/*.html",
      "styles/*.scss"
    ],

    options:
    {
      livereload: "<%= oraclejet.ports.livereload %>"
    }
  }
};

Specifically, add the compass section and then add the styles/*.scss entry to the files array. This tells grunt to run compass when styles/*.scss files change.

Now we need to make the serve task aware of the new compass step. Open scripts/grunt/tasks/serve.js and add "compass:server", to the tasks array. The tasks section should now appear as follows:

  var tasks =
  [
    "compass:server",
    "connect:" + target + "Server" + (disableLiveReload ? ":keepalive" : ""),
  ];

Test it out by creating an scss file in the styles folder and then invoking grunt serve. You should see the *.css complement of your scss file appear in the styles folder.

Let's move onto the build task. Open the scripts/grunt/config.copy.js file and add the following exclusions to the release.src array: "!styles/**/*.map", "!styles/**/*.scss". Finally, open the scripts/grunt/tasks/build.js file and insert the "compass:app" step into the tasks array. You can pretty much place it anywere ahead of the "copy:release" step. I chose right before the "uglify:release" step. Test your build script by invoking grunt build:release. If all is working, you are now ready to begin Styling Custom Components with Oracle JET.

For further information, I recommend Customizing an Oracle JET Theme Using Sass and the Oracle JET Getting Started Guide.

Tuesday, June 14, 2016

Modular Views in Knockout and Oracle JET

"I have a View and ViewModel and I'm wondering if I should break it into multiple child Views and ViewModels." This is a great question and one that I've asked myself many times. Here are my criteria:

  1. Can I divide my workload, allowing others to help me if I convert sections of a view into sub views? (avoid merge conflicts)
  2. Am I using scope-changing constructs such as ko foreach?
  3. Is my ViewModel (or view) over 100 lines?

Conflict: I suppose there are people that take great pleasure in resolving merge conflicts. I'm not one of them. I can't say that I'm a fan of any type of conflict. Merge conflicts fall into that same "conflict/resolution" bucket. If I am working on a project with a teammate and we both need to access the same view, then I may give great consideration to partitioning that view.

Scope: It is quite common to use ko foreach and other scope-changing constructs. Often I find myself iterating over a data set. For each element in that data set, it is given that I will have to respond to some type of event: click to delete a row, view details, and so on. It is at this point, when I'm writing the event handler, that I realize I have changed scope. I have 3 options:

  • Put my event handler at the root of my ViewModel and reference it using context variables ($page) and then try to figure out how to identify the current element (such as ko.dataFor(event.target)),
  • Enrich my child data model with event handler methods, or
  • Move the contents of the ko foreach into a new view and ViewModel.

I'm not fond of $parent and other context-related variables. It seems too easy to lose sight of the real scope and couples my solution in a manner I'm not sure I prefer. As an alternative, I often place the content of a ko foreach in a new view and ViewModel so that event handlers and other ViewModel methods can interact with my data without scope issues. This is sort of like a scope reset.

100 lines... OK, you may have to humor me with this one. I find 100 lines to be easy to comprehend. Once I go over this threshold, code becomes more difficult to follow. I can read 100 lines with three presses of the page down key. I can keep that all in my brain and understand it without much scrolling (I suppose you could say my internal page size is 3 pages?).

Styling Custom Components with Oracle JET SCSS Variables

In my post Extending Oracle JET Components Through Custom Bindings, I used CSS to move the ojInputText border from the input element to the ojInputText "root" element. One of my readers commented on my improper use of color values instead of SCSS variables. Great point! Oracle JET includes SCSS theme variables for color and style. Since my goal was to maintain the delivered Oracle JET styling, it is better to use the Oracle declared SCSS theme variables. Here is what that prior example would look like if I used Oracle JET's theme-specific SCSS variables:

// following three lines taken from oj-alta.scss
@import "../bower_components/oraclejet/dist/scss/alta/oj.alta.variables";
// note: this generates a very large css file and takes a while to compile.
// if you are just interested in the variables, you can cheat and comment out
// these two imports
@import "../bower_components/oraclejet/dist/scss/3rdparty/normalize/normalize";
@import "../bower_components/oraclejet/dist/scss/alta/oj.alta.widgets";

.content-container {
  margin: 10px;
}

/* icon CSS here */
.birthday-input:after {
    font: normal normal normal 14px/1 FontAwesome;
    content: "\f1fd";
    margin: 0 10px;
}

.birthday-input input {
    border: none;
    flex: 1;
}

.oj-inputtext.birthday-input {
    display: flex;
    flex-flow: row nowrap;
    align-items: center;
    background-color: $background3Color;
    border: 1px solid $border4Color;
}

Notice the import at the top of this code fragment. This is a relative path pointing to the Oracle JET SCSS files within the bower install directory. In my case I store scss in a "styles" subfolder of the root project folder, making this path a relative path from ./styles to ./bower_componnets.

This solution assumes that you have your project configured for SCSS (grunt tasks, etc). Be aware that SCSS is not part of the Yeoman generated Oracle JET build script. You can learn more about Oracle JET and SCSS in the document Customizing an Oracle JET Theme Using Sass

Unfortunately, I can't exactly show this in a jsFiddle because the SCSS in jsFiddle doesn't appear to support remote scss partials. I can, however, paste in the relevant oj.alta.variables and pretend that I am importing the real Alta variables. In the sample below, instead of modifying one of my prior examples, this is another iteration of the "ojInputText with animated clear button" that has better support for accessibility (you can now tab to the delete button if it is visible).