sencha-logoIn the last post, we jumped through the hoops of setting up a brand new ExtJS 4.2 application, and we even “upgraded” it a bit by switching to the fresher “Neptune” theme. But after all of that, we were left with a fairly lame application. It works, of course, but doesn’t do anything. We need to fix that.

In this installment, we’ll take the mockup that we looked at previously and create some code to prepare the main skeletal sections of the app, including:

  • The header
  • The left side bar
  • The main content area

Getting these in place will let us begin surgically creating various parts of our application. Hooray!

Note: Code for this installment can be found on GitHub.

An Aside: Dynamic Loading

Unless you specifically go another direction, ExtJS 4 applications are going to be a single-page application, driven by a single, “compiled” version of all the JS classes that are required to make your application run.

When developing the application, however, it is certainly not ideal to have to compile the application every time you want to make a change. To facilitate super-rapid development, ExtJS 4 includes a very robust Dynamic Loading system. While we won’t dwell on the intricacies of it here, this Dynamic Loading system allows us to create an arbitrary number of inter-related classes (some custom, singleton classes, some extensions of core classes) that will be loaded on-demand as required by our application.

What this means in practical terms is that in your development environment, your ExtJS app will dynamically load several hundred JavaScript files (some core, some yours) each and every time you run your app (check out the “Sources” tab in Chrome Dev Tools while you’re working on your app). The beauty, however, is that this is all driven by conventions within the MVC architecture of ExtJS 4: you will never have to add these files to your index.html, ever.

To read more about the Dynamic Loading of ExtjS 4, see this excellent introduction.

The Viewport

Before we create the frame views for our application, we need to first understand the “Viewport.” ExtJS has a special container class called Ext.container.Viewport. This container is special because it automatically renders itself to the document body. It is quite greedy, eating up all of the available viewable space in the browser’s viewport. In addition to this, the Viewport is self-aware of its surroundings, so it will automatically resize itself with the browser window. While you certainly have to take this functionality into consideration when architecting your app, it is very handy because you never have to worry about what areas of the app will and will not be “visible” on browser resize: when a Viewport is used, the answer is “all of it”.

To understand how this works within our current app, let’s start with the app.js file at the root of our ExtJS app. You’ll notice two things:

  1. views: [ ‘Viewport’ ]….
  2. autoCreateViewport: true

Views [] Config

First, the views[] section. This is a convention within the MVC architecture of ExtJS 4 that allows you to specify classes that are required (e.g., loaded if not already instantiated). By convention, any class defined in views: [] should exist in the view folder and should be referenced by its classname, leaving off the application’s name space and “views” package.

To illustrate this, open the views/Viewport.js. On the first line of this file, you can see that this class is named CarTracker.view.Viewport:

  • CarTracker: Our app’s namespace
  • view: The “package” where our views are stored, by convention
  • Viewport: The name of our class

By specifying Viewport in the views[] config of our main application class, ExtJS will automatically try to load a file with the filename of Viewport.js and the class name of CarTracker.view.Viewport from the view folder of our application.

WARNING: It will try to load the file. If it can’t find it because it doesn’t exist, or because of a mis-named file/class, you will get a nice big red and angry error message in your Console.

To save yourself alot of headaches, there are a few “standards” that you should start implementing in all the ExtJS 4 classes that you create:

Package names (e.g., “folders”) should always be lowercase. If you usually upper-case folder names in general, stop that :)

Class names should use (upper) CamelCase – eg. MyCustomClass, SomethingFancy, Viewport

The actual class name MUST MUST MUST match the name (and case) of the file name. So if you have Viewport.js, your class name should be Viewport. If you change it to ViewPort, ExtJS will hate you and refuse to work.

autoCreateViewport: true

This config tells our app to automatically load and instantiate the Viewport class before it does anything else. If you set this to false, you will have to manually instantiate the Viewport.

Inside Viewport.js

Now that we’ve gotten to know the Viewport a bit, let’s take a look at the actual code in this file. In view, open the Viewport.js file. A few things to note:

  • extend: Viewport.js is a custom class which extends the Ext.container.Viewport class. By extending this class, we get all the goodness baked into the base class, but can easily extend/configure our custom extension however we need for our application
  • requires: Like the views[] config that we saw in app.js, the requires[] config tells our custom class which files should be loaded before instantiating itself. The difference here is that while views[] had the convention of automatically looking in the AppNameSpace.view convention for loading views, requires[] “requires” the full class path.
  • layout: This particular Viewport implementation is using a border layout. Border layouts are great because they allow us to define up to 5 “regions” (north, south, east, west, center) that are closely related to each other, including the ability to resize, collapse, etc. Border layouts are particularly useful for laying out the main “frame” of typical applications (like ours), so is a good fit for what we want to do
  • items: The items[] config allows us to specify any child components that we would like to be created (more on this below)

So as we can see, our current Viewport is using a Border layout, and has two child components defined: west and center. Given that our desired layout is pretty close to this, all we need to do to finish out our frame is to add the header. We can do that by adding the north region, like so:

Ext.define('CarTracker.view.Viewport', {
    extend: 'Ext.container.Viewport',
    requires:[
        'Ext.layout.container.Border'
    ],
    layout: {
        type: 'border'
    },
    items: [
        {
            region: 'north',
            xtype: 'panel',
            // hbox layout will distribute items horizontally within this container
            // so if we ever want to add a menu on the right (or something else), it will be easy
            layout: 'hbox',
            // adding an item to contain the title and logo of our app
            items: [
                {
                    xtype: 'panel',
                    bodyPadding: 5,
                    html: '<img src="resources/images/car.png" /><h1>Car Tracker</h1>',
                    cls: 'header'                    
                }
            ]
        },
        {
            region: 'west',
            xtype: 'panel',
            // split allows us to resize the west panel in relation to the center
            split: true,
            title: 'Menu',
            width: 200
        },
        {
            region: 'center',
            xtype: 'panel',
            title: 'Main Content'
        }
    ]
});

As you can see, we’ve tweaked the west and center regions a bit, as well as added the north region.

And we’re done. If you refresh your app in the browser, you should now see something like this:

Viewport

A Bit O’ Refactoring

At this point, we’ve finished our layout. The one downside is that it’s all contained within the single Viewport.js file, even though we technically have 3 major layout sections of this app (header, sidebar, maincontent). In these early stages, it’s manageable to have it all in one file; however, as our app inevitably grows, this file might become unwieldy. Refactoring our “regions” into their own proper classes will allow us to maintain our current layout structure, but manage each region as its own file.

To do this, let’s start by creating a new package called layout in our view folder. This package can hold these core layout files, as well as any others that we might add in the future.

As you develop ExtJS 4.2 apps, you’ll quickly learn that packaging views is an incredibly important practice. Not only does it allow you to logically group like-views together, but it also has the benefit of allowing you to incorporate common naming standards across packages. As with other package naming conventions, it is recommended that any custom packages use a lowercase, singular standard (e.g., layout, edit, search, etc.).

In our new layout package, create three new files: North.js, West.js, Center.js:

North.js

Ext.define('CarTracker.view.layout.North', {
    extend: 'Ext.panel.Panel',
    // alias allows us to define a custom xtype for this component, which we can use as a shortcut
    // for adding this component as a child of another
    alias: 'widget.layout.north',
    region: 'north',
    bodyPadding: 5,
    html: '<img src="resources/images/car.png" /><h1>Car Tracker</h1>',
    cls: 'header',                   
    initComponent: function(){
        var me = this;
        Ext.applyIf(me,{

        });
        me.callParent( arguments );
    } 
});
West.js

Ext.define('CarTracker.view.layout.West', {
    extend: 'Ext.panel.Panel',
    alias: 'widget.layout.west',
    region: 'west',
    title: 'Menu',
    split: true,
    width: 200,
    initComponent: function(){
        var me = this;
        Ext.applyIf(me,{

        });
        me.callParent( arguments );
    } 
});
Center.js

Ext.define('CarTracker.view.layout.Center', {
    extend: 'Ext.panel.Panel',
    alias: 'widget.layout.center',
    region: 'center',
    title: 'Center Content',
    initComponent: function(){
        var me = this;
        Ext.applyIf(me,{

        });
        me.callParent( arguments );
    } 
});

As we see, all the config code for our regions from Viewport.js has been moved into individual classes. Now that we’ve done this, we can trim our Viewport.js content significantly:

Viewport.js

Ext.define('CarTracker.view.Viewport', {
    extend: 'Ext.container.Viewport',
    requires:[
        'Ext.layout.container.Border',
        'CarTracker.view.layout.North',
        'CarTracker.view.layout.West',
        'CarTracker.view.layout.Center'
    ],
    layout: {
        type: 'border'
    },
    items: [
        { xtype: 'layout.north' },
        { xtype: 'layout.west' },
        { xtype: 'layout.center' }
    ]
});

A few things to notice:

First, since we classed out our regions, we need to let Viewport.js know about them. Therefore, we simply add them to the requires[] config, and the dynamic loading magic of ExtJS 4 takes care of making sure they are loaded and ready before the Viewport is instantiated.

We could have added these to the views[] config of app.js as well. However, as our app grows, adding every view necessary to the application would become unwieldy…plus, having the clue of the requires within the Viewport file gives us context for how these files are used within our app.

Next, notice that we are adding our regions by xtype. This is a shortcut way of creating an object, and obviates the need for creating the object by the full class path (e.g., CarTracker.view.layout.North).

Be careful when defining your xtypes. They are stored in a single shared scope, so they need to be unique. A strategy that I’ve found to be very useful is to use the package as part of the xtype name (e.g., view/layout/North.js becomes layout.north). If this standard is consistently implemented, it guarantees uniqueness since the file to which it is tied must be unique within its package.

Perfect. If we reload the app, we’ll notice that it looks precisely the same as before. However, now that we’ve refactored, it will be much easier to segregate (and test!) changes to the views as they are required as the app grows and changes.

Conclusion

In this installment of the walkthrough, we’ve looked at how to layout the skeletal frame of our application. We’ve learned a bit about the Viewport, and we’ve even done our first (and certainly not last!) round of refactoring to make our code cleaner and easier to maintain.

For our next section, we’ll create our first data model and store as we begin to lay the ground work for our application interacting with the server.