Extjs - Advanced / Miscellaneous Stuffs

extjs

Preambles:

In order to do more advanced stuffs, we may need to use a debugger and step into Ext JS code, and see which function we need to override. Ext JS offer use a way to override their built-in function, so need to understand how that mechanism works. For example, if we need to add some additional logic on top of the built-in function, how can we invoke the original built-in function? Beside learning about overrides, we also need to visit the Ext JS API documentation page, and use the search box to search for Controller. Read everything on the ExtJS API documentation page, and read a book on Ext JS.

Ext JS goes one step further by adding a convenient mechanism of overrides known as Ext.override and starting from Ext JS 4 with the configuration property overrides.

While debugging I find it handy to write the log records to know when my controllers are initialized. Instead of adding console.log() into the constructor or init of each controller I use overrides. Here is how I prescribe Ext JS to override the behavior of the standard Controller class using new Ext JS 4 overriding mechanism - Ext.Class.override configuration:

Ext.override(Ext.app.Controller, {
   init: function() {
        this.callOverridden();
        console.log(this.self.getName() + ' created');
    }
});

Both versions have the same effect - they override the init method. (As any prototype-based overriding they affect even existing instances of the classes, although in case of init() method this is not relevant). I recommend you use the first technique as it's more compact, readable, and most importantly - safe, since override: 'Ext.app.Controller' ensures that Ext.app.Controller class is loaded by the time the override is executed.

In the classical object model you must inherit a class to override its methods, so these overrides might look weird at first glance. The internal implementation is dead simple - the function declaration gets replaced. The existing declaration is preserved in an internal variable, and you get access to it via this.callOverriden();

  1. this.callOverridden()
  2. this.callParent(arguments)
  3. this.callSuper(arguments)
  4. superclass.initComponent.call(this)

What are different approaches altering built-in behaviors

For clarification: By real class modification I mean a intended permanent modification/extension of a class, which should always be done by extending a class. But it is not a temporary solution for just a specific problem (bug-fix, etc.).

  1. prototype: I guess is well known and allows you to override a member for all instances of a class. This variant should not be used for real class modifications.
  2. Ext.override: does nearly the same then prototype but it fully applies to the ExtJS Class-system which allows you to use callParent()
  3. Extending a existent class to apply additional behavior & rendering. Use this variant to create a subtype that behaves different without loosing the original type.
  4. Overriding per instance will happen in really rare cases and might not be applicable to all properties. In such a case (where I don't have a example at hand) you have a single need for a different behavior and you might consider overriding a setting just per instance. Basically you do such things all times when you apply a config on class creation but most time you just override default values of config properties but you are also able to override properties that references functions. This completely override the implementation and you might allows don't have access to the basetype (if any exist) meaning you cannot use callParent. You might try it with setValue to see that it cannot be applied to a existing chain. But again, you might face some rare cases where this is useful, even when it is just while development and get reimplemented for productive. For such a case you should apply the override after you created the specific by using Ext.override as mentioned above.
  5. Mixins?

How can we override a built-in method?

Ext.define('my.application.form.field.Text' {
    override: 'Ext.form.field.Text'
    getValue: function () {
        // your custom functionality here
        arguments[1] = false;

        // callParent can be used if desired, or the method can be 
        // re-written without reference to the original
        this.callParent(arguments)
    }
});

The above code works with Ext JS 5. so I would then load this file in my Application.js and add it to the requires array there which applies the override to the app globally. I think Ext 6 projects include an override folder and simply adding this file to that folder ensures the override is applied.

We are moving towards using Ext.define as our preferred way to override. A sample override would be like:

Ext.define('Override.form.field.Text', {
    override : 'Ext.form.field.Text',

    initialize : function() {
        this.callOverridden(arguments);
    }
});

When using a framework, I sometimes find a bug, or a certain behavior that I would like to change. Though there are multiple ways to solve this kind of issue, such as extending that class and using that extension in lieu of a base framework class, sometimes the only option is to override a method for a base class. Ffor example, I need to change the behavior of Ext.container.Container. This base class is a superclass for so many UI widgets in the framework. Below is a snippet of the inheritance model for Container.

Screenshot-2014-06-26-15.33.13.png

Ext.override is mainly used for fixing bugs or changing the core behaviors of certain classes. Imagine for a moment using a vanilla Ext JS data Store that is filtered via some query. Even if a filter has been applied, there is still a requirement needed to remove all records from that store. So, when I execute store.removeAll I expect all records to be removed from the data Store. But, this is not what happens. When a store is filtered, a call to removeAll simply removes all filtered records, not all records. So, to accomplish that task I am going to override the removeAll method for all the stores in my project.

Overriding methods: This process should work as if you are using the new version of the framework ExtJS 5. Sencha has designed a standard architecture to solve this kind of issue, placing an override folder in their project structure.

Ext.define('OverridesInExtJS.model.User', {
    extend: 'Ext.data.Model',

    fields:[
        {
            name : 'id',
            type : 'int'
        },
        {
            name : 'name'
        },
        {
            name : 'twitter'
        }
    ]
});

//Store definition.
Ext.define('OverridesInExtJS.store.Users', {
    extend   : 'Ext.data.Store',
    requires : [
        'OverridesInExtJS.model.User'
    ],

    model    :'OverridesInExtJS.model.User',
    autoLoad : true,

    data : [
        {id : 1, name : 'Patrick Sheridan', twitter : ' @sheridap'},
        //…
        {id : 30, name : 'Timothy Eagan', twitter : '@TimothyEagan'},
        {id : 31, name : 'Tyler Knappe', twitter : '@tknappe'}
    ]
});

// Grid definition:
Ext.define('OverridesInExtJS.view.users.UsersGrid', {
    extend : 'Ext.grid.Panel',
    xtype  : 'usersgrid',

    title   : 'Users',
    store   : 'Users',
    columns : [
        {
            text      : 'ID',
            dataIndex : 'id'
        }, 
        {
            header    : 'NAME',
            flex      : 3,
            dataIndex : 'name'
        }, 
        {
            header    : 'TWITTER',
            flex      : 2,
            dataIndex : 'twitter'
        }
    ],
    tbar : [
        '->',
        {
            text   : 'Filter',
            itemId : 'filter'
        },{
            text   : 'Clear Filter',
            itemId : 'clearFilter'
        },{
            text   : 'Remove All',
            itemId : 'removeAll'
        }
    ]
});

// Main controller definition:
Ext.define('OverridesInExtJS.controller.Main', {
    extend : 'Ext.app.Controller',
    refs   : [{
        ref      : 'usersGrid',
        selector : 'usersgrid'
    }],

    init : function() {
        var me = this;
        me.control({
            'usersgrid #filter': {
                click : me.onFilterGrid
            },
            'usersgrid #clearFilter': {
                click : me.onClearFilter
            },
            'usersgrid #removeAll': {
                click : me.onRemoveAllUsers
            }
        })
    },
    onFilterGrid : function(btn) {
        Ext.getStore('Users').filterBy(function(record) {
            return record.get('id') > 10;
        });
    },
    onClearFilter : function() {
        Ext.getStore('Users').clearFilter();
    },
    onRemoveAllUsers : function() {
        Ext.getStore('Users').removeAll();
    }
});

With the above code, at the top of the grid, we have 3 buttons: Filter, Clear Filter, and Remove All.

This is the store behavior:

  1. 1.First I filter my data by clicking the “Filter” button, which will then display all the records where id is greater than 10.
  2. 2.Then, I will remove all the records from the store by clicking the “Remove All” button.
  3. 3.After that, I will clear the store filter by clicking the “Clear Filter” button and I am left with only records where id is less than or equal to 10.

Notice the store.removeAll method of the store is not removing the unfiltered records. The complete code to this step is here.

Now I am going to make some changes to the removeAll method of my Ext.data.Store; For that I am going to use the Ext.override method. First, I need to see where the overrides go. If I navigate through my project I will find that Sencha Cmd has already created a folder where our overrides of the framework.

Now I will add the store override. Using the convention of how Sencha has its own class files, I need to create a data folder inside my overrides folder. It is in this folder that I will place our Store.js class file.

To override my store I just need to call the clearFilter method before the rest of the removeAll method logic happens.

/**
 * @class Ext.data.Store
 * @overrides Ext.data.Store
 * This is the code for overriding the store`s removeAll method
 */
Ext.define('Ext.data.Store', {
    override :'Ext.data.Store',

    removeAll : function () { 
        this.clearFilter(); //clear any filters than remove
        this.callParent(arguments);
    }
});

Now I need to include my overrides. For that I am going to add the path where the project can use the overrides to the beginning of my app.js file and require my overrides there.

/*
    This file is generated and updated by Sencha Cmd. You can edit this file as
    needed for your application, but these edits will have to be merged by
    Sencha Cmd when upgrading.
*/

Ext.Loader.setConfig({
    paths : {        
        'Overrides': 'overrides'
    }
});

Ext.application({
    name   : 'OverridesInExtJS',
    extend : 'OverridesInExtJS.Application',

    requires : ['Overrides.data.Store'],// importing our overrides

    autoCreateViewport : true
});

To ensure that my overrides are included in my testing and production builds, I’m going to need to modify a Sencha Cmd configuration file. Here’s my updated .sencha/app/sencha.cfg file:

# The path(s) to application javascript sources (comma separated)
app.classpath=${app.dir}/app,${app.dir}/app.js,${app.dir}/overrides

Iit is bad practice to edit the core code of any framework.

How can we manage overrides?

As your project grows there are more and more overrides to manage and deploy in production. Prior to the introduction of Ext.Loader it was popular to combine all overrides into a single file, say overrides.js that you would reference in the index.html right after ext.js. However, this approach had several drawbacks:

  1. It's hard to navigate between the overrides located in the single file.
  2. The <script> tag doesn't guarantee the order of scripts loading. So, you can face the problem when your application is loaded and started, and only after that your overrides will come into play.

A better approach is to explicitly load your overrides by Ext.Loader. This allows to keep them in the nice folder structure and to control the order of execution. I like my overrides to mirror the folder structure of the patched Ext JS sources. For instance, an override of Ext.app.Controller will be in the App.patch.app.Controller, while an override of Ext.data.Model will be in the App.patch.data.Model etc.

Let's continue with overriding the Controller. Here is the modified version:

Ext.define('App.patch.app.Controller', {
    override: 'Ext.app.Controller',

    init: function() {
        this.callOverridden();
        console.log(this.self.getName() + ' created');
    }
});

Assuming that App.controller.Main is my application specific controller, below is the application that pre-loads the patch. All I need to force the patch pre-load is requires: ['App.patch.app.Controller']. The patch will be loaded before application is instantiated, therefore before any controller, view, or store is created:

Ext.Loader.setConfig({ enabled: true });

Ext.application({
    requires: ['App.patch.app.Controller'],
    name: 'App',
    controllers: ['Main']
});

To manage multiple overrides, we may need to combine and version them. What do you do with multiple patches? Simply make the "root" patch that requires all others. Here is an example: App.path.ExtJSPatch:

Ext.define('App.patch.ExtJSPatch', {
    requires: [
        'App.patch.app.Controller'
        // ...Other overrides
    ]
});

Whether you are extending Ext JS or fixing the bugs, you should revise your overrides with each new Ext JS release. To reflect the version of Ext JS that your overrides are relevant for, I suggest embedding the name of the version into the name of the "root" patch and maintaining different ones, per Ext JS version:

Ext.application({
    requires: ['App.patch.ExtJS407Patch'],
...

What can we do if we are stuck in the Ext JS 2.2.1 land and have to override the initComponent method and having to call the original initComponent method?

(
    function(a) {
        var originalInitComponent = Ext.form.ComboBox.prototype.initComponent;
        Ext.override(Ext.form.ComboBox, {
            forceSelection: true,
            onBlur: function() {
            },
            initComponent: function() {
                //this.callParent(arguments)
                originalInitComponent.call(this);
            }
        });
    }
)();

References:

https://extjs.eu/writing-a-big-application-in-ext-part-2/ - skimmed through, important
https://ahlearns.wordpress.com/2013/06/20/ext-js-4-empty-value-in-a-combobox/ - skimmed through, important
https://stackoverflow.com/questions/9371705/how-to-re-empty-combobox-when-forceselection-is-set-to-true-extjs-4/23386168 - skimmed through, important
http://flexblog.faratasystems.com/index.php/ext-js-this-tricky-button-click-event/ - skimmed through, important
https://www.opsview.com/resources/engineering/blog/extjs-adding-click-events-components - skimmed through, important
https://stackoverflow.com/questions/24671165/extjs-override-specific-listeners - skimmed through, important
http://flexblog.faratasystems.com/index.php/private-methods-in-ext-js/ - skimmed through, important
https://stackoverflow.com/questions/18249715/steps-to-overriding-sencha-extjs-standard-component-functionality-ext-tree-pane - skimmed through, important
https://stackoverflow.com/questions/26712839/extjs5-overriding-native-method-defined-inside-ext-all-debug-js - skimmed through, important
https://moduscreate.com/blog/a-dive-into-the-sencha-class-config-system/ - skimmed through, important
http://www.ohmztech.com/guides/classconfigext4/ - skimmed through, important
https://stackoverflow.com/questions/5014227/how-to-override-a-default-config-option-on-ext-form-action-submit - skimmed through, important
http://jsuereth.com/ajax/extjs/2008/12/08/subclassing-in-extjs.html - skimmed through, important
https://stackoverflow.com/questions/3171946/extjs-extend-class-via-constructor-or-initcomponent - skimmed through, important
https://stackoverflow.com/questions/1721935/better-way-to-call-superclass-method-in-extjs/4038890#comment12216567_4038890 - skimmed through, important
https://groups.google.com/forum/#!topic/deftjs/kIwfWYY28mM - skimmed through, important
http://codegurukul.blogspot.com/2013/04/extjs-initcomponent-and-constructor.html - skimmed through, important
https://www.extjs-tutorial.com/extjs/config - skimmed through, important
https://level7systems.co.uk/extending-and-overriding-extjs5-components/ - skimmed through, important
http://training.figleaf.com/tutorials/senchacomplete/chapter3/lesson8/4.cfm - skimmed through, important
https://www.sencha.com/forum/archive/index.php/t-284001.html - skimmed through, important
https://stackoverflow.com/questions/20062131/extjs4-change-textfield-emptytext-on-the-fly - skimmed through, important

https://www.sencha.com/forum/showthread.php?236268-How-to-override-a-event - done reading, important
https://www.sencha.com/forum/showthread.php?292208-Correct-way-to-extend-a-class-and-override-config-properties - done reading, important
https://stackoverflow.com/questions/14254321/best-practice-for-overriding-classes-properties-in-extjs - done reading, important
http://antonmoiseev.com/2012/02/27/extjs-overrides/ - done reading, important
https://www.appfoundation.com/2012/04/resetting-the-selection-in-a-combobox-ext-js-4-x/ - important
https://thecliftonian.wordpress.com/2012/04/17/inheritance-in-extjs-4-overriding-methods-in-your-subclass/ - done reading, important
https://www.sencha.com/forum/showthread.php?173374-Ext-override()-on-Ext-components-in-ExtJS-4-x - done reading, important
https://moduscreate.com/blog/writing-ext-js-overrides/ - done reading, important
https://edspencer.net/2009/07/24/extoverride-monkey-patching-ext-js/ - done reading, important
https://www.sencha.com/forum/showthread.php?137612-Override-constructor-in-JsExt-4 - done reading, important
https://www.sencha.com/forum/showthread.php?330826-Extjs-6-2-Classic-dynamically-change-emptyText - done reading, important
https://fiddle.sencha.com/#fiddle/1m99&view/editor - done reading, important

https://www.sencha.com/forum/showthread.php?242797-Overriding-methods-in-extended-mixin-doesn-t-work - done reading, important, mixins
http://programminglist.blogspot.com/2013/08/extjs-unable-to-override-class.html - done reading, important, mixins

http://senchado.blogspot.com/2012/09/overriding-method-in-object-instance.html - done reading

https://stackoverflow.com/questions/42509619/mixin-overriding-in-extjs
https://stackoverflow.com/questions/15246430/extjs-4-1-override-mixins
https://extjsexamples.blogspot.com/2014/04/extjs4-overrides-mixins-modules.html
http://justinfagnani.com/2015/12/21/real-mixins-with-javascript-classes/
http://raganwald.com/2015/12/28/mixins-subclass-factories-and-method-advice.html

https://extjs.eu/writing-a-big-application-in-ext-part-2/
https://github.com/deftjs/DeftJS/issues/77

https://hackernoon.com/the-rise-and-fall-of-ext-js-c9d727131991

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License