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动态调整iframe高度的方法
Mar 06 Javascript
在jquery中处理带有命名空间的XML数据
Jun 13 Javascript
JS中的构造函数详细解析
Mar 10 Javascript
js实现可兼容IE、FF、Chrome、Opera及Safari的音乐播放器
Feb 11 Javascript
完美解决jQuery fancybox ie 无法显示关闭按钮的问题
Nov 29 Javascript
微信小程序 解析网页内容详解及实例
Feb 22 Javascript
EasyUI的DataGrid绑定Json数据源的示例代码
Dec 16 Javascript
详解vue中组件参数
Jul 09 Javascript
jquery.param()实现数组或对象的序列化方法
Oct 08 jQuery
微信小程序实现多图上传
Jun 19 Javascript
elementUI同一页面展示多个Dialog的实现
Nov 19 Javascript
JS实现数组去重的11种方法总结
Apr 04 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
在IIS上安装PHP4.0正式版
2006/10/09 PHP
thinkphp框架实现删除和批量删除
2016/06/29 PHP
javascript 多级checkbox选择效果
2009/08/20 Javascript
JavaScript函数获取事件源的小例子
2014/05/14 Javascript
jquery简单图片切换显示效果实现方法
2015/01/14 Javascript
jquery读取xml文件实现省市县三级联动的方法
2015/05/29 Javascript
JS实现的随机排序功能算法示例
2017/06/09 Javascript
纯js实现页面返回顶部的动画(超简单)
2017/08/10 Javascript
JQuery用$.ajax或$.getJSON跨域获取JSON数据的实现代码
2017/09/23 jQuery
微信小程序自定义底部弹出框
2020/11/16 Javascript
浅析vue 函数配置项watch及函数 $watch 源码分享
2018/11/22 Javascript
mpvue 单文件页面配置详解
2018/12/02 Javascript
微信小程序模板template简单用法示例
2018/12/04 Javascript
详解vue开发中调用微信jssdk的问题
2019/04/16 Javascript
vscode中eslint插件的配置(prettier配置无效)
2019/09/10 Javascript
vue scroll滚动判断的实现(是否滚动到底部、滚动方向、滚动节流、获取滚动区域dom元素)
2020/06/11 Javascript
Python获取当前路径实现代码
2017/05/08 Python
python selenium 对浏览器标签页进行关闭和切换的方法
2018/05/21 Python
利用Python半自动化生成Nessus报告的方法
2019/03/19 Python
利用Python实现kNN算法的代码
2019/08/16 Python
PyQt5中QTableWidget如何弹出菜单的示例代码
2020/02/23 Python
selenium+超级鹰实现模拟登录12306
2021/01/24 Python
全球销量第一生发产品:Viviscal
2017/12/21 全球购物
Lyle & Scott苏格兰金鹰官网:英国皇室御用品牌
2018/05/09 全球购物
本科应届生自荐信
2014/06/29 职场文书
学校党员个人问题整改措施思想汇报
2014/10/08 职场文书
领导四风问题整改措施思想汇报
2014/10/13 职场文书
民事赔偿协议书
2014/11/02 职场文书
2014年局领导班子自身建设情况汇报
2014/11/21 职场文书
2014年心理健康教育工作总结
2014/12/06 职场文书
2014年度工作总结报告
2014/12/15 职场文书
2015清明节祭奠英烈寄语大全
2015/03/04 职场文书
飞越疯人院观后感
2015/06/09 职场文书
自书遗嘱范文
2015/08/07 职场文书
MySQL深度分页(千万级数据量如何快速分页)
2021/07/25 MySQL
利用nginx搭建RTMP视频点播、直播、HLS服务器
2022/05/25 Servers