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 相关文章推荐
javascript 全选与全取消功能的实现代码
Dec 23 Javascript
深入理解JavaScript系列(30):设计模式之外观模式详解
Mar 03 Javascript
jquery实现浮动在网页右下角的彩票开奖公告窗口代码
Sep 04 Javascript
AngularJS内建服务$location及其功能详解
Jul 01 Javascript
Vue.js事件处理器与表单控件绑定详解
Mar 20 Javascript
vue2.0 根据状态值进行样式的改变展示方法
Mar 13 Javascript
vue iView 上传组件之手动上传功能
Mar 16 Javascript
详解angular路由高亮之RouterLinkActive
Apr 28 Javascript
快速解决vue动态绑定多个class的官方实例语法无效的问题
Sep 05 Javascript
Vue指令指令大全
Feb 09 Javascript
过滤器vue.filters的使用方法实现
Sep 18 Javascript
Vue实现可移动水平时间轴
Jun 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
《魔兽争霸3:重制版》翻车了?你想要的我们都没有
2019/11/07 魔兽争霸
支持php4、php5的mysql数据库操作类
2008/01/10 PHP
javascript 小型动画组件与实现代码
2010/06/02 PHP
微信 getAccessToken方法详解及实例
2016/11/23 PHP
php 开发中加密的几种方法总结
2017/03/22 PHP
php实现映射操作实例详解
2019/10/02 PHP
实用javaScript技术-屏蔽类
2006/08/15 Javascript
javascript实现的距离现在多长时间后的一个格式化的日期
2009/10/29 Javascript
在次封装easyui-Dialog插件实现代码
2010/11/14 Javascript
JS解析json数据并将json字符串转化为数组的实现方法
2012/12/25 Javascript
js使浏览器窗口最大化实现代码(适用于IE)
2013/08/07 Javascript
页面加载完后自动执行一个方法的js代码
2014/09/06 Javascript
BOM系列第三篇之定时器应用(时钟、倒计时、秒表和闹钟)
2016/08/17 Javascript
使用jQuery的toggle()方法对HTML标签进行显示、隐藏的方法(示例)
2016/09/01 Javascript
jqueryUI tab标签页代码分享
2017/10/09 jQuery
setTimeout时间设置为0详细解析
2018/03/13 Javascript
使用vue-infinite-scroll实现无限滚动效果
2018/06/22 Javascript
JS加密插件CryptoJS实现的DES加密示例
2018/08/16 Javascript
Vue filter 过滤当前时间 实现实时更新效果
2019/12/20 Javascript
解决pycharm双击但是无法打开的情况
2020/10/31 Javascript
[01:18:36]LGD vs VP Supermajor 败者组决赛 BO3 第一场 6.10
2018/07/04 DOTA
Python open()文件处理使用介绍
2014/11/30 Python
python编写Logistic逻辑回归
2020/12/30 Python
python导出chrome书签到markdown文件的实例代码
2017/12/27 Python
Python 正则表达式 re.match/re.search/re.sub的使用解析
2019/07/22 Python
Python爬取豆瓣视频信息代码实例
2019/11/16 Python
Python抓包程序mitmproxy安装和使用过程图解
2020/03/02 Python
python实现快递价格查询系统
2020/03/03 Python
Python实现多线程下载脚本的示例代码
2020/04/03 Python
HTML5 Canvas如何实现纹理填充与描边(Fill And Stroke)
2013/07/15 HTML / CSS
小学生节约用水倡议书
2014/05/15 职场文书
毕业生对母校寄语
2015/02/26 职场文书
离婚律师函范本
2015/05/27 职场文书
情人节单身感言
2015/08/03 职场文书
小学语文教师竞聘演讲稿范文
2019/08/09 职场文书
python 中[0]*2与0*2的区别说明
2021/05/10 Python