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 相关文章推荐
jQuery EasyUI API 中文文档 - ProgressBar 进度条
Sep 29 Javascript
JS获取url链接字符串 location.href
Dec 23 Javascript
常规表格多表头查询示例
Feb 21 Javascript
Node.js 学习笔记之简介、安装及配置
Mar 03 Javascript
使用JavaScript判断手机浏览器是横屏还是竖屏问题
Aug 02 Javascript
BootStrap table删除指定行的注意事项(笔记整理)
Feb 05 Javascript
JavaScript验证知识整理
Mar 24 Javascript
基于JavaScript实现选项卡效果
Jul 21 Javascript
layui 设置table 行的高度方法
Aug 17 Javascript
jquery判断滚动条距离顶部的距离方法
Sep 05 jQuery
Vue中Table组件Select的勾选和取消勾选事件详解
Mar 19 Javascript
js实现整体缩放页面适配移动端
Mar 31 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
PHP初学者头疼问题总结
2006/10/09 PHP
PHP结合jQuery.autocomplete插件实现输入自动完成提示的功能
2015/04/27 PHP
jquery的Tooltip插件 qtip使用详细说明
2010/09/08 Javascript
JavaScript 基础篇之运算符、语句(二)
2012/04/07 Javascript
JS案例分享之金额小写转大写
2014/05/15 Javascript
AngularJS语法详解(续)
2015/01/23 Javascript
js仿黑客帝国字母掉落效果代码分享
2020/11/08 Javascript
解决jquery中动态新增的元素节点无法触发事件问题的两种方法
2015/10/30 Javascript
原生JavaScript实现异步多文件上传
2015/12/02 Javascript
ES6中非常实用的新特性介绍
2016/03/10 Javascript
微信小程序开发之圆形菜单 仿建行圆形菜单实例
2016/12/12 Javascript
vue使用axios实现文件上传进度的实时更新详解
2017/12/20 Javascript
在vue项目中,使用axios跨域处理
2018/03/07 Javascript
玩转vue的slot内容分发
2018/09/22 Javascript
Nodejs中使用puppeteer控制浏览器中视频播放功能
2019/08/26 NodeJs
vue中input的v-model清空操作
2019/09/06 Javascript
解决vue一个页面中复用同一个echarts组件的问题
2020/07/19 Javascript
浅谈vue获得后台数据无法显示到table上面的坑
2020/08/13 Javascript
Vue 集成 PDF.js 实现 PDF 预览和添加水印的步骤
2021/01/22 Vue.js
用Python写的图片蜘蛛人代码
2012/08/27 Python
python实现的一只从百度开始不断搜索的小爬虫
2013/08/13 Python
python动态网页批量爬取
2016/02/14 Python
Python列表推导式与生成器表达式用法示例
2018/02/08 Python
对pandas数据判断是否为NaN值的方法详解
2018/11/06 Python
详解Django项目中模板标签及模板的继承与引用(网站中快速布置广告)
2019/03/27 Python
numpy 声明空数组详解
2019/12/05 Python
Python中的全局变量如何理解
2020/06/04 Python
Python 转移文件至云对象存储的方法
2021/02/07 Python
英国50岁以上人群的交友网站:Ourtime
2018/03/28 全球购物
导游实习生自荐书
2014/01/28 职场文书
王力宏牛津大学演讲稿
2014/05/22 职场文书
运动员获奖感言
2014/08/15 职场文书
党的群众路线教育实践活动对照检查材料(教师)
2014/09/24 职场文书
派出所副所长四风问题个人整改措施思想汇报
2014/10/13 职场文书
2016年安康杯竞赛活动总结
2016/04/05 职场文书
yyds什么意思?90后已经听不懂00后讲话了……
2022/02/03 杂记