Friday, June 7, 2013

Creating Twitter Bootstrap Widgets - Part IIIA - Using Dojo To Bring It Together

Creating Twitter Bootstrap Widgets - Part I - Anatomy of a Widget
Creating Twitter Bootstrap Widgets - Part II - Let's Assemble
Creating Twitter Bootstrap Widgets - Part IIIA - Using Dojo To Bring It Together

Note:  With a customer deadline coming up and with all the work that MWLUG 2013 requires, I haven't had the time to devote to writing this series.  So I took most of today to complete this part. As I was writing this part of the series, I realized that I should talk about creating widgets using Dojo in general. So I split this part into part A and B because it was getting way too long.

Welcome to part three of my five part series "Creating Twitter Bootstrap Widgets". In the first two parts, we talked about how Bootstrap widgets can be assembled using a consistent framework of standard HTML elements with CSS3 styling.  In part three, we will connect together our combo box widget using Dojo and make it functional.  

As XPages developer you should be familiar with Dojo as the underlining Javascript library.  XPages widgets are mostly Dojo widgets, Dijits.  To create a dojo widget we should be familiar with dojo.declare and dijit._Widget ( dijit._widgetBase for 1.7 or higher).  If you are familiar with these two features of Dojo, then you should wait for Part IIIB which I will publish next week.  

Since JavaScript does not support a Class system Dojo simulates an object oriented class structure using dojo.declare which we will use to create our Bootstrap combo box widget.  In my opinion, this is one major reason to use Dojo which goes beyond the standard JavaScript Prototype.  With dojo.declare you can creating superclasses that other classes can inherit and use. Remember that I mentioned that Bootstrap widgets are like the RISC of widgets. With dojo.declare you can look at a Bootstrap widget as a collection of simpler widgets each with it only superclass.  Therefore in reflecting with the RISC approach, we want to create a number of reusable core superclasses that we can use to create our combo box widget.  For more information about using dojo.declare go to:

The other important class of Dojo that we need to understand in creating our Bootstrap widget is dijit._Widget.  It handles the widget lifecycle from creation, startup to destruction.  In addition, it handles the registration of the widget and make it available to be reference in our code.  So if our widget has the id  of "myWidget", we can reference it after it has been instantiated using dijit.byId('myWidget'). I attempted to create my own widget registration classes so that I would not need to include all the overhead of dijit._Widget, but that experiment turned into a complex mess and I went back to dijit._Widget.  Maybe in the future I will try again.  I just do not like loading extra stuff if I do not need it.  A widget lifecycle includes:

Creation and Start Up:
  • constructor()
  • postscript()
  • create()
  • postMixinProperties()
  • buildRendering()
  • postCreate()
  • startup()
  • destroy();
  • destroyDescendants();
  • destroyRecursive();
  • destroyRendering();
  • uninitialize();
When creating your own Bootstrap widgets, the most important dijit methods are constructor, postCreate and startup.  Majority of the time when I create my own widgets, I just use postCreate. 

The skeleton HTML of our combo box Bootstrap widget is:

<div class="input-append dropdown">
 <input type="text" class="input">
 <button class="btn" type="button">
  <span class="caret"></span>
 <ul class="dropdown-menu">
Note: I changed the DOM node as described in part II.  I changed the class "span3" of the input to "input-medium' and remove the class "span3" from the ul tag which provide the same alignment with less code.  

One important part of the widget life cycle is to define the HTML DOM of the widget. This is done in the life cycle "buildRendering."  You can define our Bootstrap widget skeleton DOM HTML in the widget js file, in a HTML template, or directly as part of page HTML DOM.  

Dojo provides a helper class , dijit._Templated, that allows you to define a key variable "templateString" as the DOM representative of the widget.  You can defined it directly

templateString = '<div class="input-append dropdown"><input type="text" class="input-medium"><button class="btn" type="button"><span class="caret"></span></button><ul class="dropdown-menu"></ul></div>';
or from a template file that you have as part of your file structure.
templateString: dojo.cache("myNameSpace", "templates/combobox.html")

By using the dijit._Templated, dijit._Widget will automatically use the templateString in the buildRendering() cycle for the widget.

If you are using a HTML template to define the templateString, you will also need to use dojo.provide to tell Dojo where the HTML template and js file are located. In addition to using a templateString to define the structure of our widget you can also define the widget DOM directly in the buildRendering() method or just have the complete widget DOM reference in the HTML page itself and get a handle the widget DOM node.

Creating the Bootstrap Combo Box Widget Classes

That all said, I like having the complete widget DOM skeleton as part of the HTML DOM so when the page loads there is no additional overhead of loading the dijit._Templated superclass.  Our iPhora Application Designer engine automatically places the complete Bootstrap widget DOM skeleton into the HTML DOM of the page.   Also in our case, we do not declaratively create the widget, so there is no need to perform a buildRendering. 

Since we will be using dijit._Widget to build our widget, we need to use dojo.require('dijit._Widget') to load the superclass when we load the page.  

In creating our combo box, Dojo will need to:
  • Get a data source that will define the dropdown list
  • Build the list nodes inside our UL dropdown node from a data source
  • Connect the different sub nodes to different events that can happen
  • Define setters and getters to our widget so that we can interact with the widget

There are several events that we expect to happen in a combo box:
  • Click on dropdown button to open or close the dropdown menu
  • Click somewhere else in the body to close the dropdown menu when the dropdown menu is opened.
  • Click on the dropdown menu to select the value and close the dropdown menu
  • Click on the input field to open and close the dropdown menu.

In order to have the widget understand the different events, you will need to define in your widget skeleton  the events for different parts of widget DOM using dojoAttachEvent.  When you are creating your widget class using dojo.declare, you need to define each event handle as a private method, for example _onSelectDropDown within your widget class.


In our case it will be dojoAttachEvent="onClick:_onSelectDropDown".  So our widget HTML DOM skeleton will look like:

<div class="input-append dropdown">
<input type="text" class="input" dojoAttachEvent="onClick:_onToggleDropDown">
<button class="btn" type="button" dojoAttachEvent="onClick:_onToggleDropDown">
<span class="caret"></span>
<ul class="dropdown-menu" dojoAttachEvent="onClick:_onSelectDropDown">

Adding dojoAttachEvent in the widget DOM for events is consider best practice, but Richard never follows what is considered the normal anyway.  I prefer to use dojo.connect or in the case of dojo.declare, this.connect.  Why, since we are programmatically creating the widgets (because it is slightly faster) rather than declaratively, we are not using dojo.parse .  For a good discussion about best practices in creating custom Dijits go to this series:

Our combo box widget can be split up into three core widgets that can be reused. Therefore, we will first create a three superclasses using dojo.declare that we can reuse with other widgets in the future along with our combo box widget class:
  • Input Class called 'demoInput'
  • List Class called 'demoList'
  • Dropdown Class called 'demoDropDown'

Next time, we will go through the creation of each the classes and add them as superclasses to our combo box class to complete our widget class and demo it using our newly created classes.

No comments:

CollabSphere 2022 Presentation: COL103 Advanced Automation and Cloud Service Integration for your Notes/Nomad Applications

Our presentation for CollabSphere 2022. Learn how to quickly add workflow automation into Notes/Nomad applications using No-code tools that ...