ViewModel
ViewModel是一个管理数据对象的类, 它运行对它感兴趣的组件绑定它并在变化的时候得到通知。ViewModel和ViewController一样,是属于某个 View的。子视图可以继承父的视图模型。
使用 bind的配置来绑定数据,当数据发生改变时,会调用对应的 setter方法。
组件绑定
组件是数据绑定的主要方式。
绑定的配置需要有setter方法,比如Panel 的title配置,title有setTitle()方法, 所有可以绑定数据。
以width为例(有setWidth()方法)
Ext.create('Ext.panel.Panel', {
title: 'Simple Form',
viewModel: {
type: 'test' // we will define the "test" ViewModel soon
},
bind: {
html: '<p>Hello {name}</p>',
width: '{someWidth}'
}
});
这里绑定的语法类似于Ext.Template。
如果绑定布尔类型的值的话,
bind: {
hidden: '{!name}' // negated
}
name也可是一个String类型。
绑定的优先级
数据绑定一般要优先于静态配置。
子组件的绑定
一个组件的所有子组件可以访问这个组件的 viewModel
看实例:
Ext.create('Ext.panel.Panel', {
title: 'Simple Form',
viewModel: {
type: 'test'
},
layout: 'form',
defaultType: 'textfield',
items: [{
fieldLabel: 'First Name',
bind: '{firstName}' // uses "test" ViewModel from parent
},{
fieldLabel: 'Last Name',
bind: '{lastName}'
}]
});
双向绑定
双向绑定意味着视图和模型的数据实时同步。 视图的数据改变自动写到模型中。这回自动更新绑定到同一数据的其他组件。
需要注意的是并不是所有的配置都会将其更改发布到 ViewModel。
定义publish和twoWayBindable会将改动写回ViewModel。使用publishState方法也可以在组件和应用逻辑之间发布数据。
publish和twoWayBindable是组件的配置项。支持一个或多个属性。看个例子:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>View Model</title>
<script type="text/javascript" src="../ext/build/ext-all-debug.js"></script>
<link href="../build/development/Extjs6App/classic/resources/Extjs6App-all.css" rel="stylesheet" type="text/css" />
<script>
//1.定义用户的视图模型
Ext.define('Osxm.view.UserViewModel', {
extend: 'Ext.app.ViewModel',
alias: 'viewmodel.user', // 使用hello获取该model
data: {
firstName: 'chen',
lastName: 'oscar'
},
formulas: {
// We'll explain formulas in more detail soon.
name: function (get) {
var fn = get('firstName'), ln = get('lastName');
return (fn && ln) ? (fn + ' ' + ln) : (fn || ln || '');
}
}
});
//2.定义用户的视图组件
Ext.define('MyApp.view.UserView', {
extend: 'Ext.panel.Panel',
layout: 'form',
viewModel: {
type: 'user' // 使用视图模型的alias属性:"viewmodel.user"
},
bind: {
title: 'Hello {name}'
},
defaultType: 'textfield',
items: [{
fieldLabel: 'First Name',
bind: '{firstName}'
},{
fieldLabel: 'Last Name',
bind: '{lastName}'
},{
xtype: 'button',
text: 'Submit',
bind: {
hidden: '{!name}'
}
}]
});
</script>
<script>
Ext.onReady(function() {
Ext.create('MyApp.view.UserView', {
renderTo : Ext.getBody(),
width : 400
});
});
</script>
</head>
<body>
</body>
</html>
实例效果如下:
标题的显示和按钮是否显示会根据输入框的值动态改变。
也就是:
视图模型的值改变了, 会反应到视图上。
视图上的值进行修改, 视图模型的值也会修改。
绑定和组件状态
某些时候组件的状态, 类似checkbox是否选中或是 Gird中选择一行,对其他的组件会有影响。当一个组件有使用reference?来表示,这个组件会在视图模型中发布一些关键的属性。
Ext.create('Ext.panel.Panel', {
title : 'Login Form',
width : 200,
viewModel : {
},
renderTo : Ext.getBody(),
items : [ {
xtype : 'checkbox',
boxLabel : 'Manual Login',
reference : 'manualLogin'
}, {
xtype : 'textfield',
fieldLabel : 'User Name',
bind : {
disabled : '{!manualLogin.checked}'
}
} ]
});
之上只有’Manual Login’这个复选框选中, 才可以在输入框中进行输入。
这里虽然viewModel中没有设置, 但是viewModel不能少, 好的做法是定义实际的viewModel类型。
多值绑定
也可以绑定多个值
Ext.create('Ext.Component', {
bind: {
data: {
fname: '{firstName}',
lname: '{lastName}'
}
}
});
绑定记录
也可以绑定一个Store中的记录
Ext.create('Ext.Component', {
bind: {
data: {
reference: 'User',
id: 42
}
}
})
User是Ext.data.Session类型。
关联绑定
Ext.create('Ext.Component', {
bind: {
data: {
reference: 'User',
id: 42,
association: 'address'
}
}
});
绑定的选项设置
bindTo
绑定一个值后自动断开连接
Ext.create('Ext.Component', {
bind: {
data: {
bindTo: '{name}',
single: true
}
}
});
deep-对象属性改变得到通知
Ext.create('Ext.Component', {
bind: {
data: {
bindTo: '{someObject}',
deep: true
}
}
});
视图模型公式
公式实例:
Ext.define('Osxm.view.FormulasViewMode', {
extend: 'Ext.app.ViewModel',
alias: 'viewmodel.formulavm',
data:{
x:1,
y:2
},
formulas: {
x2y: function (get) {
return get('x2') * get('y');
},
x2: function (get) {
return get('x') * 2;
}
}
});
Ext.create('Ext.panel.Panel', {
title : 'Formulas View Model',
width : 200,
viewModel : {
type : 'formulavm'
},
renderTo : Ext.getBody(),
defaultType : 'textfield',
items : [ {
xtype : 'textfield',
fieldLabel : 'x',
bind : '{x}'
} ,{
xtype : 'textfield',
fieldLabel : 'y',
bind : '{y}'
} ,{
xtype : 'textfield',
fieldLabel : 'x*2',
bind : '{x2}'
} ,{
xtype : 'textfield',
fieldLabel : 'x*2*y',
bind : '{x2y}'
}]
});
比较好的是使用显式绑定:
x3:{
bind: {
x: '{x}',
y: '{y}'
},
get: function (data) {
return data.x + data.y; //这里是字符串相加
}
}
双向公式
可以通过set方法实现双向公式, 类似:
Ext.define('MyApp.view.TestViewModel', {
extend: 'Ext.app.ViewModel',
alias: 'viewmodel.test',
formulas: {
name: {
get: function (get) {
var fn = get('firstName'), ln = get('lastName');
return (fn && ln) ? (fn + ' ' + ln) : (fn || ln || '');
},
set: function (value) {
var space = value.indexOf(' '),
split = (space < 0) ? value.length : space;
this.set({
firstName: value.substring(0, split),
lastName: value.substring(split + 1)
});
}
}
}
});
开发建议
为避免滥用和内存泄漏, 一些最佳实践:
- 使用 type来配置使用视图模型
Ext.define('MyApp.view.TestView', {
//...
viewModel: {
type: 'test'
},
});
-
命名显而易见
-
不必要的话不要在对象中嵌套数据
-
使用子的视图模型,在组件需要的时候可以清理数据
-
若非必要,不要创建子视图模型
6.使用公式替换重复绑定 -
公式不要太深
-
双向公式需要稳定。