原文:http://www.sencha.com/learn/architecting-your-app-in-ext-js-4-part-2/
在《ExtJS应用架构设计》一文,我们探讨了如何使用ExtJS构建一个潘多拉风格的应用程序。我们采用了MVC架构,并将它应用到一个比较复杂的用户界面,应用中带有多个视图和模型。在这篇文章中,我们将在架构的基础上继续探讨控制和模型的设计与代码问题,并开始使用Ext.application和Viewprot类。
现在,让我们开始编写应用。
定义应用
在ExtJS 3,Ext.onReady方法是应用程序和开发人员开始编写应用架构的入口。在ExtJS 4,我们推出了类似MVC模式,该模式可以让你在创建应用程序时遵循最佳做法。
新的MVC包要求使用Ext.application方法作为入口,该方法将创建一个Ext.app.Application实例,并在页面准备好以后触发launch方法。它取代了在Ext.onReady内添加诸如创建Viewport和设置命名空间等功能的这种写法。
app/Application.js
- Ext.application({
- name: 'Panda',
- autoCreateViewport: true,
- launch: function() {
- // This is fired as soon as the page is ready
- }
- });
配置项name将会创建一个命名空间。所有视图、模型和Store和控制器都会以该命名空间为命名。设置autoCreateViewport为true,框架将字段加载app/view/Viewport.js文件。在该文件内,将会定义一个名称为Panda.view.Viewport的类,类名必须使用应用中name指定的命名空间。
The Viewport class
在UI中的视图,都是独立的部件,因而需要使用Viewport将它们粘合起来。它会加载要求的视图及它们的定义需要的配置项,以实现应用的整体布局。我们发现,通过定义视图并将它们加载到viewprot,是创建UI基本结构最快的方法。
重要的是,这个过程的重点在搭建视图,而不是单个视图本身,就如雕刻一样,先开始创建视图的粗糙形状,然后细化它们。
创建构造块
利用前文中的工作,我们现在可以开始定义视图了。
app/view/NewStation.js
- Ext.define('Panda.view.NewStation', {
- extend: 'Ext.form.field.ComboBox',
- alias: 'widget.newstation',
- store: 'SearchResults',
- ... more configuration ...
- });
app/view/SongControls.js
- Ext.define('Panda.view.SongControls', {
- extend: 'Ext.Container',
- alias: 'widget.songcontrols',
- ... more configuration ...
- });
app/view/StationsList
- Ext.define('Panda.view.StationsList', {
- extend: 'Ext.grid.Panel',
- alias: 'widget.stationslist',
- store: 'Stations',
- ... more configuration ...
- });
app/view/RecentlyPlayedScroller.js
- Ext.define('Panda.view.RecentlyPlayedScroller', {
- extend: 'Ext.view.View',
- alias: 'widget.recentlyplayedscroller',
- itemTpl: '<div></div>',
- store: 'RecentSongs',
- ... more configuration ...
- });
app/view/SongInfo.js
- Ext.define('Panda.view.SongInfo', {
- extend: 'Ext.panel.Panel',
- alias: 'widget.songinfo',
- tpl: '<h1>About </h1><p></p>',
- ... more configuration ...
- });
我们只是列了一些定义,组件的具体配置将不在本文进行讨论。
在上述配置,可看到使用了三个Store,这些Store的名称都是上一篇文章定义的。现在,我们开始创建Store。
模型和Store
通常,在初始阶段,以包含数据的静的json文件作为服务器端是相当有用。以后,这些静态文件可以作为实际的服务器端动态文件的参考。
在当前应用,要使用Station和Song两个模型,还需要定义使用这两个模型并绑定到数据组件的Store。每个Store都会从服务器端加载数据,模拟的数据文件格式如下:
静态数据
data/songs.json
- {
- 'success': true,
- 'results': [
- {
- 'name': 'Blues At Sunrise (Live)',
- 'artist': 'Stevie Ray Vaughan',
- 'album': 'Blues At Sunrise',
- 'description': 'Description for Stevie',
- 'played_date': '1',
- 'station': 1
- },
- ...
- ]
- }
data/stations.json
- {
- 'success': true,
- 'results': [
- {'id': 1, 'played_date': 4, 'name': 'Led Zeppelin'},
- {'id': 2, 'played_date': 3, 'name': 'The Rolling Stones'},
- {'id': 3, 'played_date': 2, 'name': 'Daft Punk'}
- ]
- }
data/searchresults.json
- {
- 'success': true,
- 'results': [
- {'id': 1, 'name': 'Led Zeppelin'},
- {'id': 2, 'name': 'The Rolling Stones'},
- {'id': 3, 'name': 'Daft Punk'},
- {'id': 4, 'name': 'John Mayer'},
- {'id': 5, 'name': 'Pete Philly & Perquisite'},
- {'id': 6, 'name': 'Black Star'},
- {'id': 7, 'name': 'Macy Gray'}
- ]
- }
模型
ExtJS 4中的模型类似ExtJS 3中的记录,它们主要的区别是,可以在模型上定义代理、验证和关联。应用中的模型Song代码如下:
app/model/Song.js
- Ext.define('Panda.model.Song', {
- extend: 'Ext.data.Model',
- fields: ['id', 'name', 'artist', 'album', 'played_date', 'station'],
- proxy: {
- type: 'ajax',
- url: 'data/recentsongs.json',
- reader: {
- type: 'json',
- root: 'results'
- }
- }
- });
可以看到,在模型中定义了代理,这是一个好的方式,这样,就可以直接在模型加载和保存模型实例,而不需要经过Store。而且,当有多个Store使用该模型的时候,也不需要重新为每个Store定义一次代理。
下面继续定义Station 模型:
app/model/Station.js
- Ext.define('Panda.model.Station', {
- extend: 'Ext.data.Model',
- fields: ['id', 'name', 'played_date'],
- proxy: {
- type: 'ajax',
- url: 'data/stations.json',
- reader: {
- type: 'json',
- root: 'results'
- }
- }
- });
Store
在ExtJS 4,多个Store可以使用相同的数据模型,即使Store需要从不同的来源加载数据。在实例中,模型Station将会在SearchResults和Stations这两个Store中使用,而它们会从不同的位置加载数据。其中,SearchResults将返回返回搜索结果,而另一个则返回用户收藏的Station。为了实现这一点,其中一个Store需要重写模型中定义的代
理。
app/store/SearchResults.js
- Ext.define('Panda.store.SearchResults', {
- extend: 'Ext.data.Store',
- requires: 'Panda.model.Station',
- model: 'Panda.model.Station',
- // Overriding the model's default proxy
- proxy: {
- type: 'ajax',
- url: 'data/searchresults.json',
- reader: {
- type: 'json',
- root: 'results'
- }
- }
- });
app/store/Stations.js
- Ext.define('Panda.store.Stations', {
- extend: 'Ext.data.Store',
- requires: 'Panda.model.Station',
- model: 'Panda.model.Station'
- });
在SearchResults的定义,已经重写了Station模型中代理的定义,当调用Store的load方法时,其代理将调用Store的的代理以替换模型中定义的代理。
当然,也可以在服务器端使用相同的接口实现返回搜索结果和用户收藏的station,这样,两个Store就可以使用模型中定义的默认代理了,只是加载数据时请求的参数不同。
接着创建RecentSongs:
app/store/RecentSongs.js
- Ext.define('Panda.store.RecentSongs', {
- extend: 'Ext.data.Store',
- model: 'Panda.model.Song',
- // Make sure to require your model if you are
- // not using Ext JS 4.0.5
- requires: 'Panda.model.Song'
- });
要注意,在当前版本的ExtJS中,在Store中的model属性还不能自动创建一个依赖,因而需要通过requires配置项指定模型以便能够实现动态加载模型。
另外,根据约定,Store的类名要使用复数,而模型的类名要使用单数。
增加Store和模型到应用
定义好模型和Store后,现在可以把它们加到应用里。打开Application.js并添加如下代码:
app/Application.js
- Ext.application({
- ...
- models: ['Station', 'Song'],
- stores: ['Stations', 'RecentSongs', 'SearchResults']
- ...
- });
使用ExtJS 4 MVC包的另外一个好处是,应用会自动加载在stores和models中定义的Store和模型。当Store加载后,会为每个Store创建一个实例,并将其名称作为storeId。这样,就可以使用该名称将其绑定到视图中数据组件,例如“SearchResults”。
应用粘合剂
在开始的时候,可以将视图逐一加到Viewport,这样比较容易调试出视图错误的配置。先在Panda应用中构建Viewport:
- Ext.define('Panda.view.Viewport', {
- extend: 'Ext.container.Viewport',
通常,应用的Viewport类扩展自Ext.container.Viewport,这会让应用把浏览器的可用空间作为应用的空间。
- requires: [
- 'Panda.view.NewStation',
- 'Panda.view.SongControls',
- 'Panda.view.StationsList',
- 'Panda.view.RecentlyPlayedScroller',
- 'Panda.view.SongInfo'
- ],
这里要设置viewprot依赖的视图, 这样,就可以使用视图中通过alias属性定义的名称作为xtype配置项的值来定义视图。
- layout: 'fit',
- initComponent: function() {
- this.items = {
- xtype: 'panel',
- dockedItems: [{
- dock: 'top',
- xtype: 'toolbar',
- height: 80,
- items: [{
- xtype: 'newstation',
- width: 150
- }, {
- xtype: 'songcontrols',
- height: 70,
- flex: 1
- }, {
- xtype: 'component',
- html: 'Panda<br>Internet Radio'
- }]
- }],
- layout: {
- type: 'hbox',
- align: 'stretch'
- },
- items: [{
- width: 250,
- xtype: 'panel',
- layout: {
- type: 'vbox',
- align: 'stretch'
- },
- items: [{
- xtype: 'stationslist',
- flex: 1
- }, {
- html: 'Ad',
- height: 250,
- xtype: 'panel'
- }]
- }, {
- xtype: 'container',
- flex: 1,
- layout: {
- type: 'vbox',
- align: 'stretch'
- },
- items: [{
- xtype: 'recentlyplayedscroller',
- height: 250
- }, {
- xtype: 'songinfo',
- flex: 1
- }]
- }]
- };
- this.callParent();
- }
因为Viewport扩展自Container,而Container没有停靠项,因而必须添加一个面板作为viewport的第一个子组件,并让使用fit布局让面板与viewprot有相同的大小。
在架构方面,需要特别注意的是,不要在实际视图中定义有具体配置的布局,例如,不要定义flex、width或height等配置项,这样,就可以很容易的调整其在应用程序中某个位置的整体布局,从而增加架构的可维护性和灵活性。
应用逻辑
在ExtJS 3,通常会使用按钮的句柄、绑定监听时间到子组件或扩展时重写其方法以实现应用逻辑。然后,就像不应该在HTML标记中使用内联CSS样式一样,不应该在视图中定义应用逻辑。在ExtJS 4,通过MVC包提供的控制器,可响应视图或其它控制器中的监听事件,并执行这些事件对应的应用逻辑。这样的设计有及格好处。
第一个好处是,应用逻辑不用绑定到视图实例,这意味着在需要时,可以在视图实例化或销毁时,应用逻辑可以继续处理其它事情,如同步数据。
另外,在ExtJS 3,会有许多嵌套视图,而每一个都会添加一层应用逻辑。当将应用逻辑移动到控制器,将它们集中起来,这样就易于维护和修改。
最后,控制器基类会提供一些功能,让控制器易于执行应用逻辑。
创建控制器
现在,已经有了UI的基本架构、模型和Store的配置,是时候为应用定义控制器了。我们计划定义两个控制器,Station和Song,定义如下:
app/controller/Station.js
- Ext.define('Panda.controller.Station', {
- extend: 'Ext.app.Controller',
- init: function() {
- ...
- },
- ...
- });
app/controller/Song.js
- Ext.define('Panda.controller.Song', {
- extend: 'Ext.app.Controller',
- init: function() {
- ...
- },
- ...
- });
在应用中包含控制器后,框架会在调用init方法的时候字段加载控制器。在init方法内,可以定义监听视图和应用的事件。在大型应用,有时需要在运行时加载额外的控制器,可以使用getController方法加载:
- someAction: function() {
- var controller = this.getController('AnotherController');
- // Remember to call the init method manually
- controller.init();
- }
当在运行时加载额外的控制器时,一定要记得手动调用加载的控制器的init方法。
在实例应用,只要将控制器加到controllers配置项中的数组,让框架加载和初始化控制器就可以了。
app/Application.js
- Ext.application({
- ...
- controllers: ['Station', 'Song']
- });
定义监听
现在要在控制器的init方法内调用其control方法控制UI部件:
app/controller/Station.js
- ...
- <span class="me1">init</span><span class="sy0">:</span> <span class="kw2">function</span><span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span>
- <span class="kw1">this</span>.<span class="me1">control</span><span class="br0">(</span><span class="br0">{</span>
- <span class="st0">'stationslist'</span><span class="sy0">:</span> <span class="br0">{</span>
- selectionchange<span class="sy0">:</span> <span class="kw1">this</span>.<span class="me1">onStationSelect</span>
- <span class="br0">}</span><span class="sy0">,</span>
- <span class="st0">'newstation'</span><span class="sy0">:</span> <span class="br0">{</span>
- select<span class="sy0">:</span> <span class="kw1">this</span>.<span class="me1">onNewStationSelect</span>
- <span class="br0">}</span>
- <span class="br0">}</span><span class="br0">)</span><span class="sy0">;</span>
- <span class="br0">}</span>
- ...
方法control会通过对象的关键字查询组件。在示例中,会通过视图的xtypes配置项查询组件。然而,使用组件查询,必须确保能指向UI中的知道部件。想了解更多有关组件查询的信息,可访问这里。
每一个查询都会绑定一个监听配置对象,在每个监听配置对象内,事件名就是监听对象的关键字,而事件是由查询的目标组件提供的。在示例中,将使用Grid(在StationsList视图扩展出的)提供的selectionchange事件,及ComboBox(从NewStation视图中扩展出的)提供的select事件。要找到特定组件有那些事件,可以在API文档中查看组件的事件部分。
监听配置对象中的值是事件触发后执行的函数。函数的作用将是控制器本身。
现在为Song控制器添加一些监听:
app/controller/Song.js
- ...
- init: function() {
- this.control({
- 'recentlyplayedscroller': {
- selectionchange: this.onSongSelect
- }
- });
- this.application.on({
- stationstart: this.onStationStart,
- scope: this
- });
- }
- ..
除了监听RecentlyPlayedScroller视图的selectionchange事件外,这里还需要监听应用事件。这需要使用application实例的on方法。每一个控制都可以使用this.application直接访问application实例。
当在应用中有许多控制器都需要监听同一个事件的时候,应用事件这时候非常有用。与在每一个控制器中监听相同的视图事件一样,只需要在一个控制器中监听视图的事件,就可以触发一个应用范围的事件,这样,等于其它控制器也监听了该事件。这样,就实现了控制器之间进行通信,而无需了解或依赖于对方是否存在。
控制器Song需要知道新的station是否已开始,以便更新song滚动条和song信息。
现在在Station控制器内,编写当应用事件stationstart触发时的响应代码:
app/controller/Station.js
- ...
- onStationSelect: function(selModel, selection) {
- this.application.fireEvent('stationstart', selection[0]);
- }
- ...
这里只是简单在触发stationstart事件时,将selectionchange事件中的第一个选择项作为唯一参数传递给事件。
小结
在这篇文章中,可以了解到应用架构的基本技术。当然,这只是其中的一小部分,在该系列的下一部分,将可以看到一些更高级的控制器技术,并继续在控制器中实现操作,以及在视图中添加细节,以完成Panda应用。
原文:http://www.sencha.com/learn/architecting-your-app-in-ext-js-4-part-2/
在《ExtJS应用架构设计》一文,我们探讨了如何使用ExtJS构建一个潘多拉风格的应用程序。我们采用了MVC架构,并将它应用到一个比较复杂的用户界面,应用中带有多个视图和模型。在这篇文章中,我们将在架构的基础上继续探讨控制和模型的设计与代码问题,并开始使用Ext.application和Viewprot类。
现在,让我们开始编写应用。
定义应用
在ExtJS 3,Ext.onReady方法是应用程序和开发人员开始编写应用架构的入口。在ExtJS 4,我们推出了类似MVC模式,该模式可以让你在创建应用程序时遵循最佳做法。
新的MVC包要求使用Ext.application方法作为入口,该方法将创建一个Ext.app.Application实例,并在页面准备好以后触发launch方法。它取代了在Ext.onReady内添加诸如创建Viewport和设置命名空间等功能的这种写法。
app/Application.js
- Ext.application({
- name: 'Panda',
- autoCreateViewport: true,
- launch: function() {
- // This is fired as soon as the page is ready
- }
- });
配置项name将会创建一个命名空间。所有视图、模型和Store和控制器都会以该命名空间为命名。设置autoCreateViewport为true,框架将字段加载app/view/Viewport.js文件。在该文件内,将会定义一个名称为Panda.view.Viewport的类,类名必须使用应用中name指定的命名空间。
The Viewport class
在UI中的视图,都是独立的部件,因而需要使用Viewport将它们粘合起来。它会加载要求的视图及它们的定义需要的配置项,以实现应用的整体布局。我们发现,通过定义视图并将它们加载到viewprot,是创建UI基本结构最快的方法。
重要的是,这个过程的重点在搭建视图,而不是单个视图本身,就如雕刻一样,先开始创建视图的粗糙形状,然后细化它们。
创建构造块
利用前文中的工作,我们现在可以开始定义视图了。
app/view/NewStation.js
- Ext.define('Panda.view.NewStation', {
- extend: 'Ext.form.field.ComboBox',
- alias: 'widget.newstation',
- store: 'SearchResults',
- ... more configuration ...
- });
app/view/SongControls.js
- Ext.define('Panda.view.SongControls', {
- extend: 'Ext.Container',
- alias: 'widget.songcontrols',
- ... more configuration ...
- });
app/view/StationsList
- Ext.define('Panda.view.StationsList', {
- extend: 'Ext.grid.Panel',
- alias: 'widget.stationslist',
- store: 'Stations',
- ... more configuration ...
- });
app/view/RecentlyPlayedScroller.js
- Ext.define('Panda.view.RecentlyPlayedScroller', {
- extend: 'Ext.view.View',
- alias: 'widget.recentlyplayedscroller',
- itemTpl: '<div></div>',
- store: 'RecentSongs',
- ... more configuration ...
- });
app/view/SongInfo.js
- Ext.define('Panda.view.SongInfo', {
- extend: 'Ext.panel.Panel',
- alias: 'widget.songinfo',
- tpl: '<h1>About </h1><p></p>',
- ... more configuration ...
- });
我们只是列了一些定义,组件的具体配置将不在本文进行讨论。
在上述配置,可看到使用了三个Store,这些Store的名称都是上一篇文章定义的。现在,我们开始创建Store。
模型和Store
通常,在初始阶段,以包含数据的静的json文件作为服务器端是相当有用。以后,这些静态文件可以作为实际的服务器端动态文件的参考。
在当前应用,要使用Station和Song两个模型,还需要定义使用这两个模型并绑定到数据组件的Store。每个Store都会从服务器端加载数据,模拟的数据文件格式如下:
静态数据
data/songs.json
- {
- 'success': true,
- 'results': [
- {
- 'name': 'Blues At Sunrise (Live)',
- 'artist': 'Stevie Ray Vaughan',
- 'album': 'Blues At Sunrise',
- 'description': 'Description for Stevie',
- 'played_date': '1',
- 'station': 1
- },
- ...
- ]
- }
data/stations.json
- {
- 'success': true,
- 'results': [
- {'id': 1, 'played_date': 4, 'name': 'Led Zeppelin'},
- {'id': 2, 'played_date': 3, 'name': 'The Rolling Stones'},
- {'id': 3, 'played_date': 2, 'name': 'Daft Punk'}
- ]
- }
data/searchresults.json
- {
- 'success': true,
- 'results': [
- {'id': 1, 'name': 'Led Zeppelin'},
- {'id': 2, 'name': 'The Rolling Stones'},
- {'id': 3, 'name': 'Daft Punk'},
- {'id': 4, 'name': 'John Mayer'},
- {'id': 5, 'name': 'Pete Philly & Perquisite'},
- {'id': 6, 'name': 'Black Star'},
- {'id': 7, 'name': 'Macy Gray'}
- ]
- }
模型
ExtJS 4中的模型类似ExtJS 3中的记录,它们主要的区别是,可以在模型上定义代理、验证和关联。应用中的模型Song代码如下:
app/model/Song.js
- Ext.define('Panda.model.Song', {
- extend: 'Ext.data.Model',
- fields: ['id', 'name', 'artist', 'album', 'played_date', 'station'],
- proxy: {
- type: 'ajax',
- url: 'data/recentsongs.json',
- reader: {
- type: 'json',
- root: 'results'
- }
- }
- });
可以看到,在模型中定义了代理,这是一个好的方式,这样,就可以直接在模型加载和保存模型实例,而不需要经过Store。而且,当有多个Store使用该模型的时候,也不需要重新为每个Store定义一次代理。
下面继续定义Station 模型:
app/model/Station.js
- Ext.define('Panda.model.Station', {
- extend: 'Ext.data.Model',
- fields: ['id', 'name', 'played_date'],
- proxy: {
- type: 'ajax',
- url: 'data/stations.json',
- reader: {
- type: 'json',
- root: 'results'
- }
- }
- });
Store
在ExtJS 4,多个Store可以使用相同的数据模型,即使Store需要从不同的来源加载数据。在实例中,模型Station将会在SearchResults和Stations这两个Store中使用,而它们会从不同的位置加载数据。其中,SearchResults将返回返回搜索结果,而另一个则返回用户收藏的Station。为了实现这一点,其中一个Store需要重写模型中定义的代
理。
app/store/SearchResults.js
- Ext.define('Panda.store.SearchResults', {
- extend: 'Ext.data.Store',
- requires: 'Panda.model.Station',
- model: 'Panda.model.Station',
- // Overriding the model's default proxy
- proxy: {
- type: 'ajax',
- url: 'data/searchresults.json',
- reader: {
- type: 'json',
- root: 'results'
- }
- }
- });
app/store/Stations.js
- Ext.define('Panda.store.Stations', {
- extend: 'Ext.data.Store',
- requires: 'Panda.model.Station',
- model: 'Panda.model.Station'
- });
在SearchResults的定义,已经重写了Station模型中代理的定义,当调用Store的load方法时,其代理将调用Store的的代理以替换模型中定义的代理。
当然,也可以在服务器端使用相同的接口实现返回搜索结果和用户收藏的station,这样,两个Store就可以使用模型中定义的默认代理了,只是加载数据时请求的参数不同。
接着创建RecentSongs:
app/store/RecentSongs.js
- Ext.define('Panda.store.RecentSongs', {
- extend: 'Ext.data.Store',
- model: 'Panda.model.Song',
- // Make sure to require your model if you are
- // not using Ext JS 4.0.5
- requires: 'Panda.model.Song'
- });
要注意,在当前版本的ExtJS中,在Store中的model属性还不能自动创建一个依赖,因而需要通过requires配置项指定模型以便能够实现动态加载模型。
另外,根据约定,Store的类名要使用复数,而模型的类名要使用单数。
增加Store和模型到应用
定义好模型和Store后,现在可以把它们加到应用里。打开Application.js并添加如下代码:
app/Application.js
- Ext.application({
- ...
- models: ['Station', 'Song'],
- stores: ['Stations', 'RecentSongs', 'SearchResults']
- ...
- });
使用ExtJS 4 MVC包的另外一个好处是,应用会自动加载在stores和models中定义的Store和模型。当Store加载后,会为每个Store创建一个实例,并将其名称作为storeId。这样,就可以使用该名称将其绑定到视图中数据组件,例如“SearchResults”。
应用粘合剂
在开始的时候,可以将视图逐一加到Viewport,这样比较容易调试出视图错误的配置。先在Panda应用中构建Viewport:
- Ext.define('Panda.view.Viewport', {
- extend: 'Ext.container.Viewport',
通常,应用的Viewport类扩展自Ext.container.Viewport,这会让应用把浏览器的可用空间作为应用的空间。
- requires: [
- 'Panda.view.NewStation',
- 'Panda.view.SongControls',
- 'Panda.view.StationsList',
- 'Panda.view.RecentlyPlayedScroller',
- 'Panda.view.SongInfo'
- ],
这里要设置viewprot依赖的视图, 这样,就可以使用视图中通过alias属性定义的名称作为xtype配置项的值来定义视图。
- layout: 'fit',
- initComponent: function() {
- this.items = {
- xtype: 'panel',
- dockedItems: [{
- dock: 'top',
- xtype: 'toolbar',
- height: 80,
- items: [{
- xtype: 'newstation',
- width: 150
- }, {
- xtype: 'songcontrols',
- height: 70,
- flex: 1
- }, {
- xtype: 'component',
- html: 'Panda<br>Internet Radio'
- }]
- }],
- layout: {
- type: 'hbox',
- align: 'stretch'
- },
- items: [{
- width: 250,
- xtype: 'panel',
- layout: {
- type: 'vbox',
- align: 'stretch'
- },
- items: [{
- xtype: 'stationslist',
- flex: 1
- }, {
- html: 'Ad',
- height: 250,
- xtype: 'panel'
- }]
- }, {
- xtype: 'container',
- flex: 1,
- layout: {
- type: 'vbox',
- align: 'stretch'
- },
- items: [{
- xtype: 'recentlyplayedscroller',
- height: 250
- }, {
- xtype: 'songinfo',
- flex: 1
- }]
- }]
- };
- this.callParent();
- }
因为Viewport扩展自Container,而Container没有停靠项,因而必须添加一个面板作为viewport的第一个子组件,并让使用fit布局让面板与viewprot有相同的大小。
在架构方面,需要特别注意的是,不要在实际视图中定义有具体配置的布局,例如,不要定义flex、width或height等配置项,这样,就可以很容易的调整其在应用程序中某个位置的整体布局,从而增加架构的可维护性和灵活性。
应用逻辑
在ExtJS 3,通常会使用按钮的句柄、绑定监听时间到子组件或扩展时重写其方法以实现应用逻辑。然后,就像不应该在HTML标记中使用内联CSS样式一样,不应该在视图中定义应用逻辑。在ExtJS 4,通过MVC包提供的控制器,可响应视图或其它控制器中的监听事件,并执行这些事件对应的应用逻辑。这样的设计有及格好处。
第一个好处是,应用逻辑不用绑定到视图实例,这意味着在需要时,可以在视图实例化或销毁时,应用逻辑可以继续处理其它事情,如同步数据。
另外,在ExtJS 3,会有许多嵌套视图,而每一个都会添加一层应用逻辑。当将应用逻辑移动到控制器,将它们集中起来,这样就易于维护和修改。
最后,控制器基类会提供一些功能,让控制器易于执行应用逻辑。
创建控制器
现在,已经有了UI的基本架构、模型和Store的配置,是时候为应用定义控制器了。我们计划定义两个控制器,Station和Song,定义如下:
app/controller/Station.js
- Ext.define('Panda.controller.Station', {
- extend: 'Ext.app.Controller',
- init: function() {
- ...
- },
- ...
- });
app/controller/Song.js
- Ext.define('Panda.controller.Song', {
- extend: 'Ext.app.Controller',
- init: function() {
- ...
- },
- ...
- });
在应用中包含控制器后,框架会在调用init方法的时候字段加载控制器。在init方法内,可以定义监听视图和应用的事件。在大型应用,有时需要在运行时加载额外的控制器,可以使用getController方法加载:
- someAction: function() {
- var controller = this.getController('AnotherController');
- // Remember to call the init method manually
- controller.init();
- }
当在运行时加载额外的控制器时,一定要记得手动调用加载的控制器的init方法。
在实例应用,只要将控制器加到controllers配置项中的数组,让框架加载和初始化控制器就可以了。
app/Application.js
- Ext.application({
- ...
- controllers: ['Station', 'Song']
- });
定义监听
现在要在控制器的init方法内调用其control方法控制UI部件:
app/controller/Station.js
- ...
- <span class="me1">init</span><span class="sy0">:</span> <span class="kw2">function</span><span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span>
- <span class="kw1">this</span>.<span class="me1">control</span><span class="br0">(</span><span class="br0">{</span>
- <span class="st0">'stationslist'</span><span class="sy0">:</span> <span class="br0">{</span>
- selectionchange<span class="sy0">:</span> <span class="kw1">this</span>.<span class="me1">onStationSelect</span>
- <span class="br0">}</span><span class="sy0">,</span>
- <span class="st0">'newstation'</span><span class="sy0">:</span> <span class="br0">{</span>
- select<span class="sy0">:</span> <span class="kw1">this</span>.<span class="me1">onNewStationSelect</span>
- <span class="br0">}</span>
- <span class="br0">}</span><span class="br0">)</span><span class="sy0">;</span>
- <span class="br0">}</span>
- ...
方法control会通过对象的关键字查询组件。在示例中,会通过视图的xtypes配置项查询组件。然而,使用组件查询,必须确保能指向UI中的知道部件。想了解更多有关组件查询的信息,可访问这里。
每一个查询都会绑定一个监听配置对象,在每个监听配置对象内,事件名就是监听对象的关键字,而事件是由查询的目标组件提供的。在示例中,将使用Grid(在StationsList视图扩展出的)提供的selectionchange事件,及ComboBox(从NewStation视图中扩展出的)提供的select事件。要找到特定组件有那些事件,可以在API文档中查看组件的事件部分。
监听配置对象中的值是事件触发后执行的函数。函数的作用将是控制器本身。
现在为Song控制器添加一些监听:
app/controller/Song.js
- ...
- init: function() {
- this.control({
- 'recentlyplayedscroller': {
- selectionchange: this.onSongSelect
- }
- });
- this.application.on({
- stationstart: this.onStationStart,
- scope: this
- });
- }
- ..
除了监听RecentlyPlayedScroller视图的selectionchange事件外,这里还需要监听应用事件。这需要使用application实例的on方法。每一个控制都可以使用this.application直接访问application实例。
当在应用中有许多控制器都需要监听同一个事件的时候,应用事件这时候非常有用。与在每一个控制器中监听相同的视图事件一样,只需要在一个控制器中监听视图的事件,就可以触发一个应用范围的事件,这样,等于其它控制器也监听了该事件。这样,就实现了控制器之间进行通信,而无需了解或依赖于对方是否存在。
控制器Song需要知道新的station是否已开始,以便更新song滚动条和song信息。
现在在Station控制器内,编写当应用事件stationstart触发时的响应代码:
app/controller/Station.js
- ...
- onStationSelect: function(selModel, selection) {
- this.application.fireEvent('stationstart', selection[0]);
- }
- ...
这里只是简单在触发stationstart事件时,将selectionchange事件中的第一个选择项作为唯一参数传递给事件。
小结
在这篇文章中,可以了解到应用架构的基本技术。当然,这只是其中的一小部分,在该系列的下一部分,将可以看到一些更高级的控制器技术,并继续在控制器中实现操作,以及在视图中添加细节,以完成Panda应用。