📜 ⬆️ ⬇️

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

The ExtJS cross-browser framework is very popular now. This is truly a grand (and monstrous) set of components, classes, functions, helpers, etc., which can both make life easier for the developer and complicate it. Speaking generally, ExtJS (up to version 4) does not set any “rules of the game” for the final developer: formally there are no requirements and recommendations for designing and writing reliable applications.
It is surprising that, until now, the quality of benefits and tutorials for ExtJS beginners, to put it mildly, leaves much to be desired. Like the standard help for the ExtJS API, however [1] .
The purpose of this article is to show how to write ExtJS-based applications so that the person who will support your code does not want to tear his hair, and you just are not ashamed. But seriously, in this article I will offer a simple and short set of rules for designing and writing applications for this framework.

Avoid tight binding to DOM id


In the ExtJS examples, the method of rigid assignment of an id to a component is often used, after which it is quickly accessed via a call to Ext.getCmp (id). In the examples of the “Hello world” level, everything looks wonderful, but as soon as the project becomes larger, instead of code, porridge is found.
The second important disadvantage of this approach is that id must be unique throughout the entire document (DOM tree).

Always isolate and isolate components.


Everything is very simple. Instantiation of standard components is replaced by inheritance. Even if at this stage you just see a panel with a certain border and text inside - inherit from Ext.Panel and define properties in the class itself.
Do not turn the code into a sequence like "declare an object - add to container".
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, such a code will turn into a mess, which even its creator will not understand. In addition, the growth of dependencies between such elements in the code is practically uncontrollable.

Nested config objects: remember the scope


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

Referring to nested components


Nested components are always located in the owner's items collection. There are at least three options for how to get to the child:
  1. Accessing a collection item by index
    • non-obvious code; furious constructions of the form return this.get (0) .get (3) or even worse return this.items.items [0] .items.items [3]
    • changed the order of the components in the items - everything is broken
    • items before the parent is rendered in the DOM document is just an array (see below).
  2. Accessing an item by itemId. Such an identifier is not tied to the dom id, but allows you to make the call to the element more meaningful. In addition, this method does not depend on the order of the declaration of components in the parent.
  3. Appeal through ref. Ref is a way to specify how the link to this element will be called, when it will be created, and at what level this link will be placed.
I recommend using ref whenever possible (the code is shorter, clearer, there are no iterations over collections, no unnecessary function calls).

An example of using ref and itemId can be seen 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 - the main mechanism of interaction of widgets. This is a kind of way for a component to inform the outside world about its condition. Each widget can be a listener of other people's events, as well as be the source of its own. In this case, the component can have an arbitrary number of listeners, all of them will be notified of the event. The procedure for notifying listeners is generally not defined.
Events imply a minimum of assumptions about the recipients and their number, which significantly reduces the coherence of the code. And this in turn makes the component portable and provides the possibility of its reuse in other combinations.
If your component has a state about which objects may want to know from outside - register your own event.
If you create a composite widget and the internal component has to tell something external, use your own event.

The object before and after rendering. When need an afterrender


Each visual component is based on the browser's DOM elements. In this case, the component has a state when, as an object, it has already been created, but has not yet been “drawn” in the DOM tree. In this state, any access to the internal DOM structure is impossible. An object in such a state is semi-functional: formally it has all the declared methods, but there is no guarantee that they will work correctly.
As soon as the component commits itself to the DOM structure, it generates a render event and then an afterrender. Honestly, there is no particular difference between these events, but as a rule, the render event is used by the component itself for internal purposes and it is likely to accidentally overlap the event handler with its method, say, calling it onRender (). To catch such an unobvious problem will not be so easy, but ExtJS “falls”, as you know, always silently.
It should be remembered that the component reports that it has already been drawn, before its child components have been drawn.
It is considered good practice to correctly handle any calls to a component that require the existence of DOM elements. In this case, if the component is not drawn, these operations should be “postponed” until it is drawn - in this case simply hang the handler dynamically on your own afterrender.
Below is an example of a function that correctly handles a situation when a call occurs before a component is drawn 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 (the next “cunning”) xtype of the configuration object uniquely determines the type of the constructed object. This means that if you pass such a configuration object to the universal factory Ext.ComponentMgr.create () (and it is automatically called for each nested config object), then we will get an instance of the corresponding class at the output.
This approach allows you to:

Instead of a conclusion - a little about the 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 down into logical blocks (widgets), which are simple or composite components. It is very important to think over the structure and 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 “parent”) is aware of nested (child) elements. The reverse is not true.
  2. Parent can refer directly to child elements.
  3. Child elements communicate information "up" only through the event mechanism. The child element should not make any assumptions about the parent and the set of its methods.
  4. “Horizontal” connections between child elements (calling each other’s methods directly) are strictly prohibited. Only the parent is engaged in synchronization of "daughters".
  5. Conversation through a constant DOM id (Ext.getCmp ()) is strictly prohibited.

Useful links:

  1. ExtJS 3 API
  2. The keyword "this" in detail
  3. Four Tips for Staying on Track with Scope in ExtJS

Source: https://habr.com/ru/post/127908/


All Articles