现在,我们经常都可以看到复杂的JavaScript应用程序,由于这些应用程序变得越来越复杂,一长串的jQuery回调语句或者通过应用程序在各个状态执行不同的函数调用,这些做法都会变得无法再让人接受,这导致了JavaScript开发人员开始寻找一种组织和效率更优秀的开发方式。

实现组织和效率的其中一个最常用的架构模式,就是我们熟知的Model View Controller (MVC)模式,这种模式鼓励开发人员将其应用程序的不同部分分割为更易于管理的模块,我们不必使用一个函数直接调用数据库,通过创建了一个Model(模型或实体)来管理数据库;通过模板(Template)或视图(View)来简化显示代码; 最后,通过使用控制器(Controller)来处理我们的应用程序的请求,MVC模式尽量降低每个模块之间的耦合度,提供程序的开发效率。

我们熟知的Javascript MVC框架有:Ember.js、Backbone.js、Knockout.js、Spine.js、Batman.js 和 Angular.js等。

图1 Javascript MVC framework

通过上图,我们我们可以清楚地了解Javascript MVC框架之间的特性,复杂度和学习曲线的区别,从左到右我们了解到各个Javascript MVC框架是否支持数据绑定(Data Binding)、模板(Templating)和持久化等特性,从下到上MVC框架的复杂性递增,说实话我并没有去对比每个框架之间的优劣,如果大家有做过相关的对比或看过有关的文章也不吝赐教。

在接下来的博文中,我们将介绍Ember.js的使用。

目录

1.1.2 正文

Ember.js是一个JavaScript的MVC框架,它由Apple前雇员创建的SproutCore 2.0改名进化而来,Ember已经发布到1.0.0-RC.3

MVC定义

在介绍Ember之前,首先让我们回顾一下MVC模式,下面我们讲通过一个例子介绍MVC模式在程序设计中的作用,例如:

1. 用户执行一个操作,比如敲击键盘或单击鼠标按钮。

2. 控制器(Controller)接收输入并触发一个消息给模型(Model)。

3. 模型根据消息修改其内容(CRUD操作)。

4. 视图(View)监视模型中的变更,并将相应地更新呈现到用户界面中。

通过上面,我们了解到MVC中各个部件之间的作用和联系,在了解 MVC 模式的工作方式后,我们可以更加明确是否需要在我们的项目中引入Javascript的MVC框架。

在构建Ember应用程序时,我们会使用到六个主要部件:应用程序(Application)、模型(Model)、视图(View)、模板(Template)、路由(Routing)和控制器(Controller)。

接下来,我们将通过实现一个具体的程序来介绍Ember的使用。

设置Ember

首先,我们需要引用一系列Javascript库,所以我们在程序中添加js文件,并且把以下js文件保存到该文件夹中:

ember.js: ember 1.0.0-rc.3

ember-data.js: revision 12

handlebars.js: handlebars 1.2.rc.3

jquery.js: jQuery 1.9.1

app.js: 我们的应用程代码

上面,我们把一系列的js文件保存到了本地中,当然我们也可以通过使用CDN(内容分发网络)来获取相应Javascript库,接下来,让我们创建index.html页面。

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="description" content="" />
<meta name="description" content="" />
<meta name="keywords" content="" />
<meta name="author" content="" />
<title></title>
<link rel="stylesheet" href="" type="text/css" />
<link rel="stylesheet" href="" type="text/css" />
</head>
<body>
<!-- Add Javascript libs Reference -->
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="js/libs/handlebars-1.0.0-rc.3.js"></script>
<script src="js/libs/ember-1.0.0-rc.2.js"></script>
<script src="js/libs/ember-data.js"></script>
<script src="js/app.js"></script>
</body>
</html>

现在,我们已经实现了第一个Ember程序,但是它还没有具体功能,接下来,我们将给程序添加功能。

模板(Handlebars)

Ember.js使用的是Handlebars模板引擎,在我们开始使用之前,首先让我们先简单介绍一下handlebars.js;如果大家有使用过jQuery模板或其他脚本模板,那么对于掌握handlebars.js的使用就没有太大的困难了,如果确实没有使用过也不用担心,因为handlebars.js的使用也是挺简单的。

它让开发人员可以混合原始HTML和Handlebars表达式生成渲染相应的HTML;表达式以包括在{{}}中,我们可以通过两种方法把Handlebars模板加载到页面中,我们可以直接内嵌的html页面中,通过在页面中添加类型为text/x-handlebars的脚本标记内;或保存到以handlebars或hbs为后缀的文件中,然后通过Ember.js加载到页面中。

为了简单起见,我们把Handlebars脚本直接嵌入到index.html页面中。

<script type="text/x-handlebars" data-template-name="application">
<h1>Employee System</h1>
{{outlet}}
</script>

上面,我们定义了模板application,并且添加了Handlebars表达式{{outlet}},它的作用就类似一个占位符,告诉Ember这里的内容要动态地加载到页面当中,当我们在浏览器中打开index页面并没有显示模板中的信息。

应用程序(Application)

这是由于我们还没有定义Ember程序,每个Ember应用程序都需要一个Ember应用程序实例,接下来让我们在app.js中创建第一个Ember应用程序实例吧!

首先,我们创建一个Ember应用程序实例,具体实现如下:

// Creates an application instance.
App = Ember.Application.create();

上面,我们定义了一个名为 App 的Ember应用程序,当然我们可以把程序命名为任意的,但有一点我们要注意的是Ember要求变量的名称都以大写字母开头。

现在,我们在浏览器中打开页面,可以显示模板加载的信息了。

图2 Index页面

也许有人会问Ember怎么知道哪些模板需要加载呢?更重要的一点是,我们并没有告诉Ember要加载的模板名称,我们只是直接把模板application嵌入到页面中。

其实,这里有个“潜规则”:如果我们没有定义ApplicationView(应用程序视图),那么Ember会自动生成一个ApplicationView并且默认加载名为application的模板,假设,我们把模板重命名为application1,那么默认的ApplicationView将找不到要加载的模板。

当然,我们也可以通过定义ApplicationView来指定需要加载的模板名称,具体实现如

// Defines an application view, then loading
// relative templates.
App.ApplicationView = Ember.View.extend({
templateName: 'application1'
});

现在,我们还有一个疑问就是表达式{{outlet}}中的内容该如何加载显示呢?

路由(Routing)

由于{{outlet}}的内容是根据路由选择后动态获取的模板内容,所以我们先介绍Ember程序的路由,它可以帮助管理应该程序的状态和用户导航所需资源的资源;当我们的应用程序启动时,路由是负责显示模板,加载数据,以及管理应用程序的状态。

现在,我们通过指定URL方式义来定义应用程序的路由,具体定义如下:

// Defines a goal routing home and
// the detail information of employee routing.
App.Router.map(function() {
this.route("home", {path: "/"});
this.route("employee", {path: "/employee/:employee_id"});
});

上面,我们定义了两个路由分别是:应用程序的全局路由home和employee,在index页面进行加载同时访问home路由的模板,数据和应用程序状态;而employee路由将根据employee_id访问每个一个员工的基本信息。

接下来,我们定义home模板,具体实现如下:

<script type="text/x-handlebars" data-template-name="home">
<h3>Employee Information</h3>
<ul>
{{#each item in employeeInfo}}
<li>item</li>
{{each}}
</ul>
</script>

上面,我们定义了home模板,并且使用了each表达来迭代访问employeeInfo对象中的元素,这时我们又有一个疑问了,那就是employeeInfo对象从哪里获取呢?

前面,我们提到Controller负责从Model中获取数据,然后通过模板加载显示,那么我们可以通过显市定义Controller来获取数据,如果我们不定义的话,Ember会自动生成一个HomeController。

// Defines a custom controll.
App.HomeController = Ember.Controller.extend({
employeeInfo: ['Jackson Huang', 'Ada Li', 'JK Rush']
});

上面,我们自定义了HomeController并且初始化了employeeInfo数组,现在我们刷新一下index页面。

图3 Index页面

现在,我们又有一个疑问了,假如,我们程序有很多资源要访问,那么我们是否都显式地定义Controller呢?

其实,我们还可以通过定义路由控制器实现自动选择控制器,而且Ember会自动生成相应的Controller无需我们编写任何代码,具体实现如下:

  // Defines a routing handler.
App.HomeRoute = Ember.Route.extend({
model: function(){
return ['Jackson Huang', 'Ada Li', 'JK Rush'];
},
setupController: function(controller, model){
controller.set('content', model)
}
});

现在,我们定义了路由控制器App.HomeRoute并且重写了方法setupController,它接收路由处理程序匹配的控制器作为第一个参数即HomeController,接着我们给HomeController传递model参数,那么HomeController就可以获取相应的数据并且加载到模板中显示了。

上面,我们成功把数据加载到页面中,但是数据都是直接hardcode在Controller中,我们并没有定义Model来获取数据。

接下来,我们将实现从Fixtures中获取数据,这时我们需要使用ember-data.js库,具体实现如下。

// Customs a store.
App.Store = DS.Store.extend({
// Notify the version of ember data api used.
revision: 12, // Used FixtureAdapter.
adapter: 'DS.FixtureAdapter'
});

上面,我们在app.js中定义DS.Store的子类App.Store,并且申明我们程序使用Ember data api的版本是12,当api版本更新或使用的版本太旧时,ember-data.js就会返回相应的错误信息。

例如:当前的ember-data.js版本是12,如果我们在app.js中定义使用的是版本1的api,在控制台中我们就会看到以下的错误信息。

图4 ember-data.js版本信息

模型(Model)

模型是一个用来表示应用程序数据的对象,它可能是一个简单的数组或通过RESTful API动态检索的数据;ember-data.js提供加载、映射和更新应用程序模型的API。

ember-data.js为每个应用程序都提供存储空间,存储空间负责保持已加载的Model和检索还未加载的Model。

前面,我们定义了应用程序App,现在,需要给程序提供数据也就是员工信息,所以我们要创建程序的模型(实体)Employee,接下来我们将实现模型的定义。

// Defines a employee model.
App.Employee = DS.Model.extend({
name: DS.attr('string'),
department: DS.attr('string'),
title: DS.attr('string')
})

上面,我们定义了Employee模型,它继承了DS.Model并且包含三个字段分别是name,department和title。

接下来,我们通过定义App.Employee.FIXTURES,模拟从服务器端获取数据。

// Defines a JSON array.
App.Employee.FIXTURES = [
{
id: 1,
name: 'Jackson Huang',
department: 'IT',
title: 'programmer'
},
{
id: 2,
name: 'Ada Chen',
department: 'purchasing',
title: 'buyer'
},
{
id: 3,
name: 'JK Rush',
department: 'IT',
title: 'programmer'
},
{
id: 4,
name: 'Lucy Liu',
department: 'IT',
title: 'tester'
},
{
id: 5,
name: 'Julia Liu',
department: 'HR',
title: 'Manager'
}
];

上面,我们定义了JSON数组App.Employee.FIXTURES,它包含了一系列员工的基本信息。

接下来,我们修改home和添加employee模板,具体实现如下:

<!-- Home temp START -->
<script type="text/x-handlebars" data-template-name="home">
<h3>
Employee Information</h3>
<ul>
{{#each item in content}}
<li>{{item}}</li>
{{/each}}
</ul>
<h3>
Employee</h3>
<ul>
{{#each employee in employees}} {{#linkTo "employee" employee}}
<p>
{{employee.name}}
</p>
{{/linkTo}} {{/each}}
</ul>
</script>
<!-- Home temp END --> <!-- Employee temp START -->
<script type="text/x-handlebars" data-template-name="employee">
<div>
<h3>
Name: {{name}}</h3>
<p>
Department: {{department}}
</p>
<p>
Title: {{title}}
</p>
{{#linkTo home class='btn btn-primary'}}Back{{/linkTo}}
</div>
</script>
<!-- Employee temp END -->

在home模板中,我们添加each表达式迭代访问employee元素,然后通过linkTo选择employee路由;然后根据路由选择在employee模板显示相应的员工信息。

图5 程序页面

现在,我们完成了Employee程序的基本功能了,提供用户查下员工的信息了。

1.1.3 总结

本文通过Demo例子介绍了Ember的使用,主要介绍了Ember的模型,控制器、模板和路由,由于Ember是Javascript MVC框架,而且作为初学者很容易困惑于它的自动生成和默认规则,所以我极力推荐大家要仔细看一遍RoutingController的官方文档。

我们通过介绍Ember的Handlerbars模板引擎,定义了Demo程序的页面,然后通过路由控制器定义路由行为,根据路由行为选择控制器,控制器负责数据加载和显示。但我们的例子中还没有设计的Ember视图模块,如果想进一步学习请参考官方文档或书籍。

参考

http://www.cnblogs.com/rush/archive/2013/04/29/3051191.html

【JavsScript】Ember.js的更多相关文章

  1. 【前端】Ember.js学习笔记

    Model 在默认情况下,model钩子返回的值,会设置为关联的控制器的model属性.例如,如果App.PostsRoute通过model钩子返回了一个对象,这个对象会设置为App.PostsCon ...

  2. 【jQuery】 实用 js

    [jQuery] 实用 js 1. int 处理 parseInt(") // int 转换 isNaN(page) // 判断是否是int类型 2. string 处理 // C# str ...

  3. 【转】WdatePicker.js的使用方法 帮助文档 使用说明 如何使用

    [转]WdatePicker.js的使用方法 帮助文档 使用说明 如何使用 日期控件支持平面显示功能,只要设置一下eCont属性就可以把它当作日历来使用了,无需触发条件,直接显示在页面上 示例2-1 ...

  4. 【02】Node.js 安装配置(OK)

    [02] Node.js 安装配置 本章节我们将向大家介绍在window和Linux上安装Node.js的方法. Node.js安装包及源码下载地址为:http://www.nodejs.org/do ...

  5. 【转】Angular.js VS. Ember.js:谁将成为Web开发的新宠?

    本文源自于Quora网站的一个问题,作者称最近一直在为一个新的Rails项目寻找一个JavaScript框架,通过筛选,最终纠结于 Angular.js和 Ember.js. 这个问题获得了大量的关注 ...

  6. 【转】three.js详解之入门篇

    原文链接:https://www.cnblogs.com/shawn-xie/archive/2012/08/16/2642553.html   开场白 webGL可以让我们在canvas上实现3D效 ...

  7. 【Nodejs】Node.js(Express)の環境構築

    [Express]の環境 参考URL:http://expressjs.com/en/starter/generator.html ①Node.jsの準備 (参考URL:https://www.cnb ...

  8. 【ES6】改变 JS 内置行为的代理与反射

    代理(Proxy)可以拦截并改变 JS 引擎的底层操作,如数据读取.属性定义.函数构造等一系列操作.ES6 通过对这些底层内置对象的代理陷阱和反射函数,让开发者能进一步接近 JS 引擎的能力. 一.代 ...

  9. 【转】Vue.js:轻量高效的前端组件化方案

    摘要:Vue.js通过简洁的API提供高效的数据绑定和灵活的组件系统.在前端纷繁复杂的生态中,Vue.js有幸受到一定程度的关注,目前在GitHub上已经有5000+的star.本文将从各方面对Vue ...

随机推荐

  1. div模拟的下拉框特效

    随笔- 4 文章- 0 评论- 0 ? <style type="text/css"> body, ul, li { margin: 0; padding: 0; fo ...

  2. 【quick-cocos2d-x】Lua 面向对象(OOP)编程与元表元方法

    版权声明:本文为博主原创文章,转载请注明出处. 面向对象是一种对现实世界理解和抽象的方法,是计算机编程技术发展到一定阶段后的产物. 早期的计算机编程是基于面向过程的方法,通过设计一个算法就可以解决当时 ...

  3. JQuery中的事件以及动画

    .bind事件 <script src="script/jquery-1.7.1.min.js"></script> <script> $(fu ...

  4. ASP.NET MVC 常用内置验证特性 简介

    1.[Required] : 必须输入 [Required(ErrorMessage = "请输入用户名")] 2.[StringLength] : 限制字符串长度 [String ...

  5. navicat 或者workbench 无法连接127.0.0.1(61)的解决方法

    1.输入mysql -uroot 进入命令行模式, 2.输入"show variables like '%sock%';"查看sock文件所在位置 如: 3.配置客户端(以navi ...

  6. HDU5875:Function

    题目链接: Function 分析: icpccamp里的方法不会,我用了一个nex[]数组存储当前点ai需要取模的下一个点aj的编号j,如果aj>ai,就不用遍历. 时间为920ms 代码: ...

  7. var隐式类型

    var dogName = "ruiky"; 1.[编译器]会在编译时自动根据值的类型推断这个变量的类型:       2.变量类型不可更改:因为声明的时候已经确定类型了. 3.可 ...

  8. MacTerminal快捷键

    [MacTerminal快捷键] 在Mac系统中并没有Home.End等键,所以在使用时并不是特别的顺手,但是有几个键位组合可以使Terminal的操作更加灵活方便. 1.将光标移动到行首:ctrl ...

  9. devexpress皮肤设置

    DEV的皮肤管理控件:SkinController: TdxSkinController; 皮肤设置:SkinController.SkinName := appInfo.SkinName; TdxR ...

  10. 编译安装-Percona 5.6

    CentOS 6.4编译安装Percona 5.6.16 Percona 下载地址:http://www.percona.com 一.卸载原有MySQL检查是否安装有MySQL Server: rpm ...