隨著Html 5的興起,基於此的項目也越來越的多,因而出現了很多MVC框架,如:Backbone.js、Ember.js、Angular.JS等,此系列將闡述這些框架在代碼層面的區別,希望可以給初學者一些思路。
本文將介紹Ember.JS構建基於jQuery Mobile的PhoneGap項目(Hybird App)。
使用Ember.JS + jQuery Mobile + PhoneGap構建一個Hybird App項目,命名為Adobe Reader,具有如下功能:
下面將展示Adobe Reader的關鍵代碼,並分別介紹Ember.JS中M(Model)V(VIEw)C(Controller)
代碼特點、UI Binding
的實現等功能。
App = Em.Application.create();
與所有的Ember.JS App都是一樣,首先要建立一個Application
。
App.PageView = Mov.PageVIEw.extend();
上面的代碼與通常的Ember.JS App在VIEw
(視圖)層的繼承不太一樣,主要區別在於全部的VIEw
(視圖)都繼承自Mov
,那麼Mov
是什麼?
Mov
定義在ember-bridge-jqm中,可以在assetswwwemberember-bridge-jqm.js中找到。為什麼Ember.JS不能直接與jQuery Mobile一起使用,而非要一個“代理”才行?
因為Ember.JS在實現的時候會修改Html代碼,因此具有“侵入性”。眾所周知jQuery Mobile也是一個“侵入性”很強的類庫,例如:它會把 <div data-role="header"></div>
這樣的代碼“改為”<div data-role="header" class="ui-header ui-bar-a" role="banner"></div>
。當這兩種“侵入性”的類庫放在一起,就會出現各種未知錯誤,所以才有ember-bridge-jqm.JS這樣的“代理”類庫。
通常一個典型的jQuery Mobile具有如下結構:
<div data-role="page"> <div data-role="header"></div> <div data-role="content"></div> <div data-role="footer"></div> </div>
加入ember-bridge-jqm.JS後,Html結構可以直接用JavaScript的方式實現:
App.PageView = Mov.PageView.extend(); App.HeaderView = Mov.HeaderView.extend(); App.FooterView = Mov.FooterView.extend(); App.ContentView = Mov.ContentVIEw.extend();
注:上述代碼分別實現了jQuery Mobile的 "page" "header" "content" "footer"視圖。
以上就是VIEw
(視圖)層的關鍵代碼,主要負責實現jQuery Mobile的結構,接下來看一下Model
(模型)的代碼。
定義如下數據結構(value Object)
:
App.Articles = Ember.Object.extend({ title : null, link : null, desc : null, creator : null, date : null });
上述結構與RSS源的XML節點保持一致。由於需要處理XML,因此增加一個叫做ServicesModel
的函數,功能是讀取XML,代碼如下:
App.ServicesModel = function( target, url ) { $.AJax({ type : "GET", url : url, success: function( XML ) { //get json var json = $.xml2JSon( XML ); //call json2obj App.Json2Obj( target, JSon.item ); } }); }
ServicesModel具有如下功能:
再看一下JSon2Obj
的實現:
App.JSon2Obj = function( target, tmp ) { target.set( 'content', [] ); $( tmp ).each( function( index, value ) { var tmp = App.Articles.create({ title : value.title, link : value.link, desc : value.description, creator : value.creator, date : value.date, }); target.pushObject( tmp ); }); }
JSon2Obj
具有如下功能:
VO(App.Articles.create())
。pushObject
將每個遍歷後的App.Articles(VO)
保存到target
(傳入的參數)。注:pushObject
是Ember.JS方法,與Ember.ArrayController
一同使用,將循環遍歷(each
)後的VO
(App.Articles
)保存到(pushObject
)ArrayController
的content
中。
以上就是Model
(模型)層的關鍵代碼,主要負責實現XML的讀取、解析和保存,接下來看一下Controller
(控制器)的代碼。
App.getArticlesController = Ember.ArrayController.create({ content : [], init : function () { //call services model App.ServicesModel( this, "http://feeds.adobe.com/XML/rss.cfm?query=byMostRecent&languages=5" ); } });
getArticlesController
具有如下功能:
App.getArticlesController = Ember.ArrayController.create();
Model
層的ServicesModel
,並傳入兩個參數:
pushObject
方法)以上就是JavaScript端MVC各層的關鍵代碼,代碼的實現與其他框架並無太大的區別,需要注意的是如何使用Ember.JS方式實現MVC結構的寫法:
Model:Ember.Object.extend();
View:Mov.PageVIEw.extend();
Controller:Ember.ArrayController.create();
接下來看一下Html端的Ember.js寫法,並展示Ember.JS有別於其他MVC框架的特點:UI Binding
。
<head>
標簽內:
<!-- jquery mobile --> <link rel="stylesheet" href="jqm/jquery.mobile-1.2.0.min.css" /> <!-- customer --> <link rel="stylesheet" href="stylesheets/style.CSS" />
<body>
標簽內:
<!-- common --> <script src="common/jquery-1.8.2.min.JS"></script> <script src="common/jquery.XML2json.js"></script> <!-- jquery mobile --> <script src="jqm/jquery.mobile-1.2.0.min.js"></script> <!-- ember.js --> <script src="ember/ember-0.9.8.1.min.js"></script> <script src="ember/ember-bridge-jqm.JS"></script> <!-- customer --> <script src="Javascripts/app.js"></script> <!-- phonegap --> <script src="phonegap/cordova-2.0.0.JS"></script>
注:之所以要把<script>
放到<body>
標簽中主要基於加快加載速度的考慮。
在<body>
中加入如下代碼:
<script type="text/x-handlebars" data-template-name="main"></script> <div data-role="page"></div>
注:data-template-name
的值一定要設定為"main"
在Javascripts/app.JS的VIEw
(視圖)層中加入如下代碼:
App.PageView = Mov.PageView.extend({ templateName:'main', id: 'page-vIEw', didInsertElement: function() { $.mobile.changePage(this.$()); } });
注:App.PageVIEw.templateName
和id
的值一定按照上述設定。
在Html端加入如下代碼:
{{#view App.HeaderView}} ..... {{/view}} {{#view App.ContentView}} ..... {{/view}} {{#view App.FooterView}} ..... {{/vIEw}}
這些代碼的結構等價於jQuery Mobile的結構:
<div data-role="header"></div> <div data-role="content"></div> <div data-role="footer"></div>
注:App.HeaderVIEw
、App.ContentVIEw
、App.FooterVIEw
定義在Javascripts/app.JS中,{{}}是模版引擎(Handlebars.JS)的語法。
在Javascripts/app.JS新增一個VIEw
(視圖)層,命名為ListVIEw
:
App.ListView = Mov.ListVIEw.extend();
在Html端加入如下代碼:(重點)
{{#view App.ContentView}} {{#collection App.ListVIEw contentBinding="App.getArticlesController"}} <a {{ bindAttr href="content.link" }} data-AJax="false" > <h3>{{ content.title }}</h3> <p>via {{ content.creator }}</p> <p>{{{ content.desc }}}</p> </a> {{/collection}} {{/vIEw}}
具有如下功能:
ContentVIEw
,並在其中加入ListVIEw
。ListVIEw
的contentBinding
設定為App.getArticlesController
,由於ListVIEw
是一個數組結構(App.getArticlesController = Ember.ArrayController.create();)
所以使用了關鍵字Collection,而非VIEw。content.title、content.creator、content.desc
來源於App.getArticlesController.content
,而App.getArticlesController.content
的值是調用pushObject
而來,通過App.JSon2Obj的代碼可知,content.title、content.creator、content.desc
分別存儲了解析XML後的App.Articles(Value Object)
值。注:bindAttr href="content.link"
並沒有寫成:<a href="{{ content.link }}">
,是因為Ember.JS在“注入”代碼的時候,會生成<script id="metamorph-0-start" type="text/x-placeholder">XXX</script>
這樣的結構,所以在設定href
時不能直接寫{{ content.link }}
,而是使用bindAttr
方式。
Embe.JS通過Collection
和pushObject
以及模版引擎(Handlebars.JS)來實現。
由於PhoneGap只起到打包(android App)作用,並沒有使用PhoneGap的相關功能,因此無需引入<script src="phonegap/cordova-2.0.0.JS"></script>
,直接打包即可,PhoneGap的過程略去。
Collection
和pushObject
以及模版引擎(Handlebars.JS)實現)