Ext JS MVC: Controlling multiple View instances

July 2, 2013

Oftentimes we need to control multiple instances of the same View class, and in majority of such cases we can share the same Controller instance between all of these Views. The key here is to make use of `xtype` and properly configured component selectors.

Suppose that we have a form; in that form we need to invalidate secondary field when primary field value is invalid, and vice versa:

Ext.define('MyApp.view.MyForm', {
    extend: 'Ext.form.Panel',
    alias: 'widget.myform',

    defaultType: 'textfield',

    items: [{
        fieldLabel: 'Primary',
        name: 'primary'
    }, {
        fieldLabel: 'Secondary',
        name: 'secondary'
    }]
});

Instead of hardcoding the logic in the Form class itself, we can abstract it into a Controller:

Ext.define('MyApp.controller.MyForm', {
    extend: 'Ext.app.Controller',

    init: function() {
        this.control({
            /*
             * Comma in a ComponentQuery selector means that component
             * matches if either of subselectors is valid; i.e. it is
             * effectively an || operator. Only fields with names
             * 'primary' or 'secondary' that are children of a form
             * with xtype 'myform' will be watched for events,
             * and so we do not have to check the field validity in the
             * event handler even if we add more fields to our form.
             */
            'myform textfield[name=primary], myform textfield[name=secondary]': {
                validitychange: 'onValidityChange'
            }
        });
    },

    onValidityChange: function(field, isValid) {
        var map = {
                primary: 'secondary',
                secondary: 'primary'
            },
            selector, linkedField;

        /*
         * First we need to find the field that is linked
         * to the one on which validitychange event was fired.
         * We will do that by quering field`s parent form
         * for a field that has specified name.
         */
        selector    = 'textfield[name=' + map[field.name] + ']';
        linkedField = field.up('myform').down(selector);

        if (isValid) {
            linkedField.clearInvalid();
        }
        else {
            linkedField.markInvalid('Field ' + field.fieldLabel +
                                    ' should be valid');
        }
    }
});

Note that we are looking up field’s form by its `xtype`; this way it is more future-proof. For example, the fields may be moved into a different FieldSets; if we used `field.up()` it would not return the form object anymore and the construct would break. Looking up the form itself hedges us against the changes and ensures that we find exactly the field we need because the form has distinct `xtype`.

The only caveat (which is actually a feature) to watch for with `xtype` is that by default it matches a component of a class that has this `xtype`, and components of classes that extend it. E.g., if we were to make our MyForm a base class the Controller code above would also work:

Ext.define('MyApp.view.MyChildForm', {
    extend: 'MyApp.view.MyForm',
    alias: 'widget.mychildform',

    ...
});

In case you want your selectors to match only components of the classes that actually have `xtype` needed, use `(true)` suffix to make the search shallow:

'myform(true) textfield[name=primary], myform(true) textfield[name=secondary]'

See Ext.ComponentQuery documentation for more information on component selectors.

tags: , , ,
posted in Software development by nohuhu

Follow comments via the RSS Feed | Leave a comment | Trackback URL

Leave Your Comment

 
Powered by Wordpress and MySQL. Theme by Shlomi Noach, openark.org