原文地址:http://docs.sencha.com/extjs/4.0.7/#!/guide/mvc_pt2
【翻译 by 明明如月 QQ 605283073 本章节配套项目代码将在本节末尾给出】
上一节:
Ext JS 4 架构你的应用 第2节 (官方文档翻译)
前面的一系列文章中我们介绍了Ext JS 4新特性来创建潘多拉应用。我们使用了Model-View-Controller架构。
本文将在应用MVC架构下,继续实现controller (控制器)的逻辑。
参考
在我们继续实现我们的应用前,我们应该了解 Ext JS 4 MVC提供的更多的先进的方法。
在前面的章节中我们展示了怎样通过将其添加到 Ext.application中的stores和models数组方式自动加载stores和models。
app/Application.js
代码语言:javascript复制Ext.application({
...
models: ['Station', 'Song'],
stores: ['Stations', 'RecentSongs', 'SearchResults']
...
});
除了加载和实例化这些类之外也自动的为stores和models创建了getter。
对于controllers 和views也是一样。
这些在控制器中和Application 中也是一样的。也就意味着为了获取Station controller中的 Stations store,你索要做的只是将该store添加到stores数组中。
app/controller/Station.js
代码语言:javascript复制...
stores: ['Stations'],
...
现在我们可以在此控制器的任意位置获取 Stations store的引用(通过自动产生的名为getStationsStore的getter)
代码语言:javascript复制views: ['StationsList'] // creates getter named 'getStationsListView' -> returns reference to StationsList class
models: ['Station'] // creates getter named 'getStationModel' -> returns reference to Station model class
controllers: ['Song'] // creates getter named 'getSongController' -> returns the Song controller instance
stores: ['Stations'] // creates getter named 'getStationsStore' -> returns the Stations store instance
注意视图和模型的getters返回一个类的引用,而stores和controllers的getter返回的却是实际的实例。
引用view 实例
在上面的节中我们描述了 stores, models, controllers 和 views怎样配置自动创建getter来获取他们的引用。
getStationsListView
的getter将返回view类的一个引用。在我们的应用流中,我们想要选择StationsList的第一个项。这样我们不想要引用视图。而是引用viewport中StationsList 的实例。
Ext JS 3中一个获取一个页面中存在组件实例的一个非常通用的做法是使用Ext.getCmp方法。
虽然这个方法仍然可以使用,但是在Ext JS 4中我们不建议这么用。
使用Ext.getCmp 为了引用它,需要你给每一个组件定义一个唯一的id。
在新的MVC包中,使用 Ext JS 4:的ComponentQuery新特性来获取视图的引用。
app/controller/Station.js
代码语言:javascript复制...
refs: [{
// A component query
selector: 'viewport > #west-region > stationslist',
ref: 'stationsList'
}]
...
在 refs
配置中,可以设置视图实例的引用。允许你在控制器的行为中检索和操作页面组件。
可以使用 ComponentQuery 来获取组件的引用。
另外如果你没在控制器中设置引用。你可以继续在控制器的行为中使用Ext.getCmp
。
但是不建议这么用,它强迫我们管理项目中组件唯一ID,通常随着项目的增长,将带来一些问题。
需要记住的时 这些getters 将被独立的创建,不管页面中是否真的存在这个view.
如果此getter 没有匹配页面的任意的view(视图)将返回null.
这就也为这如果你有一个基于视图的逻辑而且在页面中还没有存在,这样你就需要对逻辑进行检查只有getter 方法有返回值时再执行。最后当你销毁一个你引用的组件后再调用getter方法将返回null,直到页面中存在另外一个符合选择器的组件出现。
级联应用启动的 controller(控制器)逻辑
当应用启动时你想要加载用户已经存在的站点。你可以将逻辑放在application的 onReady方法中,
MVC架构提供一个明确的全局应用逻辑和某个控制器特定的逻辑。
Step 1
app/controller/Station.js
代码语言:javascript复制...
onLaunch: function() {
// Use the automatically generated getter to get the store
var stationsStore = this.getStationsStore();
stationsStore.load({
callback: this.onStationsLoad,
scope: this
});
}
...
Station 控制器的onlaunch方法是调用 Station store的加载方法的好地方。
正如你所见,我们也设置了store加载完成的回调函数。
Step 2
app/controller/Station.js
代码语言:javascript复制...
onStationsLoad: function() {
var stationsList = this.getStationsList();
stationsList.getSelectionModel().select(0);
}
...
在这个回调函数中我们 使用自动产生的getter方法获取 StationsList 实例,并选择了第一个项。
这将触发StationsList的一个selectionchange
时间。
Step 3
app/controller/Station.js
代码语言:javascript复制...
init: function() {
this.control({
'stationslist': {
selectionchange: this.onStationSelect
},
...
});
},
onStationSelect: function(selModel, selection) {
this.application.fireEvent('stationstart', selection[0]);
},
...
Application 事件在多个控制器都需要处理同一个事件的时候非常有用。不是在每个控制器里都监听同一个视图事件,而是只有一个控制器来监听视图事件然后触发一个由其他控制器监听的应用范围的事件。
这也允许控制器在不知道或者不依赖已经存在的控制器情况下彼此通信。
在onStationSelect
行为中,我们触发一个叫stationstart的应用事件。
Step 4
app/controller/Song.js
代码语言:javascript复制...
refs: [{
ref: 'songInfo',
selector: 'songinfo'
}, {
ref: 'recentlyPlayedScroller',
selector: 'recentlyplayedscroller'
}],
stores: ['RecentSongs'],
init: function() {
...
// We listen for the application-wide stationstart event
this.application.on({
stationstart: this.onStationStart,
scope: this
});
},
onStationStart: function(station) {
var store = this.getRecentSongsStore();
store.load({
callback: this.onRecentSongsLoad,
params: {
station: station.get('id')
},
scope: this
});
}
...
作为Song 控制器init方法的一部分,我们设置了一个对stationstart
应用事件的监听器。当事件发生时,我们需要从站点加载songs到RecentSongs store中。
我们在onStationStart
方法中实现此功能。我们获取RecentSongs store的一个引用调用其load方法,定义了加载完成后要触发的控制器方法。
Step 5
app/controller/Song.js
代码语言:javascript复制...
onRecentSongsLoad: function(songs, request) {
var store = this.getRecentSongsStore(),
selModel = this.getRecentlyPlayedScroller().getSelectionModel();
selModel.select(store.last());
}
...
当站点的歌曲被加载到RecentSongs store中,我们选择RecentlyPlayedScroller的最后一首歌曲。
我们通过获取 RecentlyPlayedScroller 中dataview的选择模型调用其选择方法,传入 RecentSongs store最后一个记录,来实现上面所说的效果。
代码语言:javascript复制...
init: function() {
this.control({
'recentlyplayedscroller': {
selectionchange: this.onSongSelect
}
});
...
},
onSongSelect: function(selModel, selection) {
this.getSongInfo().update(selection[0]);
}
...
创建一个新的station(站点)
代码语言:javascript复制...
refs: [{
ref: 'stationsList',
selector: 'stationslist'
}],
init: function() {
// Listen for the select event on the NewStation combobox
this.control({
...
'newstation': {
select: this.onNewStationSelect
}
});
},
onNewStationSelect: function(field, selection) {
var selected = selection[0],
store = this.getStationsStore(),
list = this.getStationsList();
if (selected && !store.getById(selected.get('id'))) {
// If the newly selected station does not exist in our station store we add it
store.add(selected);
}
// We select the station in the Station list
list.getSelectionModel().select(selected);
}
...
总结
我们介绍了使用高级的控制器技术实现逻辑和视图的分离,使得用用架构更加容易理解和维护。
在此阶段,应用已经非常功能化。我们可以搜索和添加新的站点,还可以通过选择站点来播放站点。
站点的歌曲将被加载,我们也将显示歌曲和艺术家信息。
我们也将从风格和自定义组件创建等角度继续改进我们的应用。
【代码下载地址】http://docs.sencha.com/extjs/4.0.7/guides/mvc_pt3/code.zip