Over the last several months, I’ve been incredibly lucky to be able to work with ExtJS 4 on a daily basis. And I’m not just talking about dabbling here and there; rather, I mean developing a full-on legit application. It’s been awesome.

As I’ve been building this application, I’ve developed a TON of custom, reusable code. Some of it is app-specific, but a lot of it is application-agnostic. For example, in my ColdFusion settings, all AJAX responses are prepended with “//”. Doesn’t sound like a big deal, but the default ExtJS 4 data readers don’t understand this…so I built a set of custom reader extensions that solve this problem.

So now that the application is more in “maintenance” mode, I’ve been taking an inventory of all the custom, globally-reusable extensions and plugins that I’ve created. And it’s got me wondering about how, exactly, one should (or could) leverage all this reusable goodness in a way that doesn’t require a clunky copy-paste for every project.

Workspaces

With the release of Sencha Cmd 3.0, you can now leverage the concept of a “workspace” to aid in managing reusable code. I’ve not personally used this yet, but it seems like a really nice solution.

There is one serious downside, however: it requires that all of the pages (read “apps”) that are sharing common code have to reside under a common location. While this might be feasible for some scenarios, there are also a lot of environments in which you might want to have your app code live in disparate locations, while still leveraging common application code (e.g., the library itself, global plugins, etc.). Thus begins my quest for a solution…

Let’s Start with the Problem

In order to describe the problem, let’s actually build an application. Before we do that, however, let’s start structuring things toward our ideal, which is simply this:

An arbitrary location where we can store common libraries and assets that can be used for our ExtJS 4 apps, regardless of where they “live”.

To start, go ahead and grab the latest ExtJS 4 download (I’m using 4.1.1a in this example). Once downloaded, place it somewhere that you want to be your global “spot” for ExtJS 4 stuff. For me, I’m using the following:

/Users/{username}/sencha/extjs/ext-4.1.1a

Ok, cool. Since we’re here, let’s go ahead and create another folder that will store custom plugins and extensions. I’m calling mine “ux” (more on this later):

/Users/{username}/sencha/extjs/ux

With that out of the way, let’s build our app!

First, be sure that you’ve downloaded the latest version of Sencha Cmd 3.0–at the time of writing, I’m using 3.0.0.025. Once installed, fire up Terminal and generate a new application.

sencha -sdk /Users/{username}/sencha/extjs/ext-4.1.1a generate app TestApp /Applications/MAMP/htdocs/sencha/testapp
  • SDKPath – The path to the ExtJS 4 library that we want to use to generate this app (notice it’s the path to the folder we set up earlier
  • TestApp – This is name of the application…use sane characters, please :)
  • App Path – The path where the generated application should be placed. In this example, I’m just creating my app in a folder in my MAMP’s htdocs

If all your paths are correct, you should see a brand-new spanking app generated and ready to go. In fact, you should be able to open the index.html file and see the frame of an app. Pretty sweet. The contents of your app folder will look like so:

testapp:

/app
/ext
/resources
bootstrap.js
build.xml
index.html

Ok, so first issue. Notice how there is an “ext” folder within my newly generated app. As part of the generation process, the important bits of the framework are copied into my app folder. While this is nice for one-off apps, it doesn’t really help us in our quest for “shared” code. After all, if I’m getting a copy of the entire framework for EVERY app that I generate, I’ll have to copy any globally desirable stuff for each app. Not a pleasant thought.

Virtual Directories to the Rescue!

Fortunately, there is a salvation from this copy-hell, and it comes in the form of virtual directories. Instead of having a physical copy of the framework in our app folder, we can simply use a virtual directory to point to a global resource, even if it’s directories away from our app folder.

To add a virtual directory (in Apache), open up your httpd.conf file (mine’s located at /Applications/MAMP/conf/apache/httpd.conf) and find the section for “Aliases”. Now, add a new alias that will point from your app folder to wherever your desired ExtJS 4 library lives. Here’s one I’ve set up for this example:

Alias /sencha/testapp/ext "/Users/{username}/sencha/extjs/ext-4.1.1a"

The syntax is very simple.

The first part specifies the path that should be aliased. Notice that I’ve put “/sencha/testapp/ext”, rather than just “/ext”. If I did the latter, the alias would be referring to a path at the top level of my htdocs–in other words, at my web root. But that’s not where I want the alias, so I explicitly specify the full path (from the web root) to the desired location in my application folder.

The second part defines where the specified resource actually lives. In other words, when the request for the alias is made, the path specified in the second part is what is actually served. As you can see, I’ve pointed it to the full path of the resource that we set up at the beginning.

NOTE: Be sure to restart Apache after adding the virtual directory!

Now that the alias for “ext” is defined, we can delete the physical “ext” folder from our application folder. If we refresh our simple app in the browser, we should see that everything continues to work as expected. The big difference, of course, is that we are no longer working off a “copy” of the framework, but are actually referencing the core, global framework code. And for each new application, we can simply add another alias and use the same framework location. Less duplication, horray!

What About Global Plugins?

Global framework path is working…check. But what if we want to use global plugins/extensions across our applications?

One option is that we could place them in a folder (like “ux”) in our global framework’s “src” folder. While this certainly works, what if we download a newer version of the framework? If we’ve already stored all our custom, global plugins in ext-4.1.1a/src/ux, we’ll have to copy all of those into our new framework download if we don’t want to have to refractor a bunch of stuff. This isn’t a great solution, so we need something else.

NOTE: If you want to run the following examples, copy the “PreviewPlugin.js” file from ext-4.1.1a/examples/ux directory and place the file in /Users/{username}/sencha/extjs/ux.

Fortunately, virtual directories save us again. Instead of adding the global assets physically to our framework’s “src” folder, we can do it via a virtual directory. So consider this new Alias:

Alias /sencha/testapp/ext/src/ux "/Users/existdissolve/sencha/extjs/ux"

Notice the first part. I’m still in the context of my “testapp”, and all that I’ve done is to drill down further into the ext/src folder, defining an alias for the “ux” directory.

And in the second part, I’m simply referencing the path in my “global” location where I’ve decided to store all my global plugins.

NOTE: If you’re paying attention, you’ll remember that we earlier deleted the “ext” folder from the testapp application. The answer to your question then is, YES, we have created a virtual directory within a virtual directory. It works, and you can totally do the same thing in IIS.

So let’s test this out. In testapp/app/controller, open the Main.js file and modify it like so:

Ext.define('App1.controller.Main', {
   extend: 'Ext.app.Controller',
   requires: ['Ext.ux.PreviewPlugin']
});

Simple example. I’ve merely added one of my global plugins to the “requires”. When I reload my app, ExtJS will automatically try to load this file dynamically. In this example, it will be looking in

/testapp/ext/src/ux

Assuming all your virtual directories are setup correctly, you should be able to open Firebug or Chrome Dev Tools, view the JavaScript assets, and see the PreviewPlugin.js file as one of the files that was loaded. Sweet.

Compiling…A Bump in the Road

What we have so far is really nice for development. We can store our global assets wherever we want, house apps wherever we want, and get along just fine. And the beauty of ExtJS 4 is that during development we dynamically piece together all the necessary bits to make our application work.

However, what’s good for the goose is not good for the gander, and what you do in development for ExtJS 4 is not even close to what you should be deploying. That is, while it’s nice to dynamically load the needed assets for your application while you are developing it, the ideal production version of your application is a single, optimized, compressed version of everything that’s needed. To get at this, we need to compile.

Compiling an ExtJS 4 app is super simple. All you have to do is run something like the following from the root of your app:

cd /Applications/MAMP/htdocs/sencha/testapp
sencha compile page -in=index.html -out=build/index.html -compress

Of course, if we run this, it’s going to barf. Even though we have virtual directories created to help us when dealing with developing our website, they don’t do any good in this context, as the compiler has no idea about virtual directories…all it knows is that there is no ext folder physically within your application folder.

Fortunately, there is a REALLY easy fix for this. In your application folder, navigate to:

/app/.sencha/workspace/sencha.cfg

In this file, modify the ext.dir property to point to the full path of your global ExtJS 4 library.

Next, add the workspace.classpath property, and set it to the full path for your global “plugins” folder.

Here’s what my full file looks like:

workspace.build.dir=${workspace.dir}/build/${app.name}
ext.dir=/Users/{username}/sencha/extjs/ext-4.1.1a
workspace.cmd.version=3.0.0.250
workspace.classpath=/Users/{username}/sencha/extjs/ux

These settings will tell the compiler to use these paths for the library and global assets folder, instead of looking simply within the current application path.

Now, if we rerun the compile command, we should see it complete successfully. Additionally, our application folder should now contain a new “build” folder which has a new index.html file, as well as a “all-classes.js” file, containing our full application code base.

Wrapping Up

As seen, with just a few slight modifications, we can pretty easily set up an environment in which we can leverage both global framework AND custom code. I hope this has been helpful, and please chime in and share your experiences if you adopt this approach.