在这一步中,您将学到如何创建一个布局模板,并且学习怎样使用一个叫做ngRoute的Angular模块来构建一个具有多重视图的应用。

  ·当您现在访问/index.html,您将被重定向到/index.html#!/phones,电话列表会显示在浏览器中;

  ·当您点击一部电话的超链接,URL会改变至该指定电话,浏览器将展示一个简短的电话细节页面。

最大的不同列举如下,您可以点击这里在GitHub上查看全部的不同。

依赖

这一步中添加的路由功能是由Angular中的ngRoute模块提供的,该模块由核心的Angular框架独立出来。

由于我们使用Bower来安装客户端的依赖,该步中我们更新bower.json配置文件来添加新的依赖:

bower.json:

{
"name": "angular-phonecat",
"description": "A starter project for AngularJS",
"version": "0.0.0",
"homepage": "https://github.com/angular/angular-phonecat",
"license": "MIT",
"private": true,
"dependencies": {
"angular": "1.5.x",
"angular-mocks": "1.5.x",
"angular-route": "1.5.x",
"bootstrap": "3.3.x"
}
}

新的依赖"angular-route": "1.5.x"告诉bower去安装一个与Angular1.5.x版本兼容的angular-route模块版本。我们必须告诉bower去下载和安装这些依赖。

npm install

多重视图,路由和布局模板

我们的应用正在逐步变得复杂。在这一步之前,应用为我们的用户提供了一个单页视图(包括电话列表),并且所有的模板代码都位于phone-list.template.html文件下。构建应用的下一步,是添加一个能展示我们列表中每一部电话的细节的视图。

为了添加细节视图,我们将index.html转换成一个我们称之为“布局模板”的模板,一个于我们应用中所有视图都公共的模板。其他被包含在该布局模板中的“局部模板”依赖于当前的“路由”--当前展示给用户的视图。

Angular应用间的路由通过$routeProvider声明,这被$route服务所提供。该服务使得将控制器,视图模板和当前浏览器的URL捆绑起来变得容易。使用这点特性,我们可以实现深度链接,这使得我们可以充分利用浏览器的历史记录(前进和后退的导航)以及电子书签。

关于依赖注入,注入器和提供者的笔记

正如你注意到的,依赖注入在Angular中处于核心地位,所以对你来说深入了解它是很重要的。

在引导应用时,Angular创建一个注入器,这被用于找到和注入您应用中需要被用到的服务。注入器本身并不知道$http或$route这些服务做了什么。事实上,注入器甚至不知道这些服务的存在,除非它被适当的模块定义所配置。

注入器只执行了下面的步骤:
  ·加载您在应用中指定的模块定义;

  ·注册这些模块定义中定义的所有提供者;

  ·一旦被要求这么做,通过提供者,这作为可注入的函数中的参数,来懒惰式(lazily,需要时才加载)实例化服务和他们的依赖。

提供者是用来提供(创建)服务实例和对外配置API的对象,这可以被用来控制一个服务创建和运行时的行为。对于$route服务来说,$routeProvider提供API来允许您定义您应用中的路由。

(注意:提供者仅仅能被注入config函数,因此您不能在运行时将$routeProvider注入PhoneListController。)

Angular模块解决了从应用中移除全局变量的问题并且提供了配置注入器的方法。与AMD或require.js模块不同的是,Angular模块不会去试图解决脚本加载顺序或懒惰式脚本获取的问题。这些目标是完全独立的,并且每一个模块系统能并肩运作来实现他们的目标。

为了深入您对Angular依赖注入的理解,请看这里。

模板

$route服务经常和ngView指令结合使用。ngView指令扮演的角色是将当前路由的视图模板包含进布局模板。这使得其和我们的index.html完美契合。

app/index.html:

<head>
...
<script src="bower_components/angular/angular.js"></script>
<script src="bower_components/angular-route/angular-route.js"></script>
<script src="app.module.js"></script>
<script src="app.config.js"></script>
...
<script src="phone-detail/phone-detail.module.js"></script>
<script src="phone-detail/phone-detail.component.js"></script>
</head>
<body> <div ng-view></div> </body>

我们添加了4个额外的<script>标签来在我们的应用中加载额外的JavaScript文件:
  ·angular-route.js:定义了Angular的ngRoute模块,这为我们提供路由;

  ·app.config.js:为我们的主模块配置提供者(参见下文);

  ·phone-detail.module.js:定义了一个包含phoneDetail组件的新模块;

  ·phone-detail.component.js:定义了一个phoneDetail组件的模型(参见下文)。

注意到我们在index.html模板中移除了<phone-list></phone-list>一行并且用一个包含ng-view属性的div来替换它。

配置一个模块

模块的.config()方法为我们提供了获取用于配置的提供者的入口。为了在我们的应用中获取定义于ngRoute的提供者,服务和指令,我们需要在我们的phonecatApp模块中将ngRoute添加为一个依赖。

app/app.module.js:

angular.module('phonecatApp', [
'ngRoute',
...
]);

现在,除了核心的服务和指令,我们也能为我们的应用配置$route服务(使用其提供者)。为了能迅速定位配置代码,我们将其列为一个单独的文件并且添加.config后缀。

app/app.config.js:

angular.
module('phonecatApp').
config(['$locationProvider', '$routeProvider',
function config($locationProvider, $routeProvider) {
$locationProvider.hashPrefix('!'); $routeProvider.
when('/phones', {
template: '<phone-list></phone-list>'
}).
when('/phones/:phoneId', {
template: '<phone-detail></phone-detail>'
}).
otherwise('/phones');
}
]);

通过使用.config()方法,我们请求将必要的提供者( 比如$routeProvider)注入到我们的配置函数,然后使用它们的方法来制定对应服务的行为。在这里,我们使用$routeProvider.when()$routeProvider.otherwise()方法来指定我们的应用路由。

我们的路由定义如下:

  ·when('/phones'):决定将被展示的视图,当URL的哈希片段为/phones。通过指定的模板,Angular会创建一个phoneList组件的实例来管理视图。注意到这和我们在index.html使用的是相同的标记。

  ·when('/phones/:phoneId'):决定将被展示的视图,当URL的哈希片段为/phones/<phoneId>, <phoneId>是URL中变化的部分。管理phoneDetail组件中的视图。

  ·otherwise('/phones'):定义一个指向的回退路由,当没有被定义的路由于当前URL匹配。(这里会指向/phones)。

我们复用了我们已经构建的phoneList组件和一个新的“模型”phoneDetail组件。到目前为止,phoneDetail组件仅仅会展示选中电话的ID。(不是那么令人印象深刻,我们会在下一步中扩展它)。

注意到:phoneId作为路由声明的第二个参数,$route服务使用路由声明--'/phones/:phoneId'--作为一个与当前URL相匹配的模板。所有用:前缀定义的变量都被提取进入了$routeParams对象。

phoneDetail组件

我们创建了一个phoneDetail组件来处理电话细节视图。我们遵循和phoneList一样的惯例:使用一个独立的模块来创建phoneDetail模块,这在我们的phonecatApp模块中被添加为依赖。

app/phone-detail/phone-detail.module.js:

angular.module('phoneDetail', [
'ngRoute'
]);

app/phone-detail/phone-detail.component.js:

angular.
module('phoneDetail').
component('phoneDetail', {
template: 'TBD: Detail view for <span>{{$ctrl.phoneId}}</span>',
controller: ['$routeParams',
function PhoneDetailController($routeParams) {
this.phoneId = $routeParams.phoneId;
}
]
});

app/app.module.js:

angular.module('phonecatApp', [
...
'phoneDetail',
...
]);

一点关于子模块依赖的笔记

phoneDetail模块依赖于ngRoute模块,以此来提供$routeParams对象,这被用于phoneDetail组件的控制器中。由于ngRoute也是主模块phonecatApp中的依赖,其服务和指令在整个应用中都是可获取的(包括phoneDetail组件)。

这意味着及时我们不为phoneDetail组件的依赖列表中引入ngRoute,我们的应用依然可以正常工作。虽然删除子模块中哪些已经在主模块中引入的依赖听上去还不错,但这却损害了我们来之不易的模块化。

此处的额外知识是:

  ·永远清楚描述一个子模块的所有依赖。不要依赖于任何继承于父模块的依赖。(因为父模块可能哪天就不见了。)

总结

随着路由的建立和电话列表视图的实现,让我们进入下一步来实现一个正确的电话细节列表。

[Angular Tutorial] 9 -Routing & Multiple Views的更多相关文章

  1. Routing(路由) & Multiple Views(多个视图) step 7

    Routing(路由) & Multiple Views(多个视图) step 7 1.切换分支到step7,并启动项目 git checkout step-7 npm start 2.需求: ...

  2. [Angular Tutorial]PhoneCat Tutorial App

    (注:曾经在<不敢止步>一书中看到学到一个观点,作者认为学习一门技术最好的方法就是翻译某部领域书籍.这里我决定做一次尝试,接下来花1个月左右时间,将Angular Tutorial Pho ...

  3. AngularJS学习---Routing(路由) & Multiple Views(多个视图) step 7

    1.切换分支到step7,并启动项目 git checkout step- npm start 2.需求: 在步骤7之前,应用只给我们的用户提供了一个简单的界面(一张所有手机的列表),并且所有的模板代 ...

  4. [Angular Tutorial] 3-Components

    在先前的步骤中,我们看到了一个控制器和一个模板如何一起工作来将一个静态的HTML文件转化为动态页面(view).一般说来,这在单页应用中一种非常常见的模式(在Angular应用中尤其是这样): ·客户 ...

  5. [Angular Tutorial] 7-XHRs & Dependency Injection

    我们受够了在应用中用硬编码的方法嵌入三部电话!现在让我们用Angular内建的叫做$http的服务来从我们的服务器获取更大的数据集吧.我们将会使用Angular的依赖注入来为PhoneListCtrl ...

  6. [Angular Tutorial] 0-Bootstraping

    在这一节的tutorial中,您将会逐渐熟悉AngularJS phonecat app的最重要的源代码文件.您也将学到如何将开发服务器与angular-seed绑定到一起,并且在浏览器中运行应用. ...

  7. angular 2 - 004 routing 路由

    https://angular.io/tutorial/toh-pt5 定义一个模块用来定义路由 src/app/app-routing.module.ts import { NgModule } f ...

  8. [Angular 2] Pipes with Multiple Parameters

    Showing how to set up a Pipe that takes multiple updating inputs for multiple Component sources. imp ...

  9. [Angular Tutorial] 14 -Animations

    在这一步中,我们将会通过在我们先前创建的模板代码中添加CSS和JavaScript动画效果来扩展我们的web应用. ·我们现在使用ngAnimate模块来允许动画效果贯穿整个应用. ·我们也依赖于自带 ...

随机推荐

  1. boost之词法解析器spirit

    摘要:解析器就是编译原理中的语言的词法分析器,可以按照文法规则提取字符或者单词.功能:接受扫描器的输入,并根据语法规则对输入流进行匹配,匹配成功后执行语义动作,进行输入数据的处理. C++ 程序员需要 ...

  2. BASE64-客户端(js)加码-服务器端(c#)解码,URL传递“汉字参数”解决方法

    HttpUtility.UrlEncode 在 Encode 的时候, 将空格转换成加号('+'), 在 Decode 的时候将加号转为空格, 但是浏览器是不能理解加号为空格的, 所以如果文件名包含了 ...

  3. Android蓝牙传感应用(转)

    源:http://www.cnblogs.com/xiaochao1234/p/3753538.html Android手机一般以客户端的角色主动连接SPP协议设备(接上蓝牙模块的数字传感器),连接流 ...

  4. I2C死锁原因及解决方法(转)

    源:http://blog.csdn.net/zyboy2000/article/details/5603091 死锁总线表现为:SCL为高,SDA一直为低 现象:单片机采用硬件i2c读取E2PROM ...

  5. C#入门经典(第五章-2)字符串的处理

  6. 将自己apk打包进其他apk安装思路

    把Apk 作为asset 资源编译进去,运行的时候释放出来再安装. dexclassloader加载. 动态加载. 把一个apk写成安装器,默认安装的话,需要root或者需要漏洞利用.

  7. 磁盘寻道时间算法之----------------SCAN算法和最短寻道时间优先调度算法

    若干个等待访问磁盘者依次要访问的柱面编号为:80,40,74,14,60,31,61,假设每移动一个柱面需要4毫秒时间,移动到当前位于35号柱面,且当前的移动方向向柱面号增加的方向.请计算: (1)若 ...

  8. How to create a zip file in NetSuite SuiteScript 2.0 如何在现有SuiteScript中创建和下载ZIP压缩文档

    Background We all knows that: NetSuite filecabinet provided a feature to download a folder to a zip ...

  9. mysql的一些特殊命令

    mysql命令行工具的编辑技巧 从mysql performace blog 中学到的: 1. pager 例子 mysql> pager more PAGER set to 'more' my ...

  10. rsa or dsa?

    http://www.linuxquestions.org/questions/linux-security-4/which-is-better-rsa-or-dsa-public-key-12593 ...