Friday, November 23, 2012

Use itemId instead of id

What is Id and ItemId: Id and itemID both are the means of identifying a particular dom element from a list of dom elements. Whenever, we need to get the object of an element in ExtJs, we use Ext.getCmp(‘id’). The id must be unique in a class.
Difference between id and itemId: Although the purpose of id and itemId are same i.e. to get the reference of corresponding Dom element at run time, yet there is a very important difference between them. Id must be unique in a class and if it is repeated, then the screen, that displays the object of this class, will have more than one same id and thus resultant screen will be either distorted or will not behave as per expectation.
But, the scope of itemId is limited only to the container or object of the class in which it is being used. That is, if we define the same itemId in two different containers in the same class or same itemId present in two objects of a class, in both scenarios, screen will not be distorted and will behave as per expectation
Scenario:  Let us consider that there is a class that has a method that creates a panel object. This panel object contains a label and a button and on click of this button, we have to change the text of the label. We have to create such two panel objects and display it on html page.
First, we will do this by providing an id to this label and on click of this button we will get the reference of this label by queryById (‘id’) and will assign it new text. E.g.

    Ext.define('CustomClass',{
        config:{
            val:'This is testing',
            panelObj: {}
        },
        
        btnClickHandler: function (valob) {
            this.panelObj.queryById('labelId').setText('Value changed');
        },

        createPanel: function(){
            this.panelObj = Ext.create('Ext.panel.Panel',{
                  layout: 'vbox',
                  width: 300,
                  height: 300,
                items:[
                       {
                           xtype:'button',
                           text: 'Click me',
                           itemId: 'clickMeBtn',
                           listeners: {
                                    click: Ext.bind(this.btnClickHandler, this)
                           }
                       },
                       {
                           xtype: 'label',
                           id: 'labelId',
                           text: 'Click on button to change me'
                       }
                      ]              
            });
            return this.panelObj;
        }
    });

Code for creating the two panel object and displaying it on screen:
var panelObj1 = Ext.create('CustomClass').createPanel();
      var panelObj2 = Ext.create('CustomClass').createPanel();
     
      Ext.create("Ext.panel.Panel", {
            layout: 'border',
            width: 700,
            height: 700,
            renderTo: Ext.getBody(),
            items:
            [
                  {
                        xtype: 'panel',
                        region: 'west',
                        flex: 1,
                        items: [panelObj1]
                  },
                  {
                        xtype: 'panel',
                        region: 'center',
                        flex: 1,
                        items: [panelObj2]
                  }
            ]
      });
Observation: On screen, we will get two panel objects, but both are distorted and not working well on button click on both panel object.
Now, we are modifying the code of our CustomClass.js and use itemId instead of id in case of label as below:
                       {
                           xtype: 'label',
                           itemId: 'labelId',
                           text: 'Click on button to change me'
                       }
Now, if we create two panel object and display it on screen, it will work perfectly.
Conclusion: If we know that there will be only one occurrence of the view component on screen, we can easily use id otherwise we have to use itemId.

Monday, November 19, 2012

Reference of ‘this’ is not required for event handler


One strange thing that I have always seen that  whenever I tried to use ‘this’ keyword in ExtJs in any event handler, I didn't get the expected result. Either I got some unexpected object or sometimes ‘undefined’ in place of ‘this’. E.g.


    Ext.define('CustomClass',{
        config:{
            val:'This is testing',
            panelObj: {}
        },

       constructor: function(){
                ref = this;
       }

        createPanel: function(){
            this.panelObj = Ext.create('Ext.panel.Panel',{
                layout: 'vbox',
                width: 300,
                height: 300,
                items:[
                       {
                                   xtype:'button',
                                   text: 'Click me',
                                   itemId: 'clickMeBtn',
                                   listeners: {
                                                click: function(){
                                                 // this.panelObj.queryById('labelId').setText('Value changed');
                                                 ref.panelObj.queryById('labelId').setText('Value changed');
                                        }
                           }
                       },
                       {
                                   xtype: 'label',
                                   itemId: 'labelId',
                                   text: 'Click on button to change me'
                       }
                      ]               
            });
            return this.panelObj;
        } 
    });


In above code, if we use " this.panelObj.queryById('labelId').setText('Value changed');", I will get some unexpected result. So, I have to keep the reference of 'this' in a 'ref' variable in constructor and then I used this ref variable in the click handler to get required result.

Problem in this approach: But, this is not a good programming approach. Here, we are keeping the reference of 'this' at global level in a 'ref' variable. It will work if the above class is singleton i.e. if we will create only one object of this class in whole application. But, what will happen, if we create more than one object of this class and all the object will be active simultaneously. In this case, the 'ref' variable will contain reference of only last created object and thus using ref variable in event handlers of corresponding objects will give unexpected result.

Solution of this problem: To solve this problem, we can use bind() function provided by ExtJs. With the help of this function, we can directly use 'this' instead of 'ref' in event handler of this class. The bind() function binds a method to an event and accept a scope(may be 'this' or other object) and some more arguments. The syntax of this function is:

bind( fn, [scope], [args], [appendArgs] ) : function

fn: This is a function to be bound with an event.

scope: This is an optional parameter. It is used to provide the scope in which the function will run. E.g. here we can provide 'this' as a scope.

args: This is an optional parameter. It is an array. Overrides arguments for the call. (Defaults to the arguments passed by the caller).

appendArgs: It is an optional and boolean/number parameter. If True args are appended to call args instead of overriding, if a number the args are inserted at the specified position.

Following is the example of how can we modify our above code to use 'this' directly in an event handler  without keeping any reference of 'this' by using bind() function.


    Ext.define('CustomClass',{
        config:{
            val:'This is testing',
            panelObj: {}
        },

        btnClickHandler: function () {
            this.panelObj.queryById('labelId').setText('Value changed');
        },

        createPanel: function(){
            this.panelObj = Ext.create('Ext.panel.Panel',{
                layout: 'vbox',
                width: 300,
                height: 300,
                items:[
                       {
                                   xtype:'button',
                                   text: 'Click me',
                                   itemId: 'clickMeBtn',
                                   listeners: {
                                                click: Ext.bind(this.btnClickHandler, this)
                           }
                       },
                       {
                                   xtype: 'label',
                                   itemId: 'labelId',
                                   text: 'Click on button to change me'
                       }
                      ]               
            });
            return this.panelObj;
        } 
    });