Fieldset /w Dynamic Controls (7)

Implement MVC

In this example we’ll split up our code using ExtJS 4 MVC (Model View Controller) framework. De-coupling our models, views, and controllers makes the code very easy to modify later. This will come in handy when we have to add various input types later. Also, we will now be able to clone our fieldset with very little code:


…with this little code:

JS

...
// Create 2 fieldsets
Ext.create('Ext.container.Container',{
    renderTo: Ext.getBody()
    , defaults: { 
        xtype: 'mc_fieldsetdynamiccontrols'
        , margin: 10 
    }
    , items: [
        { title: 'Search Filter 1' }
        , { title: 'Search Filter 2' }
    ]
});
...

DEMO & SOURCE @ JSFIDDLE

JS – Model

I used to be lazy and avoided using models – I’d define them inside the store. Since then I’ve learned that splitting off a model always pays off!

...
// Represents an input on the fieldset
Ext.define('MyCompany.model.FieldsetDynamicControls', {
    extend: 'Ext.data.Model'
    , fields: ['name','label']
});
 
// Possible inputs
Ext.namespace('MyCompany.globals.FieldsetDynamicControls');
MyCompany.globals.FieldsetDynamicControls.possibleValues = [
    { label: 'Display Name', name: 'displayName' }
    , { label: 'First Name', name: 'givenName' }
    , { label: 'Middle Name', name: 'initials' }
    , { label: 'Last Name', name: 'sn' }
];
...

JS – View

Here we basically take our custom fieldset from previous examples, and wrap it in extension of the standard FieldSet; notice code where we implement custom events ‘addcontrol’ and ‘removecontrol’:

...
Ext.define('MyCompany.view.FieldsetDynamicControls', {
    extend: 'Ext.form.FieldSet'
    , alias: 'widget.mc_fieldsetdynamiccontrols' // xtype == mc_fieldsetdynamiccontrols
 
    , initComponent: function () {
        var me = this;
 
       me.addEvents('addcontrol','removecontrol');
 
        var config = {
            title: 'Search Filter'
            , width: 300
            , collapsible: true
            , iconCls: 'icon-properties'
            , style: 'background-color: #F1F1F1;'
                   + 'position: relative;' // support the [+] button
            , items:
            [
                {  // [Add] button with dropdown menu
                    xtype: 'button'
                    , style: 'position: absolute; top: ' +(Ext.isIE||Ext.isChrome?0:-20) + 'px; right: 20px;'
                    , iconCls: 'icon-add'
                    // drop-down menu
                    , menu: Ext.create('Ext.menu.Menu', {
                        plain: true
                        , items: [
                            { html: '<b>Attribute</b>' }
                            , {
                                xtype: 'combo' 
                                , queryMode: 'local'
                                , displayField: 'label'
                                , valueField: 'name'
                                , typeAhead: true
                                , allowBlank: false
                                , store: Ext.create('Ext.data.Store', {
                                    model:'MyCompany.model.FieldsetDynamicControls'
                                    , data:MyCompany.globals.FieldsetDynamicControls.possibleValues
                                    , sorters: [ { property: 'label' } ]
                                }) // eo store
                            } // eo combo
                        ] // eo items
                        , bbar: [ '->', { 
                            xtype: 'button'
                            , iconCls: 'icon-add'
                            , text: 'Add' 
                            , handler: function() {
                                // Fire custom event
                                me.fireEvent('addcontrol',me);
                            }
                        } ]
                    }) // eo Menu
                } // eo [Add] button with dropdown menu
                , {
                    xtype: 'textfield'
                    , fieldLabel: 'Test'
                    , msgTarget: 'under'
                    , allowBlank: false
                    , anchor: '-25'
                    , listeners : {
                        // Add "remove" button
                        afterrender: function(tf) {
                            var el = tf.bodyEl.createChild({tag: 'div', style:'position: absolute; right: -23px; top: 0px;'});
                            tf.innerButton = Ext.create('Ext.button.Button',{
                                renderTo: el
                                , iconCls: 'icon-remove'
                                , handler: function() {
                                    // Fire custom event
                                    me.fireEvent('removecontrol',me);
                                }
                            });
                        }
                        // Our button does not get destroyed when we
                        // destroy the parent textfield or the parent fieldset
                        , destroy: function(tf) { tf.innerButton.destroy(); }
                    } // eo listeners
                } // eo textfield
            ] // eo items
        }; // eo config object
 
        // apply config
        Ext.apply(me, Ext.applyIf(me.initialConfig, config));
        me.callParent(arguments);
    } // eo function initComponent
});
...

JS – Controller

In this kind of custom controls, you do not always want to implement a controller, for sake of not forcing the consumer to have to register it (see below in the On Ready section).

...
Ext.define("MyCompany.controller.FieldsetDynamicControls", {
   extend: "Ext.app.Controller"
 
   , init: function() {
        var controller = this;
        controller.control({
 
            'mc_fieldsetdynamiccontrols' : {
                addcontrol: function(fieldset) {
                    controller.addControl(fieldset);
                }
                , removecontrol: function(fieldset) {
                    controller.removeControl(fieldset);
                }
            }
 
        });
   }
 
    , addControl: function(fieldset) {
        alert("TODO: ADD to fieldset " + fieldset.title);
    }
 
    , removeControl: function(fieldset) {
        alert("TODO: REMOVE from fieldset" + fieldset.title);
    }
});
...

JS – On Ready

Finally; this sections got a lot smaller. Notice that we register the custom controller; without this the UI would still appear, but would not be functional. As mentioned earlier, you could get around having to register a controller by implementing the functionality entirely in the view.

...
Ext.onReady(function(){
    // Add support for fieldset icons
    implementFieldsetIcon();
 
    // Register controller
    Ext.application({name: 'MyCompany', controllers:['FieldsetDynamicControls']});
 
    // Create 2 fieldsets
    Ext.create('Ext.container.Container',{
        renderTo: Ext.getBody()
        , defaults: { 
            xtype: 'mc_fieldsetdynamiccontrols'
            , margin: 10 
        }
        , items: [
            { title: 'Search Filter 1' }
            , { title: 'Search Filter 2' }
        ]
    });
 
}); // eo onReady
...

Next -> Complete MVC Controller

VN:F [1.9.22_1171]
Rating: 5.8/10 (6 votes cast)
Fieldset /w Dynamic Controls (7), 5.8 out of 10 based on 6 ratings

One thought on “Fieldset /w Dynamic Controls (7)”

Leave a Reply

Your email address will not be published. Required fields are marked *

* Copy This Password *

* Type Or Paste Password Here *