文章目录
Ext JS 加入了ViewModels和 MVVM, 可以像MVC的 ViewController一样。这两种方式不相互排斥, 甚至可以混合使用。
应用层级控制器 - Application-level Controllers
一个控制器继承自Ext.app.Controller, 这些控制器使用类似 CSS的选择器来查找组件和响应事件。也可以通过 refs 选择或获取组件实例。
这些控制器在应用启动的时候创建, 会在整个应用的生命周期中存在。控制器也可以管理多个视图的实例。
在大型项目中, 视图和控制器可能是有多个不同开发团队开发,最终集成到应用中。 要确保控制器仅对其相关的视图响应是比较困难的。另外, 开发人员通常希望在应用启动的时候限制控制器的数量。虽然可以通过一些方法懒加载控制器, 但是即使不需要它们也不会被销毁。
视图控制器 - ViewControllers
Ext JS5及之后版本向后兼容合法的应用层级控制器。引入了新的控制器类型Ext.app.ViewController, 解决了大型项目控制器的问题。
- 使用 “listeners”和 “reference”配置简化了和视图的连接
-利用视图的生命周期管理和其管理的视图控制器的生命周期
-基于一对一的视图管理关系降低了视图控制器的复杂性
-提供封装使嵌套视图可靠
-保留在关联视图下方的任何级别选择组件并收听其事件的功能。
监听器 listeners
实例
- View子项的监听配置实例
Ext.define('MyApp.view.foo.Foo', {
extend: 'Ext.panel.Panel',
xtype: 'foo',
controller: 'foo',
items: [{
xtype: 'textfield',
fieldLabel: 'Bar',
listeners: {
change: 'onBarChange' // no scope given here
}
}]
});
在控制器中的配置:
Ext.define('MyApp.view.foo.FooController', {
extend: 'Ext.app.ViewController',
alias: 'controller.foo',
onBarChange: function (barTextField) {
// called by 'change' event
}
});
'onBarChange’没有指定范围, 则默认查找属于它自己的控制器。
安装经验, 监听配置是预留给组件创建者使用。所以视图如何监听自己的事件,还是由其基类触发的?答案是需要显示定义范围scope。
Ext.define('MyApp.view.foo.Foo', {
extend: 'Ext.panel.Panel',
xtype: 'foo',
controller: 'foo',
listeners: {
collapse: 'onCollapse',
scope: 'controller'
},
items: [{
...
}]
});
上面使用了 Ext JS的两个新功能: 范围(scopes)和声明式监听 (declarative listeners)。
Scope的值可以是 this 和 controller。如果是 MVC的应用, 大部分使用controller。
因为View是一种类型的Ext.Component, 指定视图的 xtype,这样其他的视图就可以使用这种方式来创建。例如:
Ext.define('MyApp.view.bar.Bar', {
extend: 'Ext.panel.Panel',
xtype: 'bar',
controller: 'bar',
items: [{
xtype: 'foo',
listeners: {
collapse: 'onCollapse'
}
}]
});
引用 - Reference
在完成控制器逻辑的时候, 常见的松散耦合的方式是获取需要的组件完成特定的动作, 类似:
Ext.define('MyApp.view.foo.Foo', {
extend: 'Ext.panel.Panel',
xtype: 'foo',
controller: 'foo',
tbar: [{
xtype: 'button',
text: 'Add',
handler: 'onAdd'
}],
items: [{
xtype: 'grid',
...
}]
});
Ext.define('MyApp.view.foo.FooController', {
extend: 'Ext.app.ViewController',
alias: 'controller.foo',
onAdd: function () {
// ... get the grid and add a record ...
}
});
如何获取组件, 比如上面的Grid组件, 最快的方式就是通过 配置的id使用 getCmp方法, 或者是 配置ItemId通过 refs或者是组件查找的方法, id查找不好的地方就是整个应用id及对应的DOM必须唯一。使用 itemId或者组件查找有弹性一点,但是需要通过查找的方式获取。
使用 reference?配置,可以简单的通过lookupReference的方式来获取。实例如下:
Ext.define('MyApp.view.foo.Foo', {
extend: 'Ext.panel.Panel',
xtype: 'foo',
controller: 'foo',
tbar: [{
xtype: 'button',
text: 'Add',
handler: 'onAdd'
}],
items: [{
xtype: 'grid',
reference: 'fooGrid'
...
}]
});
Ext.define('MyApp.view.foo.FooController', {
extend: 'Ext.app.ViewController',
alias: 'controller.foo',
onAdd: function () {
var grid = this.lookupReference('fooGrid');
}
});
这种方式和指定 itemId 之后通过this.down()的方式获取很类似。但是, 实现的引擎则有很大差异。
首先 reference?配置指示组件在其视图中组测自己(在这种情况下通过ViewController的存在来识别)
另外, lookupReference方法只是简单的咨询缓存是否需要刷新引用, 伪代码类似:
lookupReference: (reference) {
var cache = this.references;
if (!cache) {
Ext.fixReferences(); // fix all references
cache = this.references; // now the cache is valid
}
return cache[reference];
}
也就是说, 这种方式没有搜索而且在容器中添加或是删除组件也是一次性调整, 除了性能上提升外,还有其他的好处。
封装- Encapsulation
使用refs配置来查找很有弹性, 但是也存在一定风险。这个选择器虽然可以看到各个层级的组件,但是容易出错。举例来说, 一个控制器如果独立运行的话没有恩替, 但是导入其他的视图之后就会失败了。
使用监听和references?在ViewController?中这些问题就不在了。因为listeners 和 reference 配置仅仅定义在自己的ViewController中。
ViewController中有一个帮助方法:fireViewEvent
Ext.define('MyApp.view.foo.FooController', {
extend: 'Ext.app.ViewController',
alias: 'controller.foo',
onAdd: function () {
var record = new MyApp.model.Thing();
var grid = this.lookupReference('fooGrid');
grid.store.add(record);
this.fireViewEvent('addrecord', this, record);
}
});
使用标准的监听器:
Ext.define('MyApp.view.bar.Bar', {
extend: 'Ext.panel.Panel',
xtype: 'bar',
controller: 'bar',
items: [{
xtype: 'foo',
listeners: {
collapse: 'onCollapse',
addrecord: 'onAddRecord'
}
}]
});
监听和事件域知识
生命周期
在大型应用中,一般是在首次需要的时候再动态创建控制器,这样可以减少系统加载的时间,提升系统性能。Ext JS早期版本的限制是只要创建就会一直存在, 无缝销毁和释放资源。同样,这也没有改变一个控制器有多个(或是没有)关联视图的现实。
ViewController 在组件生命周期很早就创建出来并绑定到视图的生命周期, 当视图销毁时, ViewController也一起销毁。这意味着不再强制ViewController管理没有视图或许多视图的状态。
一对一的关系意味着关联的追踪简单并且不容易发生销毁组件的泄漏。ViewController可以实现任何方法已经各生命周期点的任务:
beforeInit?: 可以被重写。在组件initComponent?方法调用之前执行。这个方法在控制器创建完成之后就立即执行, 一般发生在组件构造器呼叫initConfig.
init?: 视图调用initComponent?不久后调用。
initViewModel? - 在视图的ViewModel?创建的时候调用。
destroy?: 清除并返回资源, 一定要调用callParent