codeflood logo

Reusable JavaScript in SPEAK

In my previous post I showed how to extend existing SPEAK models and views when creating new SPEAK components so we can reuse what we've already got rather than duplicating wads of code. Reuse at the view and model level is good but it's only one part of the puzzle. What about reusing individual functions? To reuse arbitrary code in C# we can wrap that code up in a class which can be called by other code files by way of a using statement (and appropriate references in the project). But JavaScript doesn't really have a native way to allow including existing functions from another location in the current file. It's normally up to the host environment how to get other files loaded in before the current file.

Just as a quick side note, I mention above the host environment for JS files. Browsers are not the only environment in which JavaScript runs. Node.js is making sure people realize that. So keep that in mind when working with JS. If you follow Pierre's advice (and you should, he's the boss of SPEAK) in his JavaScript modules post, it can be a good idea to split out any code that deals with DOM to make your JS code more reusable. Each host may have different techniques for loading existing code. So, back to it.

In a browser the way to ensure files containing those reusable functions are loaded before the dependent JS file is to add a script tag further up the page so the browser will load that file before loading the dependent file. This can be messy and error prone, especially when working with sizable applications such as, um, SPEAK! Luckily there are libraries to help us manage dependent JS code and ensure things get loaded in the right order. The library SPEAK uses for this is require.js. So let's start with a simple example.

Let's say we have a function that parses some information from the query string. We could write the function directly in the page code but that wouldn't be reusable on other pages. This calls for a module! To define a new module we start with a call to the define() function which is provided by require.js. In this call we provide a list of dependencies this module relies on by name, and a list of variables to bind each module to.

define(["sitecore"], function (_sc) {
});

Look familiar? It should. This is the same bit of code you'd see at the top (or near the top) of any SPEAK component. That's right, SPEAK components are also require.js modules. In the above module definition the "sitecore" module is listed as a dependency and bound to the name _sc inside this module. The first argument to define is an array and each argument provided to the 2nd argument function matches up by index with the dependent modules. For the module to be useful it needs to return something. It can be any number of variables or functions. The module can also define and use variables which are only visible within the scope of the module. In the below example the qsKey variable is not easily accessible outside the scope of the module.

define(["sitecore"], function (Sitecore) {
  var qsKey = "foo";

  return {
    someProperty: 42,
    parseUrl: function () {
      return Sitecore.Helpers.url.getQueryParameters(window.location.href)[qsKey];
    }
  }
});

Quick note, the sitecore module in the above example is bound to the variable Sitecore and not _sc as in the first example. Each module is defined in it's own file. So how do we use this module in another? We list it as a dependencies in the define() call. But it's not quite that simple. We also need to tell require.js where to load the module from. We do this by adding a path to the require.js config.

require.config({
  paths: {
    urlParser: "/sitecore/shell/client/YourApps/Common/UrlParser"
  }
});

define(["sitecore", "urlParser"], function (Sitecore, urlParser) {
    var model = Sitecore.Definitions.Models.ControlModel.extend({
        refresh: function() {
            var foo = urlParser.parseUrl();
            // more code here
        }
    };
});

Just the same as with the sitecore module, now we can use the urlParser module inside the scope of the module above. There is another very important use of modules and that's to make reusable "actions".

Considering the design of a SPEAK application. Many of the pages will be to display data and allow the user to drill down into the data. But if you want to allow the user to change the data then you'll need a way to send the modified data back to the server. Of course this would be done with a POST to a controller action through AJAX. We could easily code that up in the page code of the current page, but that's not very SPEAKish. In keeping with SPEAK standards we should promote reuse and ensure the "action" can be reused across different pages of the application (or across applications). So this is sounding pretty much like we should wrap that functionality up in a module.

The one time we may decide not to use a module is if the action needs to support binding to properties of other components or allow the developer to set parameters through the Rocks designer. If either of those requirements exist then the action can be implemented as a component. Otherwise I would opt for a module.

Comments

Leave a comment

All fields are required.