我们先看一下效果图
视图方面有一个container,包含了4个组件,一个grid(Editable Grid),一个form(Form),一个view(DataView),一个panel(DataPanel)
四个组件之间通过一个controller来根据各个组件的事件进行数据的同步显示。
数据存在于store,store又用到一个model。
app/view/MainView.js ----- container
app/view/PersonDataView.js ------- DataView
app/view/PersonGridView.js --------- Editable Grid
app/view/PersonFormView.js -------- Form
app/view/PersonDataPanel.js -------- Data Panel
app/controller/MainController.js ------- controller
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,到底有什么用呢?
看一下官方注释就明白了。
MainController里面定义了ref,到底有什么用呢?
通过上面的ref定义,在MainController中就可以使用getData(),getForm(),getGrid(),getPanel()函数,selector就对应的每个视图的alias.
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'),效果是一样的。为什么呢?具体看官方注释:
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函数。
在Ext.form.Panel中,
在PersonPanelView中有也有一个loadRecord函数,但是其父类Ext.panel.Panel并没有loadRecord函数。
两者传入的参数都是record参数,record是一个model模型。
{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









浙公网安备 33010602011771号