AngularJS入门教程之路由与多视图详解


Posted in Javascript onAugust 19, 2016

在这一步,你将学习如何创建一个布局模板并且通过路由功能来构建一个具有多个视图的应用。

请重置工作目录:

git checkout -f step-7

注意到现在当你转到app/index.html时,你会被重定向到app/index.html#/phones并且相同的手机列表在浏览器中显示了出来。当你点击一个手机链接时,一个手机详细信息列表也被显示了出来。

步骤6和步骤7之间最重要的不同在下面列出。你可以在GitHub里看到完整的差别。

多视图,路由和布局模板

我们的应用正慢慢发展起来并且变得逐渐复杂。在步骤7之前,应用只给我们的用户提供了一个简单的界面(一张所有手机的列表),并且所有的模板代码位于index.html文件中。下一步是增加一个能够显示我们列表中每一部手机详细信息的页面。

为了增加详细信息视图,我们可以拓展index.html来同时包含两个视图的模板代码,但是这样会很快给我们带来巨大的麻烦。相反,我们要把index.html模板转变成“布局模板”。这是我们应用所有视图的通用模板。其他的“局部布局模板”随后根据当前的“路由”被充填入,从而形成一个完整视图展示给用户。

AngularJS中应用的路由通过$routeProvider来声明,它是$route服务的提供者。这项服务使得控制器、视图模板与当前浏览器的URL可以轻易集成。应用这个特性我们就可以实现深链接,它允许我们使用浏览器的历史(回退或者前进导航)和书签。

关于依赖注入(DI),注入器(Injector)和服务提供者(Providers)

正如从前面你学到的,依赖注入是AngularJS的核心特性,所以你必须要知道一点这家伙是怎么工作的。

当应用引导时,AngularJS会创建一个注入器,我们应用后面所有依赖注入的服务都会需要它。这个注入器自己并不知道$http和$route是干什么的,实际上除非它在模块定义的时候被配置过,否则它根本都不知道这些服务的存在。注入器唯一的职责是载入指定的服务模块,在这些模块中注册所有定义的服务提供者,并且当需要时给一个指定的函数注入依赖(服务)。这些依赖通过它们的提供者“懒惰式”(需要时才加载)实例化。

提供者是提供(创建)服务实例并且对外提供API接口的对象,它可以被用来控制一个服务的创建和运行时行为。对于$route服务来说,$routeProvider对外提供了API接口,通过API接口允许你为你的应用定义路由规则。

AngularJS模块解决了从应用中删除全局状态和提供方法来配置注入器这两个问题。和AMD或者require.js这两个模块(非AngularJS的两个库)不同的是,AngularJS模块并没有试图去解决脚本加载顺序以及懒惰式脚本加载这样的问题。这些目标和AngularJS要解决的问题毫无关联,所以这些模块完全可以共存来实现各自的目标。

App 模块

app/js/app.js

angular.module('phonecat', []).
 config(['$routeProvider', function($routeProvider) {
 $routeProvider.
   when('/phones', {templateUrl: 'partials/phone-list.html',  controller: PhoneListCtrl}).
   when('/phones/:phoneId', {templateUrl: 'partials/phone-detail.html', controller: PhoneDetailCtrl}).
   otherwise({redirectTo: '/phones'});
}]);

为了给我们的应用配置路由,我们需要给应用创建一个模块。我们管这个模块叫做phonecat,并且通过使用configAPI,我们请求把$routeProvider注入到我们的配置函数并且使用$routeProvider.whenAPI来定义我们的路由规则。

注意到在注入器配置阶段,提供者也可以同时被注入,但是一旦注入器被创建并且开始创建服务实例的时候,他们就不再会被外界所获取到。

我们的路由规则定义如下

当URL 映射段为/phones时,手机列表视图会被显示出来。为了构造这个视图,AngularJS会使用phone-list.html模板和PhoneListCtrl控制器。

当URL 映射段为/phone/:phoneId时,手机详细信息视图被显示出来。这里:phoneId是URL的变量部分。为了构造手机详细视图,AngularJS会使用phone-detail.html模板和PhoneDetailCtrl控制器。
我们重用之前创造过的PhoneListCtrl控制器,同时我们为手机详细视图添加一个新的PhoneDetailCtrl控制器,把它存放在app/js/controllers.js文件里。

$route.otherwise({redirectTo: '/phones'})语句使得当浏览器地址不能匹配我们任何一个路由规则时,触发重定向到/phones。

注意到在第二条路由声明中:phoneId参数的使用。$route服务使用路由声明/phones/:phoneId作为一个匹配当前URL的模板。所有以:符号声明的变量(此处变量为phones)都会被提取,然后存放在$routeParams对象中。

为了让我们的应用引导我们新创建的模块,我们同时需要在ngApp指令的值上指明模块的名字:

app/index.html

<!doctype html>
<html lang="en" ng-app="phonecat">
...

控制器

app/js/controllers.js

...
function PhoneDetailCtrl($scope, $routeParams) {
 $scope.phoneId = $routeParams.phoneId;
}

//PhoneDetailCtrl.$inject = ['$scope', '$routeParams'];

模板

$route服务通常和ngView指令一起使用。ngView指令的角色是为当前路由把对应的视图模板载入到布局模板中。

app/index.html

<html lang="en" ng-app="phonecat">
<head>
...
 <script src="lib/angular/angular.js"></script>
 <script src="js/app.js"></script>
 <script src="js/controllers.js"></script>
</head>
<body>

 <div ng-view></div>

</body>
</html>

注意,我们把index.html模板里面大部分代码移除,我们只放置了一个<div>容器,这个<div>具有ng-view属性。我们删除掉的代码现在被放置在phone-list.html模板中:

app/partials/phone-list.html

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

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

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

   <ul class="phones">
    <li ng-repeat="phone in phones | filter:query | orderBy:orderProp" class="thumbnail">
     <a href="#/phones/{{phone.id}}" class="thumb"><img ng-src="{{phone.imageUrl}}"></a>
     <a href="#/phones/{{phone.id}}">{{phone.name}}</a>
     <p>{{phone.snippet}}</p>
    </li>
   </ul>

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

同时我们为手机详细信息视图添加一个占位模板。

app/partials/phone-detail.html

TBD: detail view for {{phoneId}}

注意到我们的布局模板中没再添加PhoneListCtrl或PhoneDetailCtrl控制器属性!

测试

为了自动验证所有的东西都良好地集成起来,我们需要写一些端到端测试,导航到不同的URL上然后验证正确地视图被渲染出来。

...
 it('should redirect index.html to index.html#/phones', function() {
  browser().navigateTo('../../app/index.html');
  expect(browser().location().url()).toBe('/phones');
 });
...

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

  beforeEach(function() {
   browser().navigateTo('../../app/index.html#/phones/nexus-s');
  });


  it('should display placeholder page with phoneId', function() {
   expect(binding('phoneId')).toBe('nexus-s');
  });
 });

你现在可以刷新你的浏览器,然后重新跑一遍端到端测试,或者你可以在AngularJS的服务器上运行一下。

练习

试着在index.html上增加一个{{orderProp}}绑定,当你在手机列表视图上时什么也没变。这是因为orderProp模型仅仅在PhoneListCtrl管理的作用域下才是可见的,这与<div ng-view>元素相关。如果你在phone-list.html模板中加入同样的绑定,那么这个绑定会按你设想的那样被渲染出来。

总结

设置路由并实现手机列表视图之后,我们已经可以进入步骤8来实现手机详细信息视图了。

以上就对AngularJS 路由和多视图的资料整理,后续继续补充相关知识,谢谢大家对本站的支持!

Javascript 相关文章推荐
js确认删除对话框效果的示例代码
Feb 20 Javascript
使用jQuery设置disabled属性与移除disabled属性
Aug 21 Javascript
js获取内联样式的方法
Jan 27 Javascript
JQ技术实现注册页面带有校验密码强度
Jul 27 Javascript
使用CoffeeScrip优美方式编写javascript代码
Oct 28 Javascript
js停止冒泡和阻止浏览器默认行为的简单方法
May 15 Javascript
微信小程序 教程之wxapp 视图容器 view
Oct 19 Javascript
jQuery实现模糊查询的方法分析
May 10 jQuery
jQuery实现验证用户登录
Dec 10 jQuery
Node.js API详解之 os模块用法实例分析
May 06 Javascript
微信小程序实现选项卡滑动切换
Oct 22 Javascript
elementui实现预览图片组件二次封装
Dec 29 Javascript
AngularJS入门教程之链接与图片模板详解
Aug 19 #Javascript
AngularJS之依赖注入模拟实现
Aug 19 #Javascript
AngularJS入门教程之XHR和依赖注入详解
Aug 18 #Javascript
JavaScript中函数声明与函数表达式的区别详解
Aug 18 #Javascript
Javascript中apply、call、bind的巧妙使用
Aug 18 #Javascript
AngularJS入门教程之双向绑定详解
Aug 18 #Javascript
AngularJS入门教程之迭代器过滤详解
Aug 18 #Javascript
You might like
PHP4之COOKIE支持详解
2006/10/09 PHP
第十三节--对象串行化
2006/11/16 PHP
PHP编程中八种常见的文件操作方式
2006/11/19 PHP
php实现文件下载更能介绍
2012/11/23 PHP
Codeigniter注册登录代码示例
2014/06/12 PHP
在Ubuntu 14.04上部署 PHP 环境及 WordPress
2014/09/02 PHP
PHP实现找出链表中环的入口节点
2018/01/16 PHP
PHP实现QQ、微信和支付宝三合一收款码实例代码
2018/02/19 PHP
php实现 master-worker 守护多进程模式的实例代码
2019/07/20 PHP
Ext javascript建立超链接,进行事件处理的实现方法
2009/03/22 Javascript
jQuery实现的类flash菜单效果代码
2010/05/17 Javascript
js判断背景图片是否加载成功使用img的width实现
2013/05/29 Javascript
JS中的数组的sort方法使用示例
2014/01/22 Javascript
巧用jquery解决下拉菜单被Div遮挡的相关问题
2014/02/13 Javascript
举例详解JavaScript中Promise的使用
2015/06/24 Javascript
jQuery animate和CSS3相结合实现缓动追逐效果附源码下载
2016/04/18 Javascript
connection reset by peer问题总结及解决方案
2016/10/21 Javascript
js原生Ajax的封装和原理详解
2017/03/11 Javascript
JS数组实现分类统计实例代码
2018/09/30 Javascript
vue2.0 路由模式mode=&quot;history&quot;的作用
2018/10/18 Javascript
JavaScript使用Math.random()生成简单的验证码
2019/01/21 Javascript
详解Vue3.0 前的 TypeScript 最佳入门实践
2019/06/18 Javascript
安装多版本Vue-CLI的实现方法
2020/03/24 Javascript
[01:00:44]DOTA2上海特级锦标赛主赛事日 - 3 败者组第三轮#1COL VS Alliance第三局
2016/03/04 DOTA
python使用PyV8执行javascript代码示例分享
2013/12/04 Python
python使用datetime模块计算各种时间间隔的方法
2015/03/24 Python
利用python提取wav文件的mfcc方法
2019/01/09 Python
如何使用python爬虫爬取要登陆的网站
2019/07/12 Python
python pycharm的安装及其使用
2019/10/11 Python
numpy创建单位矩阵和对角矩阵的实例
2019/11/29 Python
Python super()方法原理详解
2020/03/31 Python
布里斯班女装时尚品牌:Adrift
2017/12/28 全球购物
Farnell德国:电子元器件供应商
2018/07/10 全球购物
Cynthia Rowley官网:全球领先的生活方式品牌
2020/10/27 全球购物
投资协议书范本
2014/04/21 职场文书
敬老院志愿者活动总结
2015/05/06 职场文书