ExtJS Complex data binding

咱們先看一下效果圖api

image

視圖方面有一個container,包含了4個組件,一個grid(Editable Grid),一個form(Form),一個view(DataView),一個panel(DataPanel)app

四個組件之間經過一個controller來根據各個組件的事件進行數據的同步顯示。ide

數據存在於store,store又用到一個model。函數

app/view/MainView.js ----- containerflex

app/view/PersonDataView.js ------- DataViewui

app/view/PersonGridView.js --------- Editable Gridthis

app/view/PersonFormView.js -------- Formidea

app/view/PersonDataPanel.js -------- Data Panelspa

app/controller/MainController.js ------- controllercode

app/store/PersonStore.js --------- Store

app/model/PersonModel.js ---------- Model

app/Application.js -------- Application

app.js --------------- main entry

1. app.js

/*
 * 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.scopeCss = true;
Ext.setGlyphFontFamily('FontAwesome');
Ext.application({
    name: 'hello',

    extend: 'hello.Application'
    
//    autoCreateViewport: 'hello.view.MainView'
    
    //-------------------------------------------------------------------------
    // Most customizations should be made to hello.Application. If you need to
    // customize this file, doing so below this section reduces the likelihood
    // of merge conflicts when upgrading to new versions of Sencha Cmd.
    //-------------------------------------------------------------------------
});

2. Application.js

/**
 * The main application class. An instance of this class is created by app.js when it calls
 * Ext.application(). This is the ideal place to handle application launch and initialization
 * details.
 */

Ext.define('hello.Application', {
    extend: 'Ext.app.Application',

    name: 'hello',

    controllers:[
        'MainController'
    ],
    
    launch: function () {
        // TODO - Launch the application
        var me = this,
            ct = Ext.getBody();
        Ext.widget({
            xtype: 'mainview',
            renderTo:ct,
            height:480,
            width:720,
            frame:true,
            title:'Complex Data Binding Example by Saki',
            glyph:0xf0eb
        });
    }
});

launch函數建立了一個widget, xtype爲mainview. 該mainview對應MainView.js的alias.

3. MainView.js

Ext.define('hello.view.MainView', {
    extend: 'Ext.panel.Panel',
    alias: 'widget.mainview',
    initComponent: function () {

        var me = this,
            cfg = {};
        Ext.apply(cfg, {
            layout: {
                type: 'hbox',
                align: 'stretch'
            },
            defaults: {
                flex: 1
            },
            items: [{
                xtype: 'container',
                layout: {
                    type: 'vbox',
                    align: 'stretch'
                },
                defaults: {
                    flex: 1,
                    margin: 5
                },
                items: [{
                    title: 'Editable Grid',
                    xtype: 'persongridview',
                    glyph: 0xf0ce
                }, {
                    title: 'DataView',
                    glyph: 0xf009,
                    layout: 'fit',
                    items:[{
                        xtype: 'persondataview'
                    }]
                }]
            },{
                xtype: 'container',
                layout: {
                    type: 'vbox',
                    align: 'stretch'
                },
                defaults: {
                    flex: 1,
                    margin: 5
                },
                items: [{
                    title: 'Form',
                    xtype: 'personformview',
                    glyph: 0xf044,
                    frame: true
                }, {
                    title: 'Data Panel',
                    xtype: 'personpanelview',
                    glyph: 0xf0f6
                }]
            }]

        });
        Ext.apply(me, cfg);
        me.callParent(arguments);

    }
});

MainView總共包含了4個視圖組件,每一個組件經過xtype來指定,分別爲4個組件的alias.

感受每一個組件的別名必須以widget.來打頭,否則的話會有問題,可能這是Extjs的一種機制吧。

4. MainController.js

Ext.define('hello.controller.MainController', {
    extend:'Ext.app.Controller',
    views:[
        'MainView',
        'PersonGridView',
        'PersonFormView',
        'PersonPanelView',
        'PersonDataView'
    ],
    stores:[
        'PersonStore'
    ],
    refs:[{
        ref:'data',
        selector:'persondataview'
    },{
        ref:'form',
        selector:'personformview'
    },{
        ref:'panel',
        selector:'personpanelview'
    },{
        ref:'grid',
        selector:'persongridview'
    }],
    init:function() {

        var me = this;
        me.listen({
            component: {
                persongridview: {
                    rowselectionchange: 'onRowSelectionChange',
                    edit: 'onGridEdit'
                },
                persondataview: {
                    itemselectionchange: 'onItemSelectionChange'
                },
                'personformview button': {
                    click: 'onFormButtonClick'
                },
                'personformview field': {
                    change: 'onFormFieldChange'
                }
            }
        });


    },
    onGridEdit:function(editor,e) {
        var me = this;
        me.getForm().loadRecord(e.record);
        me.getPanel().loadRecord(e.record);
    },
    onFormFieldChange:function(field) {
        var me =this,
            form = me.getForm(),
            record = form.getRecord() || false;
        if(record){
            form.updateRecord();
            me.getPanel().loadRecord(record);
        }
        form.updateUi();

    },
    onFormButtonClick:function(btn){
        this.getForm()[btn.itemId +'Record']();
    },
    onRowSelectionChange:function(grid, selected) {
        alert("onRowSelectionChange");
        var me = this,
            record = selected[0];// || false;
        var sm = me.getData().getSelectionModel();
        me.getForm().loadRecord(record);
        alert("getForm ok");
        me.getPanel().loadRecord(record);
        if(record) {
            sm.select(record);//[record]);
        } else {
            sm.deselectAll();
        }
    },
    onItemSelectionChange:function(view, selected) {
        var me = this,
            record = selected[0] || false;
        me.getForm().loadRecord(record);
        me.getPanel().loadRecord(record);
        me.getGrid().getSelectionModel().select(selected);
    }
});

MainController裏面定義了views,stores,到底有什麼用呢?

看一下官方註釋就明白了。

image

MainController裏面定義了ref,到底有什麼用呢?

經過上面的ref定義,在MainController中就能夠使用getData(),getForm(),getGrid(),getPanel()函數,selector就對應的每一個視圖的alias.

image

5. PersonStore.js

Ext.define('hello.store.PersonStore', {
    extend: 'Ext.data.Store',
    model: 'hello.model.PersonModel',
    autoLoad:true,

    data: [
        { id:1,"fname": "Lisa", "lname": "lisa@simpsons.com", "age": "10" },
        { id:2,"fname": "Bart", "lname": "bart@simpsons.com", "age": "12" },
        { id:3,"fname": "Homer", "lname": "homer@simpsons.com", "age": "13" },
        { id:4,"fname": "Tangke", "lname": "marge@simpsons.com", "age": "15" },
    ]
});

6. PersonModel.js

Ext.define('hello.model.PersonModel', {
    extend: 'Ext.data.Model',
    idProperty:'id',
//    alias:'User',
    fields: [
        { name: 'id', type: 'int' },
        { name: 'fname', type: 'string' },
        { name: 'lname', type: 'string' },
        { name: 'age', type: 'int' }
    ]


});

7. PersonGridView.js

Ext.define('hello.view.PersonGridView', {
    extend:'Ext.grid.Panel',
    alias:'widget.persongridview',
    uses:[
        'Ext.grid.plugin.CellEditing'
    ],
    initComponent:function() {

        var me = this,
            cfg={};
        Ext.apply(cfg, {
            store:Ext.getStore('PersonStore'),
            columns:[{
                text:'First Name',
                dataIndex:'fname',
                editor:{
                    xtype:'textfield'
                }
            },{
                text:'Last Name',
                flex:1,
                dataIndex:'lname',
                editor:{
                    xtype:'textfield'
                }
            },{
                text:'Age',
                dataIndex:'age',
                editor:{
                    xtype:'numberfield'
                }
            }],
            listeners:{
                selectionchange:'onSelectionChange'
            },
            setModel:{
                allowDeselect:true
            },
            plugins:[{
                ptype:'cellediting',
                clicksToEdit:2,
                pluginId:'cellediting'
            }],
            tbar:{
                xtype:'toolbar',
                items:['->',{
                    text:'Add Record',
                    itemId:'addRecord',
                    glyph:0xf067,
                    handler:me.onAddRecord,
                    scope:me

                }]
            }
        });
        Ext.apply(me,cfg);
        me.callParent(arguments);

    },
    onAddRecord:function() {
        var me = this,
            store = me.getStore(),
            record = store.add({})[0];
        me.getPlugin('cellediting').startEdit(record, 0);
    },
    onSelectionChange:function(selModel, selected, eOpts) {
        this.fireEvent('rowselectionchange', this, selected, eOpts);
    }
});
store:Ext.getStore('PersonStore')用於獲取數據。
有些地方用的代碼是Ext.data.StoreManager.lookup('PersonStore'),效果是同樣的。爲何呢?具體看官方註釋:
 
image

8. PersonFormView.js

Ext.define('hello.view.PersonFormView', {
    extend:'Ext.form.Panel',
    alias:'widget.personformview',
    uses:[
        'Ext.form.field.Number'
    ],
    frame:true,
    initComponent:function() {

        var me = this,
            cfg={};
        Ext.apply(cfg, {
            defaultType:'textfield',
            defaults: {
                anchor: '100%'
            },
            bodyPadding:10,
            items:[{
                fieldLabel:'First Name',
                name:'fname'
            },{
                fieldLabel:'Last Name',
                name:'lname'
            },{
                fieldLabel:'Age',
                name:'age',
                xtype:'numberfield'
            }],
            buttons:[{
                text:'Rejext',
                itemId:'reject',
                disabled:true,
                glyph:0xf0e2
            },{
                text:'Commit',
                itemId:'commit',
                glyph:0xf00c,
                disabled:true
            }]
        });
        Ext.apply(me,cfg);
        me.callParent(arguments);

    },
    loadRecord:function(record) {
        var me = this;
        if(record) {
            me.callParent([record]);
        } else {
            me.clearValues();
        }
    },
    clearValues:function() {
        var me = this;
        me.getForm()._record = null;
        me.getForm().setValues({
            fname:'',
            lname:'',
            age:undefined

        });
        me.uploadUi();
    },
    commitRecord:function() {
        var me = this,
            record = me.getRecord();
        if(record) {
            me.updateRecord();
            record.commit();
            me.updateUi();
        }
    },
    rejectRecord:function() {
        var me = this,
            record = me.getRecord();
        if(record) {
            record.rejectRecord();
            me.loadRecord(record);
            me.updateUi();
        }
    },
    updateUi:function() {
        var me =this,
            record =me.getRecord(),
            disabled=record && record.dirty?false:true;
        Ext.each(me.query('button'), function(btn) {
            btn.setDisabled(disabled);
        })
    }
});

9. PersonDataView.js

Ext.define('hello.view.PersonDataView', {
    extend:'Ext.view.View',
    alias:'widget.persondataview',
    autoscroll:true,
    frame:true,
    initComponent:function() {

        var me = this,
            cfg={};
        Ext.apply(cfg, {
            store:Ext.getStore('PersonStore'),
            itemSelector:'div.person-item',
            tpl:[
                '<tpl for=".">',
                '<div class="person-item">',
                '<strong>{fname}.{lname}</strong>{{age}}',
                '</div>',
                '</tpl>'
            ],
            listeners: {
                selectionchange: 'onSelectionChange'
            },
            selModel:{
                allowDeselect:true
            }
        });
        Ext.apply(me,cfg);
        me.callParent(arguments);

    },
    onSelectionChange:function(selModel, selected, eOpts) {
        this.fireEvent('itemselectionchange', this, selected,eOpts);
    }
});

10. PersonPanelView.js 

Ext.define('hello.view.PersonPanelView', {
    extend:'Ext.panel.Panel',
    alias:'widget.personpanelview',
    cls:'person-panel',
    data:{},
    bodyPadding:0,
    frame:true,
    tpl:[
        '<table>',
        '<tr><td>First Name:</td><td><strong>{fname}</strong></td></tr>',
        '<tr><td>Last Name:</td><td><strong>{lname}</strong></td></tr>',
        '</table>'
    ],
    loadRecord:function(record) {
        alert("loadRecord");
        var me = this;
        if(record) {
            me.update(record.getData());
        } else {
            me.update({});
        }
    }

});

11. 疑問?

11.1 xtype和alias的對應關係

看了一個Extjs內部的一些組件,確實也是這樣。

Ext.define('Ext.form.field.Text', {
    extend: 'Ext.form.field.Base',
    alias: 'widget.textfield',
    requires: [
        'Ext.form.field.VTypes',
        'Ext.form.trigger.Trigger',
        'Ext.util.TextMetrics'
    ],
    alternateClassName: [
        'Ext.form.TextField',
        'Ext.form.Text'
    ],
    config: {
        
        hideTrigger: false,
        
        
        triggers: undefined
    },

 

11.2 loadRecord的用法

在PersonFormView中有一個loadRecord的函數,其重寫了父類的loadRecord函數。

image

在Ext.form.Panel中,

image

image

在PersonPanelView中有也有一個loadRecord函數,可是其父類Ext.panel.Panel並無loadRecord函數。

image

二者傳入的參數都是record參數,record是一個model模型。

image

{fname},{lname} 分別對應了record model中的fields.

11.3 getStore

onAddRecord:function() {
        var me = this,
            store = me.getStore(),
            record = store.add({some:'id'})[0];
        me.getPlugin('cellediting').startEdit(record, 0);
    },

 

這裏還不是太明白,若是添加一個默認數據。

11.4 store:

在PersonDataView和PersonGridView裏面分別使用了store配置。其分別派生自Ext.view.View和Ext.grid.Panel.

Ext.grid.Panel的官方註釋:

store : Ext.data.StoreREQUIRED

The Store the grid should use as its data source.

Ext.view.View的官方註釋:

store : Ext.data.StoreREQUIRED

The Ext.data.Store to bind this DataView to.

Available since: 2.3.0

相關文章
相關標籤/搜索