混合式框架-AngularJS

简单介绍

  AngularJS是为了克服HTML在构建应用上的不足而设计的。HTML是一门非常好的为静态文本展示设计的声明式语言,但要构建WEB应用的话它就显得乏力了。所以我做了一些工作(你也能够认为是小花招)来让浏览器做我想要的事。
  通常。我们是通过下面技术来解决静态网页技术在构建动态应用上的不足:
  类库 - 类库是一些函数的集合,它能帮助你写WEB应用。起主导作用的是你的代码,由你来决定何时使用类库。

类库有:jQuery等

  框架 - 框架是一种特殊的、已经实现了的WEB应用,你仅仅须要对它填充详细的业务逻辑。这里框架是起主导作用的,由它来依据详细的应用逻辑来调用你的代码。

框架有:knockout、sproutcore等。

  AngularJS使用了不同的方法,它尝试去补足HTML本身在构建应用方面的缺陷。AngularJS通过使用我们称为标识符(directives)的结构,让浏览器可以识别新的语法。比如:
  使用双大括号{{}}语法进行数据绑定。
  使用DOM控制结构来实现迭代或者隐藏DOM片段。
  支持表单和表单的验证;
  能将逻辑代码关联到相关的DOM元素上。
  能将HTML分组成可重用的组件。

端对端的解决方式

  AngularJS试图成为WEB应用中的一种端对端的解决方式。这意味着它不仅仅是你的WEB应用中的一个小部分,而是一个完整的端对端的解决方式。

这会让AngularJS在构建一个CRUD(添加Create、查询Retrieve、更新Update、删除Delete)的应用时显得非常“固执”(原文为 opinionated,意指没有太多的其它方式)。可是,虽然它非常“固执”,它仍然能确保它的“固执”仅仅是在你构建应用的起点,而且你仍能灵活变动。AngularJS的一些出众之处例如以下:

  构建一个CRUD应用可能用到的所有内容包含:数据绑定、基本模板标识符、表单验证、路由、深度链接、组件重用、依赖注入。
  測试方面包含:单元測试、端对端測试、模拟和自己主动化測试框架。
  具有文件夹布局和測试脚本的种子应用作为起点。

AngularJS的可爱之处

  AngularJS通过为开发人员呈现一个更高层次的抽象来简化应用的开发。如同其它的抽象技术一样,这也会损失一部分灵活性。

换句话说。并非全部的应用都适合用AngularJS来做。

AngularJS主要考虑的是构建CRUD应用。

幸运的是,至少90%的WEB应用都是CRUD应用。可是要了解什么适合用AngularJS构建,就得了解什么不适合用AngularJS构建。

  如游戏,图形界面编辑器,这样的DOM操作非常频繁也非常复杂的应用,和CRUD应用就有非常大的不同。它们不适合用AngularJS来构建。像这样的情况用一些更轻量、简单的技术如jQuery可能会更好。

实例

AngularJS下载地址

http://angularjs.org

Hello World!

開始学习AngularJS的一个好方法是创建经典应用程序“Hello World!”:

  1. 使用您喜爱的文本编辑器,创建一个HTML文件,比如:helloworld.html。
  2. 将以下的源码拷贝到您的HTML文件。
  3. 在web浏览器中打开这个HTML文件。

源码

<!doctype html>
<html ng-app>
    <head>
        <script src="http://code.angularjs.org/angular-1.0.1.min.js"></script>
    </head>
    <body>
        Hello {{'World'}}!
    </body>
</html>

请在您的浏览器中执行以上代码查看效果。

如今让我们细致看看代码。看看究竟怎么回事。 当载入该页时,标记ng-app告诉AngularJS处理整个HTML页并引导应用:

<html ng-app>

这行加载AngularJS脚本:

<script src="http://code.angularjs.org/angular-1.0.1.min.js"></script>

(想了解AngularJS处理整个HTML页的细节,请看Bootstrap。

最后,标签中的正文是应用的模板。在UI中显示我们的问候语:

Hello {{'World'}}!

注意,使用双大括号标记{{}}的内容是问候语中绑定的表达式,这个表达式是一个简单的字符串‘World’。

以下。让我们看一个更有趣的样例:使用AngularJS对我们的问候语文本绑定一个动态表达式。

Hello AngularJS World!

本演示样例演示AngularJS的双向数据绑定(bi-directional data binding):

  1. 编辑前面创建的helloworld.html文档。
  2. 将以下的源码拷贝到您的HTML文件。
  3. 刷新浏览器窗体。

源码

<!doctype html>
<html ng-app>
    <head>
        <script src="http://code.angularjs.org/angular-1.0.1.min.js"></script>
    </head>
    <body>
        Your name: <input type="text" ng-model="yourname" placeholder="World">
        <hr>
        Hello {{yourname || 'World'}}!
    </body>
</html>

请在您的浏览器中执行以上代码查看效果。

该演示样例有一下几点重要的注意事项:

  • 文本输入指令<input ng-model="yourname" />绑定到一个叫yourname的模型变量。
  • 双大括号标记将yourname模型变量加入到问候语文本。
  • 你不须要为该应用另外注冊一个事件侦听器或加入事件处理程序!

如今试着在输入框中键入您的名称。您键入的名称将马上更新显示在问候语中。 这就是AngularJS双向数据绑定的概念。 输入框的不论什么更改会马上反映到模型变量(一个方向),模型变量的不论什么更改都会马上反映到问候语文本中(还有一方向)。

AngularJS应用的解析

本节描写叙述AngularJS应用程序的三个组成部分,并解释它们怎样映射到模型-视图-控制器设计模式:

模板(Templates)

模板是您用HTML和CSS编写的文件。展现应用的视图。 您可给HTML加入新的元素、属性标记,作为AngularJS编译器的指令。 AngularJS编译器是全然可扩展的。这意味着通过AngularJS您能够在HTML中构建您自己的HTML标记。

应用程序逻辑(Logic)和行为(Behavior)

应用程序逻辑和行为是您用JavaScript定义的控制器。AngularJS与标准AJAX应用程序不同。您不须要另外编写侦听器或DOM控制器。由于它们已经内置到AngularJS中了。这些功能使您的应用程序逻辑非常easy编写、測试、维护和理解。

模型数据(Data)

模型是从AngularJS作用域对象的属性引申的。模型中的数据可能是Javascript对象、数组或基本类型。这都不重要,重要的是。他们都属于AngularJS作用域对象。

AngularJS通过作用域来保持数据模型与视图界面UI的双向同步。

一旦模型状态发生改变,AngularJS会马上刷新反映在视图界面中。反之亦然。

此外,AngularJS还提供了一些很实用的服务特性:

  1. 底层服务包含依赖注入,XHR、缓存、URL路由和浏览器抽象服务。
  2. 您还能够扩展和加入自己特定的应用服务。
  3. 这些服务能够让您很方便的编写WEB应用。

引导程序

我们如今開始准备编写AngularJS应用——phonecat。这一步骤(步骤0)。您将会熟悉重要的源码文件,学习启动包括AngularJS种子项目的开发环境。并在浏览器端执行应用。

  1. 进入angular-phonecat文件夹。执行例如以下命令:

    git checkout -f step-0

    该命令将重置phonecat项目的工作文件夹,建议您在每一学习步骤执行此命令,将命令中的数字改成您学习步骤相应的数字,该命令将清除您在工作文件夹内做的不论什么更改。

  2. 执行下面命令:

    node scripts/web-server.js

    来启动server,启动后命令行终端将会提示Http Server running at http://localhost:8000,请不要关闭该终端。关闭该终端即关闭了server。在浏览器中输入http://localhost:8000/app/index.html来訪问我们的phonecat应用。

如今。在浏览器中您应该已经看到了我们的初始应用。非常easy,但说明我们的项目已经能够执行了。

应用中显示的“Nothing here yet!”是由例如以下HTML代码构建而成。代码中包括了AngularJS的关键元素,正是我们须要学习的。

app/index.html

<!doctype html>
<html lang="en" ng-app>
<head>
    <meta charset="utf-8">
    <title>My HTML File</title>
    <link rel="stylesheet" href="css/app.css">
    <link rel="stylesheet" href="css/bootstrap.css">
    <script src="lib/angular/angular.js"></script>
</head>
<body>
<p>Nothing here {{'yet' + '!'}}</p>
</body>
</html>

代码在做什么呢?

ng-app指令:

<html lang="en" ng-app>

ng-app指令标记了AngularJS脚本的作用域,在<html>中加入ng-app属性即说明整个<html>都是AngularJS脚本作用域。

开发人员也能够在局部使用ng-app指令,如<div ng-app>。则AngularJS脚本仅在该<div>中执行。

AngularJS脚本标签:

<script src="lib/angular/angular.js"></script>

这行代码加载angular.js脚本,当浏览器将整个HTML页面加载完成后将会执行该angular.js脚本,angular.js脚本执行后将会寻找含有ng-app指令的HTML标签,该标签即定义了AngularJS应用的作用域。

双大括号绑定的表达式:

<p>Nothing here {{'yet' + '!'}}</p>

这行代码演示了AngularJS模板的核心功能——绑定,这个绑定由双大括号{{}}和表达式'yet' + '!'组成。

这个绑定告诉AngularJS须要运算当中的表达式并将结果插入DOM中。接下来的步骤我们将看到,DOM能够随着表达式运算结果的改变而实时更新。

AngularJS表达式Angular expression是一种类似于JavaScript的代码片段。AngularJS表达式仅在AngularJS的作用域中执行,而不是在整个DOM中执行。

引导AngularJS应用

通过ngApp指令来自己主动引导AngularJS应用是一种简洁的方式。适合大多数情况。在高级开发中。比如使用脚本装载应用,您也能够使用bootstrap手动引导AngularJS应用。

AngularJS应用引导过程有3个重要点:

  1. 注入器(injector)将用于创建此应用程序的依赖注入(dependency injection);
  2. 注入器将会创建根作用域作为我们应用模型的范围。
  3. AngularJS将会链接根作用域中的DOM。从用ngApp标记的HTML标签開始。逐步处理DOM中指令和绑定。

一旦AngularJS应用引导完成。它将继续侦听浏览器的HTML触发事件,如鼠标点击事件、按键事件、HTTP传入响应等改变DOM模型的事件。这类事件一旦发生。AngularJS将会自己主动检測变化,并作出对应的处理及更新。

上面这个应用的结构很easy。

该模板包仅含一个指令和一个静态绑定,当中的模型也是空的。下一步我们尝试稍复杂的应用!

img_tutorial_00

我工作文件夹中这些文件是干什么的?

上面的应用来自于AngularJS种子项目,我们通常能够使用AngularJS种子项目来创建新项目。种子项目包括最新的AngularJS代码库、測试库、脚本和一个简单的应用程序演示样例,它包括了开发一个典型的web应用程序所需的基本配置。

对于本教程,我们对AngularJS种子项目进行了下列更改:

  1. 删除演示样例应用程序;
  2. 加入手机图像到app/img/phones/;
  3. 加入手机数据文件(JSON)到app/phones/;
  4. 加入Twitter Bootstrap文件到app/css/ 和app/img/。

练习

试试把关于数学运算的新表达式加入到index.html:

<p>1 + 2 = {{ 1 + 2 }}</p>

静态模板

为了说明angularJS怎样增强了标准HTML,我们先将创建一个静态HTML页面模板。然后把这个静态HTML页面模板转换成能动态显示的AngularJS模板。

在本步骤中。我们往HTML页面中加入两个手机的基本信息,用下面命令将工作文件夹重置到步骤1。

git checkout -f step-1

请编辑app/index.html文件,将以下的代码加入到index.html文件里,然后执行该应用查看效果。

app/index.html

<ul>
    <li>
        <span>Nexus S</span>
        <p>
        Fast just got faster with Nexus S.
        </p>
    </li>
    <li>
        <span>Motorola XOOM™ with Wi-Fi</span>
        <p>
        The Next, Next Generation tablet.
        </p>
    </li>
</ul>

练习

尝试加入多个静态HTML代码到index.html, 比如:

<p>Total number of phones: 2</p>

AngularJS 模板

是时候给这些网页来点动态特性了——用AngularJS!我们这里为后面要加入的控制器加入了一个測试。

一个应用的代码架构有非常多种。

对于AngularJS应用。我们鼓舞使用模型-视图-控制器(MVC)模式解耦代码和分离关注点。

考虑到这一点。我们用AngularJS来为我们的应用加入一些模型、视图和控制器。

请重置工作文件夹:

git checkout -f step-2

我们的应用如今有了一个包括三部手机的列表。

步骤1和步骤2之间最重要的不同在以下列出。。你能够到GitHub去看完整的区别。

视图和模板

在AngularJS中,一个视图是模型通过HTML**模板**渲染之后的映射。这意味着。不论模型什么时候发生变化,AngularJS会实时更新结合点,随之更新视图。

比方,视图组件被AngularJS用以下这个模板构建出来:

<html ng-app>
<head>
  ...
  <script src="lib/angular/angular.js"></script>
  <script src="js/controllers.js"></script>
</head>
<body ng-controller="PhoneListCtrl">

  <ul>
    <li ng-repeat="phone in phones">
      {{phone.name}}
    <p>{{phone.snippet}}</p>
    </li>
  </ul>
</body>
</html>

我们刚刚把静态编码的手机列表替换掉了。由于这里我们使用ngRepeat指令和两个用花括号包裹起来的AngularJS表达式——{{phone.name}}{{phone.snippet}}——能达到相同的效果。

  • <li>标签里面的ng-repeat="phone in phones"语句是一个AngularJS迭代器。

    这个迭代器告诉AngularJS用第一个<li>标签作为模板为列表中的每一部手机创建一个<li>元素。

  • 正如我们在第0步时学到的,包裹在phone.namephone.snippet周围的花括号标识着数据绑定。

    和常量计算不同的是,这里的表达式实际上是我们应用的一个数据模型引用。这些我们在PhoneListCtrl控制器里面都设置好了。

tutorial_02.png

模型和控制器

PhoneListCtrl控制器里面初始化了数据模型(这里仅仅只是是一个包括了数组的函数。数组中存储的对象是手机数据列表):

app/js/controller.js:

function PhoneListCtrl($scope) {
  $scope.phones = [
    {"name": "Nexus S",
     "snippet": "Fast just got faster with Nexus S."},
    {"name": "Motorola XOOM™ with Wi-Fi",
     "snippet": "The Next, Next Generation tablet."},
    {"name": "MOTOROLA XOOM™",
     "snippet": "The Next, Next Generation tablet."}
  ];
}

虽然控制器看起来并没有起到什么控制的作用,可是它在这里起到了至关关键的数据。

通过给定我们数据模型的语境,控制器同意我们建立模型和视图之间的数据绑定。

我们是这样把表现层。数据和逻辑部件联系在一起的:

  • PhoneListCtrl——控制器方法的名字(在JS文件controllers.js中)和<body>标签里面的ngController指令的值相匹配。
  • 手机的数据此时与注入到我们控制器函数的作用域$scope)相关联。当应用启动之后。会有一个根作用域被创建出来,而控制器的作用域是根作用域的一个典型后继。

    这个控制器的作用域对全部<body ng-controller="PhoneListCtrl">标记内部的数据绑定有效。

AngularJS的作用域理论很重要:一个作用域能够视作模板、模型和控制器协同工作的粘接器。

AngularJS使用作用域,同一时候还有模板中的信息。数据模型和控制器。这些能够帮助模型和视图分离,可是他们两者确实是同步的!不论什么对于模型的更改都会即时反映在视图上。不论什么在视图上的更改都会被立马体如今模型中。

想要更加深入理解AngularJS的作用域,请參看AngularJS作用域文档

測试

“AngularJS方式”让开发时代码測试变得十分简单。

让我们来瞅一眼以下这个为控制器新加入的单元測试:

test/unit/controllersSpec.js:

describe('PhoneCat controllers', function() {

  describe('PhoneListCtrl', function(){

    it('should create "phones" model with 3 phones', function() {
      var scope = {},
      ctrl = new PhoneListCtrl(scope);

      expect(scope.phones.length).toBe(3);
    });
  });
});

这个測试验证了我们的手机数组里面有三条记录(临时无需弄明确这个測试脚本)。

这个样例显示出为AngularJS的代码创建一个单元測试是多么的easy。

正由于測试在软件开发中是不可缺少的环节,所以我们使得在AngularJS能够轻易地构建測试。来鼓舞开发人员多写它们。

在写測试的时候。AngularJS的开发人员倾向于使用Jasmine行为驱动开发(BBD)框架中的语法。虽然AngularJS没有强迫你使用Jasmine,可是我们在教程里面全部的測试都使用Jasmine编写。

你能够在Jasmine的官方主页或者Jasmine Wiki上获得相关知识。

基于AngularJS的项目被预先配置为使用JsTestDriver来执行单元測试。你能够像以下这样执行測试:

  1. 在一个单独的终端上。进入到angular-phonecat文件夹而且执行./scripts/test-server.sh来启动測试(Windows命令行下请输入.scripts est-server.bat来执行脚本,后面脚本命令执行方式类似);
  2. 打开一个新的浏览器窗体,而且转到http://localhost:9876 。
  3. 选择“Capture this browser in strict mode”。

    这个时候,你能够抛开你的窗体无论然后把这事忘了。JsTestDriver会自己把測试跑完而且把结果输出在你的终端里。

  4. 执行./scripts/test.sh进行測试 。

    你应当看到类似于例如以下的结果:

    Chrome: Runner reset.
    .
    Total 1 tests (Passed: 1; Fails: 0; Errors: 0) (2.00 ms)
      Chrome 19.0.1084.36 Mac OS: Run 1 tests (Passed: 1; Fails: 0; Errors 0) (2.00 ms)

    耶。測试通过了!或者没有... 注意:假设在你执行測试之后发生了错误。关闭浏览器然后回到终端关了脚本,然后在又一次来一边上面的步骤。

练习

  • index.html加入还有一个数据绑定。

    比如:

    <p>Total number of phones: {{phones.length}}</p>
  • 创建一个新的数据模型属性,而且把它绑定到模板上。比如:

    $scope.hello = "Hello, World!"

    更新你的浏览器。确保显示出来“Hello, World!”

  • 用一个迭代器创建一个简单的表:

    <table>
        <tr><th>row number</th></tr>
        <tr ng-repeat="i in [0, 1, 2, 3, 4, 5, 6, 7]"><td>{{i}}</td></tr>
    </table>

    如今让数据模型表达式的i添加1:

    <table>
        <tr><th>row number</th></tr>
        <tr ng-repeat="i in [0, 1, 2, 3, 4, 5, 6, 7]"><td>{{i+1}}</td></tr>
    </table>
  • 确定把toBe(3)改成toBe(4)之后单元測试失败。然后又一次跑一遍./scripts/test.sh脚本

迭代器过滤

我们在上一步做了非常多基础性的训练,所以如今我们能够来做一些简单的事情喽。

我们要增加全文检索功能(没错,这个真的非常easy!)。

同一时候。我们也会写一个端到端測试。由于一个好的端到端測试能够帮上非常大忙。它监视着你的应用,而且在发生回归的时候迅速报告。

请重置工作文件夹:

git checkout -f step-3

我们的应用如今有了一个搜索框。

注意到页面上的手机列表随着用户在搜索框中的输入而变化。

步骤2和步骤3之间最重要的不同在以下列出。

你能够在GitHub里看到完整的区别。

控制器

我们对控制器不做不论什么改动。

模板

app/index.html

<div class="container-fluid">
  <div class="row-fluid">
    <div class="span2">
      <!--Sidebar content-->

      Search: <input ng-model="query">

    </div>
    <div class="span10">
      <!--Body content-->

      <ul class="phones">
        <li ng-repeat="phone in phones | filter:query">
          {{phone.name}}
        <p>{{phone.snippet}}</p>
        </li>
      </ul>

       </div>
  </div>
</div>

我们如今加入了一个<input>标签,而且使用AngularJS的$filter函数来处理ngRepeat指令的输入。

这样同意用户输入一个搜索条件,立马就能看到对电话列表的搜索结果。我们来解释一下新的代码:

  • 数据绑定: 这是AngularJS的一个核心特性。当页面载入的时候。AngularJS会依据输入框的属性值名字。将其与数据模型中同样名字的变量绑定在一起。以确保两者的同步性。

    在这段代码中,用户在输入框中输入的数据名字称作query,会立马作为列表迭代器(phone in phones | filter:query`)其过滤器的输入。当数据模型引起迭代器输入变化的时候,迭代器能够高效得更新DOM将数据模型最新的状态反映出来。

img_tutorial_03

  • 使用filter过滤器:filter函数使用query的值来创建一个仅仅包括匹配query记录的新数组。

    ngRepeat会依据filter过滤器生成的手机记录数据数组来自己主动更新视图。

    整个过程对于开发人员来说都是透明的。

測试

在步骤2,我们学习了编写和执行一个測试的方法。

单元測试用来測试我们用js编写的控制器和其它组件都很方便,可是不能方便的对DOM操作和应用集成进行測试。

对于这些来说,端到端測试是一个更好的选择。

搜索特性是全然通过模板和数据绑定实现的。所以我们的第一个端到端測试就来验证这些特性是否符合我们的预期。

test/e2e/scenarios.js:

describe('PhoneCat App', function() {

  describe('Phone list view', function() {

    beforeEach(function() {
      browser().navigateTo('../../app/index.html');
    });


    it('should filter the phone list as user types into the search box', function() {
      expect(repeater('.phones li').count()).toBe(3);

      input('query').enter('nexus');
      expect(repeater('.phones li').count()).toBe(1);

      input('query').enter('motorola');
      expect(repeater('.phones li').count()).toBe(2);
    });
  });
});

虽然这段測试代码的语法看起来和我们之前用Jasmine写的单元測试很像。可是端到端測试使用的是AngularJS端到端測试器提供的接口。

执行一个端到端測试。在浏览器新标签页中打开以下随意一个:

这个測试验证了搜素框和迭代器被正确地集成起来。

你能够发现,在AngularJS里写一个端到端測试多么的简单。虽然这个样例不过一个简单的測试。可是用它来构建不论什么一个复杂、可读的端到端測试都非常easy。

练习

  • index.html模板中加入一个{{query}}绑定来实时显示query模型的当前值,然后观察他们是怎样依据输入框中的值而变化。
  • 如今我们来看一下我们怎么让query模型的值出如今HTML的页面标题上。

    你也许觉得像以下这样在title标签上加上一个绑定即可了:

    <title>Google Phone Gallery: {{query}}</title>

    可是,当你重载页面的时候,你根本没办法得到期望的结果。这是由于query模型只在body元素定义的作用域内才有效。

    <body ng-controller="PhoneListCtrl">

    假设你想让<title>元素绑定上query模型,你必须把ngController声明移动HTML元素上,由于它是titlebody元素的共同祖先。

    <html ng-app ng-controller="PhoneListCtrl">

    一定要注意把body元素上的ng-controller声明给删了。

    当绑定两个花括号在title元素上能够实现我们的目标。可是你也许发现了,页面正载入的时候它们已经显示给用户看了。一个更好的解决方式是使用ngBind或者ngBindTemplate指令。它们在页面载入时对用户是不可见的:

    <title ng-bind-template="Google Phone Gallery: {{query}}">Google Phone Gallery</title>
  • test/e2e/scenarios.jsdescribe块中增加以下这些端到端測试代码:

    it('should display the current filter value within an element with id "status"',
        function() {
            expect(element('#status').text()).toMatch(/Current filter: s*$/);
            input('query').enter('nexus');
            expect(element('#status').text()).toMatch(/Current filter: nexuss*$/);
            //alternative version of the last assertion that tests just the value of the binding
            using('#status').expect(binding('query')).toBe('nexus');
    });

    刷新浏览器,端到端測试器会报告測试失败。为了让測试通过。编辑index.html,加入一个id为“status”div或者p元素。内容是一个query绑定。再加上Current filter:前缀。

    比如:

    <div id="status">Current filter: {{query}}</div>
  • 在端到端測试里面加一条pause();语句,又一次跑一遍。你将发现測试器暂停了!这样同意你有机会在測试执行过程中查看你应用的状态。測试应用是实时的!

    你能够更换搜索内容来证明。稍有经验你就会知道,这对于在端到端測试中迅速找到问题是多么的关键。

双向绑定

在这一步你会添加一个让用户控制手机列表显示顺序的特性。动态排序能够这样实现。加入一个新的模型属性,把它和迭代器集成起来,然后让数据绑定完毕剩下的事情。

请重置工作文件夹:

git checkout -f step-4

你应该发现除了搜索框之外,你的应用多了一个下来菜单,它能够同意控制电话排列的顺序。

步骤3和步骤4之间最重要的不同在以下列出。你能够在GitHub里看到完整的区别。

模板

app/index.html

Search: <input ng-model="query">
Sort by:
<select ng-model="orderProp">
  <option value="name">Alphabetical</option>
  <option value="age">Newest</option>
</select>


<ul class="phones">
  <li ng-repeat="phone in phones | filter:query | orderBy:orderProp">
    {{phone.name}}
    <p>{{phone.snippet}}</p>
  </li>
</ul>

我们在index.html中做了例如以下更改:

  • 首先,我们添加了一个叫做orderProp<select>标签,这样我们的用户就能够选择我们提供的两种排序方法。

img_tutorial_04

  • 然后,在filter过滤器后面加入一个orderBy过滤器用其来处理进入迭代器的数据。

    orderBy过滤器以一个数组作为输入。复制一份副本,然后把副本重排序再输出到迭代器。

AngularJS在select元素和orderProp模型之间创建了一个双向绑定。而后,orderProp会被用作orderBy过滤器的输入。

正如我们在步骤3中讨论数据绑定和迭代器的时候所说的一样。不管什么时候数据模型发生了改变(比方用户在下拉菜单中选了不同的顺序),AngularJS的数据绑定会让视图自己主动更新。

没有不论什么笨拙的DOM操作!

控制器

app/js/controllers.js:

function PhoneListCtrl($scope) {
  $scope.phones = [
    {"name": "Nexus S",
     "snippet": "Fast just got faster with Nexus S.",
     "age": 0},
    {"name": "Motorola XOOM™ with Wi-Fi",
     "snippet": "The Next, Next Generation tablet.",
     "age": 1},
    {"name": "MOTOROLA XOOM™",
     "snippet": "The Next, Next Generation tablet.",
     "age": 2}
  ];

  $scope.orderProp = 'age';
}
  • 我们改动了phones模型—— 手机的数组 ——为每个手机记录其添加了一个age属性。我们会依据age属性来对手机进行排序。
  • 我们在控制器代码里加了一行让orderProp的默认值为age。假设我们不设置默认值。这个模型会在我们的用户在下拉菜单选择一个顺序之前一直处于未初始化状态。

    如今我们该好好谈谈双向数据绑定了。注意到当应用在浏览器中载入时,“Newest”在下拉菜单中被选中。

    这是由于我们在控制器中把orderProp设置成了‘age’。所以绑定在从我们模型到用户界面的方向上起作用——即数据从模型到视图的绑定。如今当你在下拉菜单中选择“Alphabetically”,数据模型会被同一时候更新,而且手机列表数组会被又一次排序。这个时候数据绑定从还有一个方向产生了作用——即数据从视图到模型的绑定。

原文地址:https://www.cnblogs.com/claireyuancy/p/6956276.html