1. app.js
App = new Ext.Application({
name:'App',
launch: function(){
/*
* here: App = this
* App.views.viewport = new App.views.Viewport();
*/
this.views.viewport = new App.views.Viewport();
//App.views.viewport = new this.views.Viewport();//worx too
//usersList and usersForm are component name which can be used by App.views['usersList'] or App.views['usersForm']
this.views.usersList = this.views.viewport.down('#usersList');
this.views.usersForm = this.views.viewport.down('#usersForm');
}
});
在app.js中创建2个变量名分别为usersList和usersForm,其namespace为App.views。 比较好的方法是在Viewpor.js中通过Ext.apply创建该两个views,并且加载到viewport的items中去(this.items),具体见(NotesAppMVC/MainView)。
2.Model: User.js
//use App.models.User or Ext.getModel('User') to get this model
App.models.User = Ext.regModel('User',{
fields:[{
name: 'id',
type: 'int'
},{
name: 'name',
type: 'string'
},{
name: 'email',
type: 'string'
},{
name: 'phone',
type: 'string'
}],
validations:[
{
type: 'presence',
name: 'name'
},{
type: 'format',
name: 'email',
matcher: /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/,
message: 'must be a valid email'
}
],
proxy: {
type: 'localstorage',
id: 'sencha-users'
}
});
App.models.User 是类名,通过 new App.models.User()来创建新的instance. 同时id:“User” 可以在使用ModelMgr中使用。
3. Store: Users.js
App.stores.users = new Ext.data.Store({
model: 'User',
autoLoad: true
});
//Uncaught TypeError: Object myStore has no method 'create'
//Uncaught TypeError: Object myStore has no method 'getAt'
// App.stores.users = Ext.regStore("myStore",{
// model: 'User',
// autoLoad: true
// });
这里App.stores.users是instance, 在List或者controller中的配置项store中,应该写instance名,而不是类名。
另外可以使用regStore为其分配一个id,但是这样就不能使用this.store.getAt(index) 以及 this.store.create(data)
4. Viewport.js
App.views.Viewport = Ext.extend(Ext.Panel,{
fullscreen: true,
layout:'card',
initComponent: function(){
Ext.apply(this,{
items:[{
xtype: 'App.views.UsersList', id: 'usersList'
},{
xtype: 'App.views.UsersForm', id: 'usersForm'
}]
});
App.views.Viewport.superclass.initComponent.apply(this, arguments);
},
reveal: function(target) {
var direction = (target === 'usersList') ? 'right' : 'left';
// console.log("target: " + target);
// console.log("direction: " + direction);
// console.log("App.views: " + App.views[target]);
//target "usersList" is a component name which is definded with this.views."usersList" = in app.js
this.setActiveItem(
App.views[target],
{ type: 'slide', direction:direction}
);
}
});
viewport.js 是规划一个总视图,并且加上2个子视图:UsersList,UsersForm,这里分配的两个id会覆盖类中定义的所用到的id。默认显示第一个items里的视图。
5. Form.js
5.1 结构规划
先构思一下form的结构,最上面是个titlebar,除了title外,最左面有个返回user List的按钮。最下面是个buttonbar, 最左边有个Delete,最右面有个Save。进一步设想一下,Delete按钮何时该出现(新记录不用delete,只有当修改旧记录时才出现),同样新记录时,save按钮不变,如果是修改旧记录,那么就显示update。最后title也要做相应改变。这里都是通过给component一个id,然后通过down/query来找到component,再进行相关配置项的修改。
App.views.UsersForm = Ext.extend(Ext.form.FormPanel, {
defaultInstructions: 'Please enter the information above',
initComponent: function(){
var titlebar, cancelButton, buttonbar, saveButton, deleteButton, fields;
cancelButton = {
text: 'cancel',
ui: 'back',
handler: this.onCancelAction,
scope: this
};
titlebar = {
id:'userFormTitlebar',
xtype: 'toolbar',
title: 'Create user',
items: [cancelButton]
};
saveButton = {
id: 'userFormSaveButton',
text: 'save',
ui: 'confirm',
handler: this.onSaveAction,
scope: this
};
//can't call deleteButton.hide()
deleteButton = {
id: 'userFormDeleteButton',
text: 'delete',
ui: 'decline',
handler: this.onDeleteAction,
scope: this
};
buttonbar = {
xtype: 'toolbar',
dock: 'bottom',
//deleteButton become a button in toolbar
items: [deleteButton, {xtype:'spacer'}, saveButton]
};
fields = {
xtype: 'fieldset',
id: 'userFormFieldset',
title: 'User details',
instructions: this.defaultInstructions,
defaults:{
xtype: 'textfield',
labelAlign: 'left',
labelWidth: '40%',
required: false,
useClearIcon: true,//Deprecated since 2.0, use clearIcon instead
autoCapitalize: false
},
items:[{
name: 'name',
label: 'name',
autoCapitalize: true//Chrome and Safari on desktop won't use this attribute, so no effect to show off
},{
xtype:'App.views.ErrorField',
fieldname: 'name'
},{
name: 'email',
label: 'email',
xtype: 'emailfield'
},{
xtype: 'App.views.ErrorField',
fieldname: 'email'
},{
name: 'phone',
label: 'phone',
xtype: 'numberfield'
},{
xtype: 'App.views.ErrorField',
fieldname: 'phone'
}]
}
Ext.apply(this, {
scroll: 'vertical',
dockedItems:[titlebar, buttonbar],
items:[fields],
listeners: {
//Fires before a Component has been visually activated.
beforeactivate: function(){
//get button component by using this.down(selector)
var deleteButton = this.down('#userFormDeleteButton'),
saveButton = this.down('#userFormSaveButton'),
titlebar = this.down('#userFormTitlebar'),
model = this.getRecord();
if(model.phantom) {
titlebar.setTitle('Create user');
saveButton.setText('create');
deleteButton.hide();
}else{
titlebar.setTitle('Update user');
saveButton.setText('update');
//this.deleteButton is the class variable, never defined
//deleteButton is a variable in initComponent defined.
//console.log(deleteButton.id);//worx
deleteButton.show();
}
},
//Fires after a Component has been visually deactivated.
deactivate: function(){
this.resetForm();
}
}
});
App.views.UsersForm.superclass.initComponent.call(this);
},
先把各个部件在initComponent中分别定义,然后添加到Ext.apply中。
5.2 函数定义
onCancelAction: function(){
Ext.dispatch({
controller: 'Users',
action: 'index'
});
},
onSaveAction: function(){
//console.log("object: " + this.id);
//this reference to usersForm because scope:this (by this save button)
//if scope not set, then this reference to the savebutton not formPanel
var model = this.getRecord();
Ext.dispatch({
controller: 'Users',
action: (model).phantom ? 'save' : 'update',
data: this.getValues(),
record: model, //Ext.FormPanel.getRecord() returns the Model instance currently loaded into this form(if any)
form: this
})
},
onDeleteAction: function(){
Ext.Msg.confirm("Delete this user?", "", function(answer){
if(answer === 'yes') {
Ext.dispatch({
controller: 'Users',
action: 'remove',
record: this.getRecord()
});
}
},this);
},
showErrors: function(errors){
var fieldset = this.down('#userFormFieldset');
//console.log("this.fields: " + this.fields.items[0].name);//worx
//this.fields has 3 items and each item is afield
this.fields.each(function(field){
//getByField is funk of Ext.data.Errors, return all errors(Array) for the given field
var fieldErrors = errors.getByField(field.name);
if(fieldErrors.length > 0){
var errorField = this.down('#'+field.name+'ErrorField');
field.addCls('invalid-field');
//Update the content area of a component(errorField). e.g. it shows both in errorfield
//var d = new Array({field:'dd',message:'dds'},{field:'dds',message:'ddss'});
//errorField.update(d);
errorField.update(fieldErrors);//but here only show message of Array fieldErrors
//console.log("fieldErrors: " + fieldErrors[0].field);//worx
//console.log("fieldErrors: " + fieldErrors[0].message);//worx
errorField.show();
}else{
this.resetField(field);
}
}, this);
fieldset.setInstructions('Please amend the flagged fields');
},
resetField: function(field){
var errorField = this.down('#'+field.name+'ErrorField');
errorField.hide();
field.removeCls('invalid-field');
return errorField;//need this?
},
resetForm: function(){
//reset all error fields
var fieldset = this.down('#userFormFieldset');
this.fields.each(function(field){
this.resetField(field);
},this);
fieldset.setInstructions(this.defaultInstructions);
//reset all normal fields
this.reset();
}
});
Ext.reg('App.views.UsersForm', App.views.UsersForm);
6. List.js
App.views.UsersList = Ext.extend(Ext.Panel, {
initComponent: function(){
var addButton, titlebar, list;
addButton = {
itemId: 'addButton',
iconCls: 'add',
iconMask: true,
ui: 'plain',
handler: this.onAddAction,
scope: this
};
titlebar = {
dock: 'top',
xtype: 'toolbar',
title: 'Users',
items: [{
xtype: 'spacer'
},
addButton
]
};
list = {
xtype: 'list',
itemTpl: '{name}',
store: App.stores.users,
//store: 'myStore',
listeners:{
scope:this,
itemtap:this.onItemtapAction
}
};
Ext.apply(this, {
html: 'placeholder',
layout: 'fit',
dockedItems: [titlebar],
items: [list]
});
App.views.UsersList.superclass.initComponent.call(this);
},
onAddAction: function(){
Ext.dispatch({
controller: 'Users',
action: 'newForm'
});
},
onItemtapAction: function(obj, index, item, e){
Ext.dispatch({
controller: 'Users',
action: 'editForm',
index: index
})
}
});
Ext.reg('App.views.UsersList', App.views.UsersList);
7. ErrorField.js
App.views.ErrorField = Ext.extend(Ext.Component, {
initComponent: function() {
config = {
xtype: 'component',
id: this.fieldname + 'ErrorField',
cls: 'errorfield',
tpl: [
'<tpl if="values.length > 0">',
' <ul>',
' <tpl for=".">',
' <li>{message}</li>',
' </tpl>',
' </ul>',
'</tpl>'
],
hidden: true
};
Ext.apply(this, config);
App.views.ErrorField.superclass.initComponent.call(this);
}
});
Ext.reg('App.views.ErrorField', App.views.ErrorField);
8. Controller
Ext.regController('Users',{
store: App.stores.users,
//store: 'myStore',
//this.application.views.viewport worx only if this app is created by Ext.regAppliction
index: function(){
App.views.viewport.reveal('usersList');
},
newForm: function(){
var model = new App.models.User();
App.views.usersForm.load(model);
App.views.viewport.reveal('usersForm');
},
editForm: function(params){
var model = this.store.getAt(params.index);
App.views.usersForm.load(model);
App.views.viewport.reveal('usersForm');
},
save: function(params){
//console.log("before: " + params.record.data.name);
//make sure before calling model.validate(), new data has been set into model.
params.record.set(params.data);
//console.log("after: " + params.record.data.name);
var errors = params.record.validate();
if(errors.isValid()){
//sava data in the store, this.store.create can only be used when App.stores.users = new Ext.data.Store({})
//not App.stores.users = Ext.regStore()
this.store.create(params.data);
//this.store.create(params.record.data);//worx too
this.index();
}else{
//errors is mixed Object with items which includes each error field
params.form.showErrors(errors);
}
},
update: function(params){
var tmpUser = new App.models.User(params.data),
errors = tmpUser.validate();
if(errors.isValid())
{
//bug here, if refresh page, show old record too
//params.record.set(params.data);
//params.record.save();
params.form.updateRecord(params.model);
this.store.sync();
this.index();
}else{
params.form.showErrors(errors);
}
},
remove: function(params){
this.store.remove(params.record);
this.store.sync();
this.index();
}
});