Javascript Coding Guidelines

posted Sep 15, 2011, 1:50 PM by Eric Patrick   [ updated Oct 25, 2013, 12:56 PM ]
When coding javascript for QBO 3, adhere to the following:
  • Use behaviors instead of lots of window.addEvent('domready', ...) code.  
  • Create small, modular script files instead of a single large script file and enter these into Javascript.config
  • Use Pascal case for class names, and Camel case for method names and variables
  • Alias global variables as local variables to enhance minification

Behaviors

QBO 3 leverages Mootool's Behaviors to simplify page load javascript code. In places where QBO 2.0 uses a css class to find elements and bind functionality to them, in QBO 3 we instead use data-behavior tags. The Behavior API handles some key infrastructure considerations for use, including:
  • A formal cleanup API to help ensure good memory management
  • Ensuring loading code is only called once per element
  • An event arbitration infrastructure to allow different DOM elements and javascript classes to communicate by raising events.
Examples of behaviors included in QBO 3 include:
  • qbo.Paginate.js: handles pagination of data grids (tables)
  • qbo.OrderBy.js: handles setting sort criteria of data grids
  • qbo.ObjectBind.js: handles creating qbo.*Object from div tags (instead of class="panel")
In many cases, the BehaviorAPI can be used to help abstract functionality between behavior filters we write. Take qbo3.ContactObject and qbo.Paginate as an example: when the user clicks on page 2 (something the Paginate filter recognizes), we need to have qbo3.ContactObject respond to that click and request page 2 from the server. Under QBO 2.0, we would do something like this:

qbo.Paginate = new Class({
    ...
    SetPage: function(recordStart) {
        $q.GetPanel(this).SetPage(recordStart);
    }
});

This implies our Paginate class is always a child of a qbo Panel, and that Panel has a SetPage method that accepts a record start parameter. Not very modular.

In QBO 3, using the BehaviorAPI, we instead do this:

Behavior.addGlobalFilter('Paginate', function (el, api) {
    ...
    // Monitor for record start changes.
    el.addEvent('click:relay(a.page)', function (e, el) {
        api.fireEvent('paginate', { RecordStart: el.get('start') });
    });
});

and in the behavior that loads qbo.*Object:

Behavior.addGlobalFilter('ObjectBind', function (el, api) {
    ...
    // Monitor for pagination events
    api.addEvent('paginate', function (data) {
        alert('Got a paginate request!');
        qboObject.refresh(data);
    });
    ...
});

Any objects participating in the behavior will have the BehaviorAPI events raised to them, and can listen appropriately.

This approach is key to long-term maintainability of complicated forms like a BPO form or complicated Short Sale forms.

Javascript.config

All pages requiring site javascript should reference the following:

<script type="text/javascript" src="Theme.ashx/Javascript"></script>

A Javascript.config file is used to identify 'standard' script files to be used on a site.

The Theme.ashx Javascript method (Theme.ashx/Javascript) will stream all javascript files found in the Scripts folder as follows:
  • Any file included in the Javascript.config will be emitted in the order found in Javascript.config
  • Any other files will be included after the script found in Javascript.config
    • if a .min version of a script exists, it will be used if Javascript.config's UseMinified="true"
    • this allows for minified scripts in UAT/PROD, but full scripts in DEV for troubleshooting
Do not place javascript files that should not be part of every QBO page in the root of the Scripts folder.  For example, Visual Studio web projects include jQuery files, which normally conflict with MooTools files. If these are accidentally deployed to the Scripts folder, they will cause errors with some QBO behaviors.

Scripts that should be included on demand, but not part of the full library, should be placed in a child folder of the Scripts folder, such as Scripts/Custom/myCustomScript.js

Theme.ashx/Javascript will emit a maxAge header to prompt the browser to cache the script. Streaming and caching all javascript files in a single HTTP GET allows for having many small, modular script files rather than a single behemoth file.

Note that Theme.ashx reads the javascript files from disk once upon application startup, and then serves the javascript from a cache. If you add a new javascript file to the Scripts folder, you must clear the application cache before the file will be available in Theme.ashx/Javascript. (Theme.ashx/Recycle will clear all application cache items.)

Caching javascript (and other) files on the browser presents a developmental challenge; it can be annoying to clear this cache in a targeted manner. To allow for targeted cache clearing, the following code snippet can be used:

// usage: qbo3.reload(url);
qbo3.reload('Theme.ashx/Javascript');


Pascal Case vs. Camel Case

Use Pascal case for class names and server-side parameters, and Camel case for name spaces, method names and variables.  For example:

qbo3.ContactObject = new Class({
  Extends: qbo3.AbstractObject,      //
  options: {
    url: 'Contact.ashx/',                   // a property used by javascript only
    someOtherProperty: 'whatever',   // a property used by javascript only
    DisplaySize: 25                         // a parameter to be passed to C#
  },
  someFunction: function(argA, argB) {
    var myLocalVariable = this.options.target;
  }
})

This is in contrast to QBO 2.0 qbo.js, where we attempted to have the javascript class casing match the C# class casing.  The downside of this is that most programmer don't work with both C# and javascript heavily, so there is no real advantage to having them match. QBO 3 is attempting to make life easier for third party developers, and the conventions here are common to Mootools, jQuery, and other javascript frameworks. 

Enhance Minification

QBO 3 will use the YUI Compressor to minify javascript code.  Minifcation can be assisted in several ways, primarily by alias global variables as local variables.

Getters and Setters

Javascript offers custom getter and setter functions for object properties. This is useful if you want to control the possible values that a property may be set to, or set a default value on a property the first time it is accessed. 


Comments