ExtJS Grid and Custom Manager Pages

This is the 3rd post in a multi-post series on how to make a MODX Extra, with Custom Manager Page (CMP), based on the making of the OAuth2Server and Zapier Extras.

By YJ Tso
December 18, 2015
ExtJS Grid and Custom Manager Pages

This is Part 3 of a 3-part series. If you haven't read Part 1, I'd suggest starting there for background info that's relevant to this post.

Despite the fact that John Peca was away on a remote island while I was doing all this, it really cannot be overstated how much I relied on his "input". Again, I scrutinized the work he did on a component with similar features, and thus learned the lion's share of what I needed.

Upon his return, I got on a call with him and picked his brain. Here's the culmination of what I've learnt so far about ExtJS and CMPs. For brevity, in the following description when I refer to "core path" and "assets path", I mean the "namespaced path for the Extra", usually: {$core_path} or {$assets_path} and then components/{$namespace}/. Also, I'll refer to the filenames from the OAuth2Server Extra, as examples, so it might be helpful to have the files from the repo open while you follow along.

  1. GPM adds the menu action to the package it generates, so when you install your Extra, there'll be a menu item under "Extras". Revo > 2.3.x automatically loads an action class from the controllers directory of the core path, with the filename {$action}.class.php. In this case: {$core_path}components/oauth2server/controllers/manage.class.php.

  2. The "manage" class includes the "index" class from the parent directory. Still not sure why both these classes couldn't be in one file, even though I asked John about this—I didn't understand the answer or maybe wasn't listening closely enough LOL. It might be preference, or for code organization, because the class in the "manage" file extends that in the "index" file. Anyways these two classes have methods that register the CSS and JS required for your CMP. The "index" file also has a method that loads lexicon topics into the ExtJS config. More on lexicon usage later.

  3. The assets folder has a file connector.php. This instantiates MODX itself, along with the OAuth2Server class, and acts as a "gateway" to the processors, usually found under the core folder. In other words: {$assets_url}components/oauth2server/connector.php -> hands requests to -> {$core_path}components/oauth2server/processors/{$action}

  4. The processors do things like: get lists of objects, validate the inputs, create objects from that input, etc. But where do all these inputs come from? Here's where the ExtJS "magic" (or "devilry", depending on your viewpoint) happens.

  5. In the assets folder, there are subfolders for CSS and JS. CSS makes stuff look nice. Under the js/mgr/ folder there's a JS file, in my example oauth2server.js. In that file, we "extend" the Ext.Component "class" and "register" it, so ExtJS "knows" about it. (For convenience I'll use the keywords introduced in ECMA 6, but when ExtJS 3.4 and Revo were first developed, "class" wasn't really a "thing" in JS.)

  6. In a subfolder sections/ there's a file manage.js, which extends MODx.Component and registers it.

  7. In the subfolder widgets is where the fun begins. The manage.panel.js file extends the MODx.Panel class and registers it. But it also configures the panel with tabs, one for each custom-registered "xtype". That brings us to...

  8. The actual widgets; for example, clients.grid.js extends MODx.grid.Grid and registers it. Note the first argument to Ext.reg() is the xtype value used in manage.panel.js—see how that connects? A registered component can be configured as part of another component by referencing its "xtype". I'm not pretending to understand fully how this works but a lightbulb went off in my head when I saw this so I thought I'd point it out :P

There's a LOT going on inside clients.grid.js so let's start a new list. That'll make this insanely large post more readable, right?

  1. Near the top, there's a function Ext.applyIf(), which basically sets config options. Wherever you see an action property in the options, it's setting a target class file, within the Extra's processors/ folder that we talked about earlier, to which to send requests, via the connector. (It can't send requests directly to class files in the core folder because the core folder is not supposed to be web-accessible.)

  2. There's a bunch of options to configure the grid columns. I won't go into an exhaustive review of those right now, but here are some docs on ExtJS grids.

  3. There's a tbar property that adds buttons to the top of your grid. Note alot of the properties that define UI text strings or labels refer to MODX lexicon entries, like this: _('oauth2server.clients.add'). Those are defined in {$core_path}components/{$namespace}/lexicon/{$cultureKey}/{$topic}.inc.php and loaded by the "index" class file we talked about earlier.

  4. The Ext.extend() function defines our custom methods:

    • getMenu() adds a context (right-click) menu.
    • addClient() is the method called by the button defined in the config. It loads another component, which extends MODx.Window, defined in the file clients.window.js. It's the modal window with the form elements to create and update individual row items.
    • updateClient() is similar to addClient() but it passes in a custom variable—we'll go over that when we review the clients.window.js file.
    • duplicateClient() calls the create action but prefills the field values.
    • removeClient() calls the processor that deletes a row item, with a call to MODx.msg.confirm(), to show a confirmation window.

Now we have a grid, with a button and context-menu items that do things. The last piece of the puzzle is the clients.window.js.

The bulk of this file is a call to Ext.applyIf() so basically we're configuring this modal window. By now you may recognize some of the properties, like action for example.

The fields property defines an array of form fields, each having some options to play with. xtype is the only one that isn't self-explanatory, because there are lots of MODX-specific xtypes as well as those added by Extras. I don't even think they're all documented in any one place, except Bob Ray has a list.

The only other interesting bit is the custom variable passed through to set the client_id field to readonly:true. John clued me in to the fact that custom variables can be used, and that readonly was was the property to use. Previously I'd tried disabled: true but in that case the client_id field value got stripped from the request to the processor.

Take-away: don't use disabled.

Wow. Ok, so that pretty much sums up my adventures in CMP-land. Did I wake up afterwards, only to realize it was all a crazy dream? Haha. No.

But I did have this tiny glass bottle in my pocket with a label that said "MODX Me" :P

Other posts in this series: