ExtJS is probably one of the biggest challenges when it comes to building components in MODX. It's not mandatory to use it, but as most people will want their components to blend in perfectly, it's an easy choice to make... This blog post will hopefully shine some light on how modExt and ExtJS blend together, and how to use the ExtJS API Docs to keep you going. Oh, and I'm throwing in some ExtJS tips & tricks here and there for the careful reader.

What exactly *is* modExt?

Real simply put you could say modExt is the same as ExtJS, but then with some additions (extensions) by the MODX Core Team and the actual back-end theme installed by default. As ExtJS is a class (or object) based JavaScript framework, so is modExt. It's important to remember that you're not limited to just the modExt implementation - no, you have the entire ExtJS framework at your disposal. Parts of that ExtJS framework have been extended (remember it's an object-based framework? Long live OOP!) to provide some specific functionality. This extended functionality is available through the "MODx" JavaScript variable - all non-overriden functions and classes are available in the "Ext" variable.

Why is it important to realize modExt is just an extension?

Well, if you are anything like me when I first started with components, you have been staring at the modExt documentation for ages but never actually got anything working out of that information. Which now makes sense, as it only lists the (most important) extended classes - not everything you can use!

That's where the ExtJS 3.4.0 API Docs come in. And boy, they are huge! And, if you know where to look, they're definitely very, very valuable.

Getting Practical: taking bdListings as an example

Just to catch up (I assume that if you made it this far into the post, you at least have been working with modExt/ExtJS a bit and understand its general syntax), here's an example of a grid using the modExt implementation of an ExtJS grid which I'll be tearing apart to show the difference between modExt and ExtJS. As it's a quite long example I have cut out some pieces (denoted by /* ... */), you can see the complete source on Github if you want.

bdListings.grid.TargetGroups = function(config) {
    config = config || {};
    Ext.applyIf(config,{
        url: bdListings.config.connector_url,
        id: 'grid-targetgroups',
        baseParams: {
            action: 'mgr/targetgroups/getlist'
        },
        params: [],
        viewConfig: {
            forceFit: true,
            enableRowBody: true
        },
        tbar: [{
            xtype: 'button',
            text: _('bdlistings.create',{ what: _('bdlistings.target') } ),
            handler: function() {
                win = new bdListings.window.TargetGroups();
                win.show();
            }
        }],
        paging: true,
        primaryKey: 'id',
        remoteSort: true,
        sortBy: 'order',
        fields: [
            {name: 'id', type: 'int'},
            {name: 'name', type: 'string'},
            {name: 'sortorder', type: 'int'}
        ],
        columns: [{
			header: _('id'),
			dataIndex: 'id',
			sortable: true,
			width: 1,
            hidden: true
		},{
			/* ... */
		}]
    });
    bdListings.grid.TargetGroups.superclass.constructor.call(this,config);
};
Ext.extend(bdListings.grid.TargetGroups,MODx.grid.Grid,{
    getMenu: function() {
        var r = this.getSelectionModel().getSelected();
        var d = r.data;

        var m = [];
        m.push({
            text: _('bdlistings.update',{what: _('bdlistings.target')}),
            handler: function () {
                win = new bdListings.window.TargetGroups();
                win.setValues(d);
                win.show();
            }
        },'-',{
			/* ... */
        });

        if (m.length > 0) {
            this.addContextMenuItem(m);
        }
    }
});
Ext.reg('bdlistings-grid-targetgroups',bdListings.grid.TargetGroups);

What I want to highlight with this example are the parts given to you by modExt, and what is actually plain ExtJS. I'll just go through the code from top to bottom and highlight a few things of importance.

  • Line 4, 6-9, 26-39. In vanilla ExtJS, every Grid has a data store, which holds the data definitions and values. As MODX components mostly work with AJAX connectors which return JSON, you get the opportunity to instantiate a store without assigning one manually. This works by defining the "url" property (which in this case is the value of the bdListings.config.connector_url, pointing to assets/components/bdlistings/connector.php which acts as our, well, connector). Beyond the url property, we also need to define our action (we can do this as a baseParam or as a regular configuration option - both work), the different fieldnames we expect (line 26-30) and what columns we want to display (line 31-39). These variables are used to create an Ext.data.JsonStore which works seamlessly with the JSON array returned by processors. Conclusion: these properties are a shortcut to an Ext.data.JsonStore, but if you prefer you can still define them manually as you will find all over the Sencha forums and Documentation.
  • Line 16,50. To add easy internationalization (commonly shortened to "i18n" for "starts with an i, then 18 characters, and ends with a n"), you will notice a tiny function with the following signature: _('key',{arrayOf: 'options'}). This is a JavaScript function available throughout the MODX Manager that checks the loaded Lexicons for the key you want. If there's any placeholders in them, you can specify the values for those via the second parameter. Conclusion: this is MODX specific and you wont find it in the ExtJS forums/docs.
  • Line 14-20: While toolbars (defined as a "tbar" or "bbar" for bottom toolbars) are not specific to modExt and they work the same as the ExtJS tbar configuration, there are cases where modExt throws in some bonuses. For example when extending a MODx.tree.Tree you will see that there is already a toolbar defined. Anything you add to the tbar configuration is added after it. If you don't want the default buttons (expand, collapse and refresh) you can set the useDefaultToolbar property to a boolean false and it will only load your own tbar config. Anything else is inherited from ExtJS. Conclusion: works the same as ExtJS, but in some components these will have default configurations.
  • Line 43 shows we are using our own object bdLisings.grid.TargetGroups from the MODx.grid.Grid object. If we were to look at the MODx.grid.Grid documentation, one of the first things we'll see documented is that it extends the Ext.grid.EditorGridPanel object (in turn extended from the Ext.grid.GridPanel object), which means we can use all of that object's properties and functionality with the added stuff from the MODx.grid.Grid documentation. Conclusion: Check out the Ext.grid.EditorGridPanel documentation often!
  • Line 44-63 is a true genius (and also mentioned in the MODx.grid.Grid documentation), and is what shows your right click context menu without having to mess with "rowcontextmenu" listeners, mouse positions and what not (boy has that taken me debug time before!). Basically you instantiate a new Array (in the core often called "m" and use m.push() with the Ext.menu.Item configuration options for every item you want to add to the menu. In the end you can (line 60-62 in this example) call the components' addContextMenuItem method, or (as of MODX 2.1) simply return the array "m". On line 56, the dash will be transformed into a subtle border between the menu items. Note that you can access the right-clicked item in the grid as "this.menu.record" in case you need conditional menus. Conclusion: This is one of the modExt extensions I'm really fond off.

I think my point would be clear... use the ExtJS 3.4 Documentation! Now, on to the next question.. how do we use that?

How to work with the ExtJS Documentation

The ExtJS documentation is huge and really has all the answers you need. Just formulate the questions right!

First find out what the class name is of what you are looking to use. Perhaps you are looking at someone else's code, check out the Ext.extend() line in that case. To related back to the grid example, we saw that extended MODx.grid.Grid which in turn extends Ext.grid.EditorGridPanel. That's the info we need! Open up the ExtJS docs and simply navigate the right tree. Ext > grid > EditorGridPanel. You'll see a few things:

  1. Usually a description of what the class does and some examples. An inheritance chart to the right to see where it's coming from.
  2. Then the config options, which is what you will most often work with. They usually list the default and possible values and, depending on the option, also some code examples.
  3. Somewhere down (halfway the EditorGridPanel doc) you'll find the Properties section. These are variables available "to the outside" when you have a reference to an object. For example "this" depending on the context or "Ext.getCmp('id-of-my-grid')" from anywhere else. You can use this to check the state or configuration of an object.
  4. Further down you'll find the Methods, you can call these on the object. The methods may change its configuration (eg addClass), or return a value to you (eg getPosition). Whenever I have a reference to an object, I like to send it to the console with console.log, allowing me to see that list of methods in Firebug when I need it. (Chrome's Dev Tools doesn't seem to show the available methods).
  5. Waaaay at the botton you'll find a list of Events. These are being triggered by the component or it's parent objects and allow you to hook into certain stuff going on. On top of listing the events, it also lists the parameters it can pass to your callback function.

So basically that's all the information you can possible need about the ExtJS objects. Now get cracking with that component!

Note: I know that doesn't even come close to everything you may need to know, but I think it would've helped me back when I started to have someone point me to a list of events and configuration options for modExt objects.. I've used a bookmark to check what events are bubbling up for months, and abusing console.log into eternity, but having an actual list is amazing! ;)

Oh, one last thing about modExt..

It's very helpful to check out what modExt offers you, and what default configuration may be set if you are extending MODx objects. You can find all the modExt files in /manager/assets/modext/. As there's quite a lot of files in there, I'd like to highlight what you can find in the different folders (relative to the /modext/ folder):

  • core - contains the building blocks of modExt. The actual MODx object in modx.js, the layout builder in modx.layout.js and the base component in modx.component.js. Not the best place to start with modExt if you'd ask me.
  • sections - in the terminology used by the MODX team for both the MODX Core and a number of Extras, a "section" is what kicks off building the interface, by loading an xtype and possibly setting variables. Not too interesting as an extra developer imo.
  • util - contains a few extra xtypes/components you can use including a date+time field, a lightbox and the core upload dialog. The utilities.js file also contains 3rd party components such as superboxselect and some other little gems. Something to check out some time.
  • widgets - now it's getting interesting! A widget can be anything from a panel, grid or window to an individual combobox. The base objects are located in this widgets/core directory, so you can find the MODx.grid.Grid definition there, as well as MODx.tree.Tree and the MODx.Console as well as many more. Their applications in the MODX Manager are in all the other folders under widget, so if you see something in the Manager you want to mimic.. the odds are they're in the widges folder :)
  • workspace - the workspace is special as it's widgets and sections in one folder, specifically for the workspace (basically Package Manager and everything around it).

Don't be afraid to check out the source of the Core or other Components!

Hi Mark, Great tutorial! It has taken me hours to figure this out until I came accross your blog :)

Is is to possible to extend your example with a summary row? I've found some documentation on this for Ext JS 4.0 (http://www.objis.com/formationextjs/lib/extjs-4.0.0/docs/api/Ext.grid.feature.Summary.html) and was hoping I could implement it but is doesn't work :s

Thanks!

Hi Mark, Great tutorial! It has taken me hours to figure this out until I came accross your blog :)

Is is to possible to extend your example with a summary row? I've found some documentation on this for Ext JS 4.0 (http://www.objis.com/formationextjs/lib/extjs-4.0.0/docs/api/Ext.grid.feature.Summary.html) and was hoping I could implement it but is doesn't work :s

Thanks!

For now I've solved it like this:

public function beforeIteration(array $list) {
$list[]=array("id"=>"Total","test","test","test","test");
return $list;
}

public function afterIteration(array $list) {
$list[]=array("id"=>"Total2","test","test","test","test");
return $list;
}

This gives me the opportunity to add a summary but the disadvantage is that adds the update functionality to these rows as well. It's not the cleanest sollution but it does the trick...

MODX uses ExtJS 3.4 right now which is pretty different from 4, and it doesn't appear that the feature mentioned there is in 3.4 as well :(

Comments are closed :(

While I would prefer to keep comments open indefinitely, the amount of spam that old articles attract is becoming a strain to keep up with and I can't always answer questions about ancient blog postings. If you have valuable feedback or important questions, please feel free to get in touch.