Loading Ext-JS Libraries in SalesForce

Large Ext-JS applications can be structured using a range of libraries. I propose a method for installing these libraries in Salesforce. I cover development time, before the code is minified. I hope that moving to production minified code would be very easy.

My working application has been a Google Map showing the UK Amateur Radio Repeater network. These are radio stations around the country which relay traffic, allowing mobile radio operators to talk to each other over larger distances or where terrain would make radio difficult.

Javascript Code Structure

My library code is organised into SalesForce Static Resources as follows:

M0RJCRepeaterMap M0RJCCommon

The M0RJCCommon.zip contains the code to set up class loading and some utilities that are commonly used by all applications. Promise.js is an implementation of the Promises pattern in Javascript. Apex.js provides a Promise enabled wrapper for Apex Remoting. A whole corporate look and feel framework could be included here.

Ext-JS is a static resource. Other code not included here comes from the Apex pages. A “$RemoteAction” namespace is created by Visual Force Components.

Locating the libraries

The magic occurs in M0RJC.js. This is included from an Apex component. The component should also include the required stylesheets. Mine is currently missing this.

<apex:component >
<apex:includeScript value="{!$Resource.ExtJS}"/>
<apex:includeScript value="{!$Resource.M0RJCCommon}/M0RJC.js"/>
<apex:includeScript value="{!$Resource.M0RJCCommon}/M0RJC/Promise.js"/>
</apex:component>

I include Promise.js here because it’s so useful for bootstrapping. I don’t want to have to use an extra Ext.require() to load it.

The Repeater Map code is included from the Visual Force Page.

<apex:page showHeader="false" sidebar="false" standardStylesheets="false" title="UK Amateur Radio Repeaters">
<c:GoogleMaps />
<c:ExtJS_and_M0RJC_Common />
<c:RadioRepeaterController />
<apex:includeScript value="{!$Resource.M0RJCRepeaterMap}/M0RJC.RepeaterMap.js"/>
<script>
M0RJC.Promise.when([
    M0RJC.Promise.extReady,
    M0RJC.Promise.require('M0RJC.RepeaterMap.RepeaterMap')
]).then(function(){
    Ext.create('Ext.container.Viewport', {
        layout: 'border',
        items: [{
            xtype: 'label',
            region: 'north',
            html: '<h1>Apex, Javascript and Promises test app.</h1><p>Source data from <A href="http://www.ukrepeater.net/">ukrepeater.net</A>.</p>'
        },{
            xtype: 'm0rjc_repeatermap',
            region: 'center'
        }]
    });
});
</script>
</apex:page>

The file M0RJC.RepeaterMap.js is very simple:

/*global M0RJC*/
M0RJC.registerPackage('M0RJC.RepeaterMap');

The M0RJC.registerPackage function is defined in M0RJC.js. It scans the page script tags for a javascript file with the same name as the package. It takes the path of this file and appends the subpath according to the package name. It then adds the path mapping to the Ext-JS Loader.

/*global Ext,M0RJC*/
/*
 * Boot the M0RJC Common Library in dev mode.
 * Provide a method to find an install path of a given script.
 */

Ext.namespace('M0RJC');

/**
 * Find the path containing the given script, recognised by a REGEX like pattern.
 *
 * @param {String} scriptPattern the pattern to search for, example 'M0RJC\.js'
 */
M0RJC.findInstallPath = function(scriptPattern){
    var scripts =  document.getElementsByTagName('script'),
        regex = new RegExp('^(.+)\\/' + scriptPattern + '$'),
        i,
        match;

    for (i = 0; i < scripts.length; i++) {
        match = regex.exec(scripts[i].src);
        if (match) {
            return match[1];
        }
    }
    return undefined;
};

/**
 * Locate and register a package. For example given the package 'M0RJC.repeaterMap' there must exist
 * a script 'M0RJC.repeaterMap.js' in the page. This script will be located and the Ext-JS loader configured to
 * find package 'M0RJC.repeaterMap' in the subfolder 'M0RJC/repeaterMap'.
 *
 * @param {String} packageName the name of the package.
 */
M0RJC.registerPackage = function(packageName){
    var pattern = packageName.replace(/\./, '\\.') + '\\.js',
        subPath = packageName.replace(/\./, '/'),
        mapping,
        installDir;

    installDir = M0RJC.findInstallPath(pattern);
    if(installDir){
        mapping = {};
        mapping[packageName] = installDir + '/' + subPath;
        Ext.Loader.addClassPathMappings(mapping);
    } else {
        Ext.log('Could not find install dir for package ' + packageName);
    }
};

/*
 * Set Loader defaults and register the M0RJC package.
 */
(function(){
    Ext.Loader.setConfig({
        enabled: true,
        disableCaching: false
    });
    M0RJC.registerPackage('M0RJC');
}());

Moving to production

A production environment may wish to minify its packages using a tool such as Sencha Cmd. All that needs to change is that the RepeaterMap static resource contains the minified javascript as M0RJC.RepeaterMap.js, replacing the short file above. This way there is no need to change any of the Visual Force components or pages that reference the libraries.

This entry was posted in SalesForce. Bookmark the permalink.

1 Response to Loading Ext-JS Libraries in SalesForce

  1. Richard says:

    The definition of Ext.onReady includes “All classes are loaded”. This means that the above Promise code could be written as


    Ext.require('M0RJC.RepeaterMap.RepeaterMap');
    Ext.onReady(function(){
    .....
    });

    The Google API requires DOM Ready to run, but Ext layouts can be defined without waiting. We still need to Ext.require() the classes that define the components we’ll use, the Repeater Map, before creating the layout.

Leave a Reply to Richard Cancel reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.