Ext JS and MVC

We've looked at the origins of MVC and the way it was adapted for traditional server-side web applications. How does it work when we use it with the kind of JavaScript-heavy application we'd typically build using Ext JS?

The whole MVC concept moves entirely into the browser. As far as we're concerned, the server can use any technology it wants. It'll generally just provide and consume data to and from the browser. We move back to an MVC implementation that is a little more like the Smalltalk version (different UI elements you see on-screen are views) and each can have their own controller.

Again, this is about breaking down responsibility. Instead of having a single controller take care of an entire page, we can have a search controller, a list controller, and a detail controller (anything that represents the logical units that make up our application). This is a key detail in how the step from server-side MVC to client-side MVC can help our application architecture.

We already know that Ext JS Components are our views, and Ext JS models are well named to fit right in. We're left with one important question: what are controllers actually supposed to do? It's probably easier to remove the things we know they don't do and see what's left. We know that models deal with data, but they're also responsible for the calculations and logic around this data. Calculations and rules, for example, belong in a model, but not in a controller.

Tip

This is a generalization. In many cases, you'll have other classes that do this logic work in order to further break down your application. The important thing to take away is that you do not want domain logic in your controller!

We also know views deal with presentation. You could build up an HTML string in your controller and pass it to the browser for rendering, but this would involve the controller in something which is the view's responsibility.

What are we left with? In truth, not much. All your controllers need to do is be in charge of your views and models. That's it. They look at the request the user is making, fetch a model, and use it to render the view to the browser.

In fact, if your controller is doing more than this, you need to take this as a bad sign. A controller should be the conductor of your orchestra, not the one making the music.

Examples of Ext JS MVC

The following screenshot shows our Ext JS v4 MVC test application:

Examples of Ext JS MVC

We've generated a stock Ext JS v4 application here, which sticks to the MVC structure and then we've amended it to suit our needs. In this small app, there's a grid of music albums on the left. When you click on the button on the grid, it generates a summary of the artists who are mentioned in the grid, and when you double-click on a row, it puts the album information in the right-hand pane. It's a toy application, but it's useful to demonstrate how MVC works. Later, we'll compare it with a similar application written with Ext JS v5. Let's take a look at the code:

// view/List.js
Ext.define('MvcEx1v4.view.List', {
    extend: 'Ext.grid.GridPanel',
    alias: 'widget.app-list',
    store: 'Albums',
    forceFit: true,
    frame: true,
    requires: ['Ext.Msg'],

    columns: [
        { text: 'Name', dataIndex: 'name' },
        { text: 'Artist', dataIndex: 'artist' }
    ],

    initComponent: function() {
        this.bbar = [
            '->',
            { xtype: 'button', text: 'Show Artist Summary', handler: this.onShowSummary, scope: this },
            '->'
        ];

        this.callParent(arguments);
    },

    onShowSummary: function() {
        var summary = this.getStore().collect('name').join(', '),

        Ext.Msg.alert('Artists', summary);
    }
});

Here's our MvcEx1v4.view.List class in view/List.js. It's a fairly straightforward grid that uses a store called 'Albums' and a button on the bottom toolbar to generate the artist summary. Notice that the event handler to generate this summary is included in the view:

// view/Detail.js
Ext.define('MvcEx1v4.view.Detail', {
    extend: 'Ext.Container',
    alias: 'widget.app-detail',
    html: 'Double-click an Album to select'
});

Our second view is MvcEx1v4.view.Detail in view/Detail.js. This is just a simple container with some placeholder HTML. Finally, we have the application viewport that holds our views:

// view/Viewport.js
Ext.define('MvcEx1v4.view.Viewport', {
    extend: 'Ext.container.Viewport',
    requires:['MvcEx1v4.view.List'],
    layout: 'hbox',
    defaults: {
        width: 250,
        margin: 20
    },
    items: [{ xtype: 'app-list' }, { xtype: 'app-detail' }]
});

Again, there are a few surprises here. Notice that we refer to our views using the values we defined in their individual "alias" configuration options: app-detail and app-list. We've taken care of the "V" in MVC, so let's move on to "M" and see where our data comes from, as shown in the following code:

// model/Album.js
Ext.define('MvcEx1v4.model.Album', {
    extend: 'Ext.data.Model',
    
    fields: [
        { name: 'name', type: 'string' },
        { name: 'artist', type: 'string' }
    ]
});
// store/Albums.js
Ext.define('MvcEx1v4.store.Albums', {
extend: 'Ext.data.JsonStore',

   model: 'MvcEx1v4.model.Album',

   data: [
        { name: 'In Rainbows', artist: 'Radiohead' },
        { name: 'Swim', artist: 'Caribou' }
    ]
});

For easier reading, I've combined the code for the model and the store that consumes it. The data for this application is added inline using the data configuration option (just to avoid messing around with server-side Ajax calls). Let's look at the final facet of MVC, the controller:

// controller/Album.js
Ext.define('MvcEx1v4.controller.Album', {
    extend: 'Ext.app.Controller',

    refs: [{
        ref: 'detail',
        selector: 'app-detail'
    }],

    init: function() {
        this.control({
            '.app-list': {
                itemdblclick: this.onAlbumDblClick
            }
        });
    },

    onAlbumDblClick: function(list, record) {
        var html = Ext.String.format('{0} by {1}', record.get('name'), record.get('artist'));

        this.getDetail().getEl().setHTML(html);
    }
});

Here's where things start to deviate from the straightforward view to data implementation you'd typically see in an Ext JS v3 application. We're introducing a new class that brings in a new architectural construct. But to what end?

The answer is communication. The controller, as we know, is the glue that sticks together the "M" and the "V". In our simple example here, it's giving us a mechanism to let the list view talk to the detail view without either of them having to be aware of each other. The control feature is used to determine what to do when the list view (aliased as app-list) fires an itemdblclick event.

We supply the onAlbumDblClick method to respond to this event. In here, we want to talk to our detail view (aliased as app-detail). We previously used the refs configuration option to help with this. Let's break it down:

refs: [{
    // We give our ref the name "detail". This autogenerates
    // a method on the controller called "getDetail" which
    // will enable us to access the view defined by the selector.
    ref: 'detail',

    // The selector is passed to Ext.ComponentQuery.query,
    // so any valid component query would work here. We're
    // just directly referencing the app-detail alias we
    // set up in the view's configuration
    selector: 'app-detail'
}]

Long story short, the refs feature gives us a shorthand way to access a view. In the onAlbumDblClick handler, we use the autogenerated this.getDetail() method that refs provides. This gives us a reference to the view. We can then set HTML of its view's element based on the event data provided by the list view.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset