Home Java Principles of writing ExtJS 2.x/3.x applications

Principles of writing ExtJS 2.x/3.x applications

by admin

The ExtJS framework for cross-browser development is very popular right now.It’s a truly grandiose (and monstrous) set of components, classes, functions, helpers, etc.that can both make a developer’s life easier and harder.Generally speaking, ExtJS (up to version 4) does not set any "rules of the game" for the end developer: formally, there are no requirements or guidelines for designing and writing robust applications.
Surprisingly, the quality of ExtJS beginner’s guides and tutorials still leaves a lot to be desired, to say the least. So does the standard ExtJS API help, though. [1]
The purpose of thisarticle is to show you how to write ExtJS-based applications so that the person who will maintain your code won’t want to tear their hair out, and you just won’t feel ashamed. But seriously, in thisarticle I will offer a simple and short set of rules for designing and writing applications in relation to thisframework.

Do away with the hard DOM id

The ExtJS examples often use the technique of hard-coding an id to a component and then calling Ext.getCmp(id) quickly. Everything looks fine in the "Hello world" level examples, but as soon as the project gets larger, you find mush instead of code.
The second important disadvantage of this approach is that the id must be unique within the entire document (DOM tree).

Always highlight and isolate components

It’s very simple. We replace the instantiation of standard components with inheritance. Even if at this stage you just see a panel with a certain border and text inside it, inherit from Ext.Panel and define properties in the class itself.
Don’t turn code into a "declare object – add to container" sequence.
In other words, avoid code similar to the following :

var p = new Ext.Panel({region: 'north', height: 30, html: '<b> Test</b> '});var mainPanel = new Ext.Panel({layout: 'border', items: [p, {region: 'center'title: 'Center'}]});

Very quickly this code will turn into a mess which even its creator cannot make out. Besides, the growth of dependencies between such items in the code is nearly uncontrollable.

Nested config objects : remember scope

Don’t forget that any function in JS can be called on behalf of any object. In other words, this inside a function can point anywhere, generally speaking. In cases where we call a function ourselves, syntactically we can control this situation. But in cases where a config-object contains functions (event handlers) on different nesting levels, the scope problem becomes obvious for them. In most cases this is a kind of "reference point" for the handler logic. Lost this – and it’s not at all clear how to get to the neighboring button to hide/sabotage it, etc., etc.
The javascript language provides at least the following methods of the Function object: apply() and call() [2]
The ExtJS kernel supplements the Function prototype with the createDelegate(scope) This function returns a wrapper function around the original function, so that your original function is guaranteed to be called in scope.
For ExtJS listeners and iterators, you can always specify explicitly which scope the specified function should be called with [3]

Addressing nested components

Nested components are always located in the owner’s items collection. There are at least three ways to get to a child element :

  1. Accessing a collection item by index
    • non-obvious code; toothy constructions like return this.get(0).get(3) or worse return this.items.items[0].items.items[3]
    • changed the order of the components in items – everything crashed
    • items before rendering parent in DOM document is just an array (see below).
    • Accessing an item in the collection by itemId. This identifier is not tied to the dom id, but makes item access more meaningful. Besides, it doesn’t depend on the order of declaration of components in the parent.
    • Referencing via ref. Ref is a way to specify what a reference to a given element will be called when it is created, and at what level that reference will be placed.

    I recommend using ref whenever possible (code is shorter, clearer, no collection iterations, no unnecessary function calls).
    You can see an example of the use of ref and itemId in the following example :

    Ext.ns('Util');Util.MyPanel = Ext.extend(Ext.Panel, {constructor: function (config) {config = config || {};Ext.apply(config, {border: false, layout: 'card', activeItem: 0, items: [{xtype: 'panel', itemId: 'mainScreen', layout: 'border', items: [{//...skipped...}]}, {xtype: 'editpanel', itemId: 'editScreen', listeners: {scope: this, cancel: this.showMainScreen, finish: this.onRuleSaved}}], buttons: [{ref: '../editBtn', handler: this.onEditBtn, scope: this}]});Util.MyPanel.superclass.constructor.call(this, config);}, showMainScreen: function () {this.getLayout().setActiveItem('mainScreen');this.editBtn.enable();}, showEditScreen: function () {this.getLayout().setActiveItem('editScreen');}, //... other methods});Ext.reg('mypanel', Util.MyPanel);

    Register your own events

    Events in ExtJS are the main mechanism of widget interaction. It is a kind of way for a component to inform the outside world of its state. Each widget can be a listener of others’ events, as well as a source of its own. A component can have an arbitrary number of listeners, and they will all be notified about the event. The order of notification of the listeners, in general, is not defined.
    Events imply a minimum of assumptions about the recipients and their number, which greatly reduces the coherence of the code. This in turn makes the component portable and allows to reuse it in other combinations.
    If your component has a state that external objects might want to know about, register your own event.
    If you create a composite widget and the internal component has something to tell the external one, use your own event.

    The object before and afterrender. When afterrender is needed.

    Every visual component is based on the DOM elements of the browser page. The component has a state where as an object it has already been created but has not yet been "rendered" in the DOM tree. In this state any reference to the internal DOM structure is impossible. The object in this state is semifunctional : it formally has all the declared methods, but there is no guarantee that they will work correctly.
    As soon as a component commits itself to the DOM structure, it generates a render event and then an afterrender event. To be honest, there isn’t much difference between these events, but normally the render event is used internally by the component itself and there is a high probability that you will accidentally override the render event handler with your own method, for example calling it onRender(). It won’t be so easy to catch such an unobvious problem, and as you know, ExtJS always crashes silently.
    Remember that a component tells you that it is already rendered, before its children are rendered.
    It is considered good form to correctly handle any calls to a component that require DOM elements to exist. If a component is not rendered, those operations should be "postponed" until it is rendered – in that case, just dynamically assign a handler to your own afterrender.
    Below is an example of a function which correctly handles the situation when it’s called before the component is rendered in the DOM:

    //...other property definitions...showDocFeedOptions: function (show) {var fn = function () {this.feedDocsFieldSet.setVisible(!!show);};if (this.rendered) {fn.apply(this);}else {this.on('afterrender', fn, this);}}

    Lazy initialization. Xtype

    The magic property (another "tricky property") xtype of a configuration object uniquely determines the type of the object being constructed. This means that if we pass such a configuration object to the universal Ext.ComponentMgr.create() factory (which is called automatically for every nested config object), we’ll end up with a copy of the corresponding class.
    This approach makes it possible to :

    • Create nested configuration objects where you can see the widget structure
    • avoid the sequence of manual creation of objects and late addition to containers
    • create components as needed (which, in turn, will increase the rendering speed of your application)

    In lieu of a conclusion, a little bit about design

    As a rule, in practice you have to deal with quite complex controls and components. In this sense, it is useful to break them into logical blocks (widgets), which are simple or composite components. It is very important to think through the structure and the scheme of control transfer between the elements of the composite widget. In my practice I use the following approach :

    1. The external component (let’s call it the "parent") knows about the nested (child) elements. The reverse is not true.
    2. The parent can refer directly to the child elements.
    3. Child elements report information "upstairs" only through the event mechanism. The child element does not have to make any assumptions about the parent and its set of methods.
    4. "Horizontal" links between child elements (calling each other’s methods directly) are strictly forbidden. Only the parent deals with the synchronization of the "children".
    5. Referencing via a constant DOM id (Ext.getCmp()) is strictly forbidden.

    Useful Links :

    1. ExtJS 3 API
    2. Keyword "this" in detail
    3. Four Tips for Staying on Track With Scope in ExtJS

    You may also like