Skip to content

Modus-Logo-Long-BlackCreated with Sketch.

  • Services
  • Work
  • Blog
  • Resources

    OUR RESOURCES

    Innovation Podcast

    Explore transformative innovation with industry leaders.

    Guides & Playbooks

    Implement leading digital innovation with our strategic guides.

    Practical guide to building an effective AI strategy
  • Who we are

    Our story

    Learn about our values, vision, and commitment to client success.

    Open Source

    Discover how we contribute to and benefit from the global open source ecosystem.

    Careers

    Join our dynamic team and shape the future of digital transformation.

    How we built our unique culture
  • Let's talk
  • EN
  • FR

With ExtJS5, Sencha added to the framework a new way of building web apps based on the MVVM pattern. A key role in this pattern is the ViewModel which coordinates the changes between Model data and the View declaratively through data binding. I like the ViewModel concept because it requires less writing, has declarative code, and Views are much cleaner. Based on my experiences, following these tips and thoughts should be very useful for both newcomers and devs familiar with the framework.

  1. Always destroy your bindings in the destroy method of your view. In the example below, the grid is a child of a parent view which has a ViewModel. But if the child is removed from the parent the binding will still be called and can lead to unexpected surprises. So you could destroy them manually, or automatically by adding a ViewModel to the view
    Ext.define('MyApp.view.MyGridTab', {
        extend: 'Ext.grid.Panel',
        xtype: 'mygrid',
        columns: [],
        beforeRender: function () {
            var me = this;
            var vm = this.lookupViewModel();
    
            me.callParent(arguments);
            me.myFieldBinding = vm.bind('{obj.myfield}', me.onMyFieldBindingChange,me);
        },
        onMyFieldBindingChange: function(myfield) {
          // Do some stuff with myfield build the search query, then load the store
          var searchRequest = this.buildSearchRequest(myfield);
          this.searchByRequest(searchRequest)
        },
        searchByRequest: function (searchRequest) {
            var me = this;
            var store = this.getStore();
    
            if (searchRequest) {
                store.getProxy().setExtraParam('search_request', searchRequest);
                store.load();
            }
        },
        destroyViewBindings: function () {
            if (this.myFieldBinding) {
                this.myFieldBinding.destroy();
                this.myFieldBinding = null;
            }
        },
        destroy: function () {
            this.destroyViewBindings();
            this.callParent();
        }
    });
  2. If possible avoid creating bindings in initComponent. The component may not be rendered but instantiated, like in tabpanels’ case, and the binding is called every time a change is detected. This occurs even if it is not needed, which decreases app performance. Depending on the context, you could use the solution below or create your bindings in beforerender.
  3. Only activate bindings when needed. In the above example, the store of the grid tab should load the data only when the tab is visible and/or activated. Currently the ViewModel can’t be disabled and enabled as needed. By default, it is initialized to run in the beforerender phase until destroy time. But like above, sometimes we need to optimize this so the performance is not affected by the bindings. So you may find it useful to have a more generic and flexible solution like the one below. The only way to disable bindings running for now, is to remove them from the ViewModel -> Scheduler by destroying them, which costs less than destroying and creating the ViewModel. For a demo check this fiddle https://fiddle.sencha.com/#fiddle/1bcf and https://github.com/vadimpopa/Ux.mixin.CustomBindableSencha ViewModels bindings
  4. Use explicit binding in formulas to speed up formula parsing
    Ext.define('MyApp.view.MyModel', {
        extend: 'Ext.app.ViewModel',
    
        formulas: {
            // Ok
            isPdfUseDisabled: function (get) {
                var country = get('patentData.country');
    
                if (country) {
                    return !MyApp.util.isPdfCollection(country);
                }
    
                return false;
            }, 
    
            // Much better
            isPdfUseDisabled: {
                bind: '{patentData.country}',
                get: function (country) {
                    if (country) {
                        return !MyApp.util.isPdfCollection(country);
                    }
    
                    return false;
                }
            },
    
            // or multiple binding
            something: {
                bind: {
                    x: '{foo.bar.x}',
                    y: '{bar.foo.thing.zip.y}'
                },
    
                get: function (data) {
                    return data.x + data.y;
                }
            }
        }
    });
  5. Careful with stores data binding (with a memory proxy) because of the nature of store.setData, the grid is not cleared if the bound data is changed to undefined or null
    Ext.define('MyApp.view.MyModel', {
        extend: 'Ext.app.ViewModel',
        formulas: {
            myCollectionsData: {
                bind: '{myData.anObject.myCollections}',
                get: function (myCollections) {
                    return myCollections || [];
                }
            }
        },
        store: {
            myCollections: {
                autoDestroy: true,
                fields: [],
                proxy: {
                    type: 'memory',
                    reader: {
                        type: 'json'
                    }
                },
                // This bind is going to leave your grid un-cleared because of the how
                // store.setData is coded.
                data: '{myData.anObject.myCollections}'
    
                // Much better, if to use a formula to accept default data
                data: '{myCollectionsData}'
            }
        }
    });
  6. As a naming rule, use the onBindingChange pattern when naming your bound handlers. Same goes for the bind references, see myFieldBinding
    me.myFieldBinding = vm.bind('{obj.myfield}', 
    me.onMyFieldBindingChange,me);
  7. Don’t overload your ViewModels with too much code or fragment the code. The balance should be between cleaning up and parent polluting. ViewModels can inherit from each other in a parent – child relation, so you can still access the parent’s data in the child. Also, overloading means more dependencies to track.
    Ext.define('MyApp.view.MyChildModel', {
        extend: 'Ext.app.ViewModel',
    
        alias: 'viewmodel.mychild',
    
        formulas: {
            myCollectionsData: {
                bind: '{myData.anObject.myCollections}',
                get: function (myCollections) {
                    return myCollections || [];
                }
            }
        }
    });
    
    Ext.define('MyApp.view.MyParentModel', {
        extend: 'Ext.app.ViewModel',
        alias: 'viewmodel.myparent',
        data: {
            myData: null
        }
    });
    
    Ext.define('MyApp.view.MyWindow', {
        extend: 'Ext.window.Window',
    
        viewModel: {
            type: 'myparent'
        },
    
        items: [{
            xtype: 'mygrid'
        }]
    });
    
    Ext.define('MyApp.view.MyGrid', {
        extend: 'Ext.grid.Panel',
        xtype: 'mygrid'
        viewModel: {
            type: 'mychild'
        }
    });
  8. Don’t repeat yourself. Sometimes you have to call one property a few times, like with dude in below example. Creating a formula to shorten bindings path won’t add much benefit. Instead it will create additional bindings for the formula itself which means additional overhead for nothing. Here’s a fiddle to experiment with this https://fiddle.sencha.com/#fiddle/1bmk
    viewModel: {
        data: {
            simpsons: {
                dude: {
                    name: 'jon simpson',
                    role: 'developer',
                    phone: '3434343'
                }
            }
        },
    
        formulas: {
            dude: function(get) {
                return get('simpsons.dude');
            } 
        }
    },
    ........
    
        items: [
            {
                xtype: 'component',
                bind: {
                    html: 'hey {dude.name}'
                }
            },
            {
                xtype: 'component',
                bind: {
                    html: 'is your phone {dude.phone} ?'
                }                
            }
        ]
  9. A quick and easy custom Ext.form.field.Display could be done by using a Bind and a Renderer
        xtype: 'displayfield',
        fieldLabel: 'Custom display field',
        bind: {
            value: '{fooArray}'
        },
        customFieldTpl: new Ext.XTemplate(
            '',
                '<span class="search-field-link" data-value="{xindex}">{name}</span>',
            ''
        ),
        renderer: function (value, field) {
            var html = field.customFieldTpl.apply(value);
            return field.htmlEncode ? Ext.util.Format.htmlEncode(html) : html;
        }

Conclusion

ViewModels are very powerful but you have to be careful and keep the beast under control so you don’t lose performance. Keep in mind that the more bindings you have, the longer it will take to run, so use them wisely. Follow me on GitHub and gist for more tips and extensions.

Posted in Application Development
Share this

Vadim Popa

Vadim Popa is a web apps engineer and a JavaScript coder. He likes travelling and mountain biking through the Romanian woods.

Related Posts

  • Sencha Ext.Config Features Ext JS 6
    How To Use Sencha Ext.Config

    In a previous post, my colleague Stan explained the foundation of the Sencha Config System…

  • Sencha Ext.Config Features Ext JS 6
    How To Use Sencha Ext.Config

    In a previous post, my colleague Stan explained the foundation of the Sencha Config System…

Want more insights to fuel your innovation efforts?

Sign up to receive our monthly newsletter and exclusive content about digital transformation and product development.

What we do

Our services
AI and data
Product development
Design and UX
IT modernization
Platform and MLOps
Developer experience
Security

Our partners
Atlassian
AWS
GitHub
Other partners

Who we are

Our story
Careers
Open source

Our work

Our case studies

Our resources

Blog
Innovation podcast
Guides & playbooks

Connect with us

Get monthly insights on AI adoption

© 2025 Modus Create, LLC

Privacy PolicySitemap
Scroll To Top
  • Services
  • Work
  • Blog
  • Resources
    • Innovation Podcast
    • Guides & Playbooks
  • Who we are
    • Our story
    • Careers Old
  • Let’s talk
  • EN
  • FR