所有javascript
随着HTML 5的兴起,基于此的项目也愈来愈的多,于是出现了不少MVC框架,如:Backbone.js、Ember.js、Angular.js等,此系列将阐述这些框架在代码层面的区别,但愿能够给初学者一些思路。css
本文将介绍Ember.js构建基于jQuery Mobile的PhoneGap项目(Hybird App)。html
使用Ember.js + jQuery Mobile + PhoneGap构建一个Hybird App项目,命名为Adobe Reader,具备以下功能:html5
下面将展现Adobe Reader的关键代码,并分别介绍Ember.js中M(Model)V(View)C(Controller)
代码特色、UI Binding
的实现等功能。java
App = Em.Application.create();
jquery
与全部的Ember.js App都是同样,首先要创建一个Application
。git
App.PageView = Mov.PageView.extend();
github
上面的代码与一般的Ember.js App在View
(视图)层的继承不太同样,主要区别在于所有的View
(视图)都继承自Mov
,那么Mov
是什么?ajax
Mov
定义在ember-bridge-jqm中,能够在assets\www\ember\ember-bridge-jqm.js中找到。为何Ember.js不能直接与jQuery Mobile一块儿使用,而非要一个“代理”才行?json
由于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)实现)
引用地址:http://www.adobe.com/cn/devnet/html5/articles/ember-jquery-phonegap.html