Extjs 5.0之后引入的MVVM模式。MVVC模式是在MVC模式之上加了一个ViewModel层。
- model:Model is a collection of fields and their data (e.g. a User model with username and password fields). Models know how to persist themselves through the data package, and can be linked to other models through associations. Models work a lot like the Ext JS 3 Record class, and are normally used with Stores to present data into grids and other components. model是数据和变量的集合(user模式自带用户名和密码这两个变量)。model知道如何通过数据包维护他们,并且可以通过associations关联到其他的model。models和record的工作方式很想,通常使用stores来将数据呈现到grid或其他组件里面。
- view:View is any type of component - grids, trees and panels are all views.view是任何类型的组建-grids,trees,panels都是view
- controller:Controllers are special places to put all of the code that makes your app work - whether that's rendering views, instantiating Models, or any other app logic。controller是一个用于放置应用逻辑的地方,包括views的,models或其他的业务逻辑。
- viewModel:The ViewModel is an abstraction of the view that mediates changes between the View and an associatedModel. In the MVC pattern, this would have been the responsibility of a specialized Controller, but in MVVM, the ViewModel directly manages the data bindings and formulas used by the View in question.viewmodel是view的一个抽象,它展示了view和关联model之间的变化。在mvc模式中,这种变化由一个特殊的controller负责,但是在mvvc中,viewmodel直接管理view使用的数据binding和formulas.
- 结构如图:
需要注意的是并不是所有的view都需要一个viewmodel。但是当他们被使用的时候,每一个相关的view都会创建一个viewmodel,这意味着可能存在一个viewmodel的多个实例。
下面以example里面的email代码为例,说明下这几个模块是怎么结合的。
首先定义一个全局视图容器类:
Ext.define('Admin.view.email.Email', {
extend: 'Ext.container.Container',
xtype: 'email',
controller: 'email',
viewModel: {
type: 'email'
},
itemId: 'emailMainContainer',
layout: {
type: 'hbox',
align: 'stretch'
},
margin: '20 0 0 20',
items: [
{
xtype: 'container',
itemId: 'navigationPanel',
layout: {
type: 'vbox',
align: 'stretch'
},
width: '30%',
minWidth: 180,
maxWidth: 240,
defaults: {
cls: 'navigation-email',
margin: '0 20 20 0'
},
items: [
{
xtype: 'emailmenu',
listeners: {
click: 'onMenuClick'
}
},
{
xtype: 'emailfriendslist'
}
]
},
{
xtype: 'container',
itemId: 'contentPanel',
margin: '0 20 20 0',
flex: 1,
layout: {
type : 'anchor',
anchor : '100%'
}
}
]
});
说明:
首先定义了一个类Admin.view.email.Email,类似于java里面的public class xxx。extend是继承。该类的类型是email(email是别名),controller引用email,viewmodel引用email。该全局view的id是emailMainContainer。之后定义了它的layout和margin。item里面包含了这个全局viewport里面包含的东西,即两个containers。
之后定义两个item的view。
Ext.define('Admin.view.email.Inbox', {
extend: 'Ext.grid.Panel',
alias: 'widget.emailinbox',
cls: 'email-inbox-panel shadow-panel',
viewModel: {
type: 'emailinbox'
},
bind: {
store: '{EmailInbox}'
},
viewConfig: {
preserveScrollOnRefresh: true,
preserveScrollOnReload: true
},
selModel: {
selType: 'checkboxmodel',
checkOnly: true,
showHeaderCheckbox: true
},
listeners: {
cellclick: 'onGridCellItemClick'
},
headerBorders: false,
rowLines: false,
columns: [
{
dataIndex: 'favorite',
menuDisabled: true,
text: '<span class="x-fa fa-heart"></span>',
width: 40,
renderer: function(value) {
return '<span class="x-fa fa-heart'+ (value ? '' : '-o') +'"></span>';
}
},
{
dataIndex: 'from',
text: 'From',
width: 140
},
{
dataIndex: 'title',
text: 'Title',
flex: 1
},
{
dataIndex: 'has_attachments',
text: '<span class="x-fa fa-paperclip"></span>',
width: 40,
renderer: function(value) {
return value ? '<span class="x-fa fa-paperclip"></span>' : '';
}
},
{
xtype: 'datecolumn',
dataIndex: 'received_on',
width: 90,
text: 'Received'
}
]
});
说明:这是一个表格。
cls表明应用的样式是email-indox-panel shadow-panel。
定义viewmodel是emailinbox,viewModel即当前视图的viewModel实例是什么,viewModel的参数值可以是viewMode的别名字符串,也可以是ViewModel带完整 命名空间的类路径的字符串形式,也可以是viewModel的配置实例对象。
关联的数据集是EmailInbox。bind即数据绑定,把Model数据绑以key-value形式暴露出去,view视图里可以采用{key}这种表达式来引用Model里的数据。
viewConfig:是关于该view的一些配置,这里将preserveScrollOnRefresh和preserveScrollOnReLoad都设置为true。
之后定义Inbox.js的viewmodel。
Ext.define('Admin.view.email.InboxViewModel', {
extend: 'Ext.app.ViewModel',
alias: 'viewmodel.emailinbox',
stores: {
EmailInbox: {
//Store reference
type: 'emailinbox'
}
}
});
说明:
ViewModel的关键在于data和stores,这里没有指明data。
data指的是此时此刻model的数据。
stores可以定义多个数据源,EmailInbox数据源的引用别名,可以通过grid.getStore("store引用名")来获取这里的数据源,后面的type是EmailInbox定义的别名,即表示这里的Store是哪个类的实例。如果有多个store你可以这样:
说明:
之后在data/email/inbox里面定义需要显示的数据即可。
stores: {
aaa: {type: ""xx.xxxx.AA""},
bbb: {type: ""xx.xxxx.BB""}
}
外部通过getStore("aa"),getStore("bb")这样来获取Store对象,后面的xx.xxx.AA是Store类的完整类路径(包含命名空间)。
然后在store里面绑定数据源。
Ext.define('Admin.store.email.Inbox', {
extend: 'Ext.data.Store',
requires:["Admin.model.email.Email"],
alias: 'store.emailinbox',
model: 'Admin.model.email.Email',
pageSize: 20,
autoLoad: true,
proxy: {
type: 'ajax',
url: '~api/email/inbox',
reader: {
type: 'json',
rootProperty: 'data'
}
}
});
说明:
store类的关键就是指定model和proxy。这里引用的数据源是Admin.model.email.Email.proxy设置读取方式是json,读取的数据是data字段。store需要的数据都以json字符串的形式定义在inbox.json文件里。Store是依赖于Model的,所以requires里需要引入Email类.
之后在data/email/inbox里面定义需要显示的数据即可。