💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
我们在学习html的时候已经学习到,通过a标签来实现页面间的跳转,使用a标签的锚点来实现在页面内的导航。在angularjs的世界中,所有类似的跳转都是通过“锚点”来完成的。没错,这就是通过锚点来实现单页面(SPA)的。单页面大幅提升了用户的使用感受,真正的实现了一次加载,即使在网络不好的情况下,也会出现加载的loading动画,使等待并不显得那么枯燥。 > 关于锚点,这篇文章不错: http://www.zhangxinxu.com/wordpress/2013/08/url-anchor-html-%E9%94%9A%E7%82%B9%E5%AE%9A%E4%BD%8D%E6%9C%BA%E5%88%B6-%E5%BA%94%E7%94%A8-%E9%97%AE%E9%A2%98/ 本节中,我们来共同学习下angularjs是如何使用锚点来进行页面的『跳转』的。 # 增加锚点信息 我们修改下模板,来增加a标签,并在a标签上写入锚点信息。 `yun-zhi/phone-list.template.html` ~~~ <ul class="phones"> <li ng-repeat="phone in $ctrl.phones | filter:$ctrl.query | orderBy:$ctrl.orderProp" class="thumbnail"> <a ng-href="#/phones/{{phone.id}}" class="thumb"> <img ng-src="{{phone.imageUrl}}" alt="{{phone.name}}" class="thumb"/> </a> <a ng-href="#/phones/{{phone.id}}">{{phone.name}}</a> <p>{{phone.snippet}}</p> </li> </ul> ~~~ 点击图片或是标题的时候,发现URL的值已经按我们的设想改变了。 ![](https://box.kancloud.cn/2016-07-29_579afa23b1ffc.png) 上一节中,我们见到了ng-src,还记得为什么我们要这么用吧?这节中,我们又迎来了ng-href。 * * * * * *选读开始* 官方是这么解释的。 **Using Angular markup like {{hash}} in an href attribute will make the link go to the wrong URL if the user clicks it before Angular has a chance to replace the {{hash}} markup with its value. Until Angular replaces the markup the link will be broken and will most likely return a 404 error. The ngHref directive solves this problem.** 上面大概的意思是说:如果使用了href,那么当页面还没有被angluarjs渲染完毕时,用户就进行了a标签的点击的话,就可以会得到一个错误的链接,而使用ng-href可以规避这个问题。 为了验证官方的正确性,我们来做个测试: `test.html` angularjs正确渲染 ~~~ <!DOCTYPE html> <html lang="en" ng-app="test"> <head> <meta charset="UTF-8"> <title>test</title> <script src="bower_components/angular/angular.js"></script> </head> <body ng-controller="ctrl"> <a href="{{google}}">google</a> </body> <script type="text/javascript"> angular.module('test', []) .controller('ctrl', function($scope){ $scope.google = 'https://www.google.com/ncr'; }) </script> </html> ~~~ 去除angularjs渲染的代码: ~~~ <!DOCTYPE html> <html lang="en" ng-app="test"> <head> <meta charset="UTF-8"> <title>test</title> <script src="bower_components/angular/angular.js"></script> </head> <body ng-controller="ctrl"> <a href="{{google}}">google</a> </body> <script type="text/javascript"> // angular.module('test', []) // .controller('ctrl', function($scope){ // $scope.google = 'https://www.google.com/ncr'; // }) </script> </html> ~~~ 测试: ![](https://box.kancloud.cn/2016-07-29_579afa23d49e9.png) ![](https://box.kancloud.cn/2016-07-29_579afa23eceab.png) 由于`{{google}}`是一个错误的地址,所以404了。 但如果换成ng-href就不样了。用同样的方法,把href换成ng-href,自己试一下看看效果吧。 *选读结束* * * * * * 当然了,上面的原因了解一下就可以了。你需要知道的是:我们要使用ng-href而不是href。 #增加路由 点击完a标签,我们当然希望它能够跳转到我们想的手机详情页面,也不是只改变URL。 下面,我们使用一个新的模块`ngRoute`来解决这个问题。我们在前面学习了,如果在当前模块中想使用yunZhi模块,那么需要将yunZhi模块注入到当前模块。在angularjs中,我们把这种方式称为依赖注入。现在我们要使用一个新的叫做ngRoute的模块,当然也需要用同样的方法将ngRoute注入到当前模块中。 yunZhi模块是我们写的,直接放置在了yun-zhi文件夹中,然后进行行引用。ngRouter模块是别人(官方)写的,那么在使用之前,我们还需要的下载这个模块的JS文件。然后在index.html进行引用,再然后在angular.moudle中进行依赖注入。 ## 下载JS文件 ngRoute全称为:angular-route,和下载angularjs及bootstrap一样,我们可以使用`bower intall angular-route#1.5.7`来完成下载。 > angular-router的版本要和angularjs的版本一致。 ## 引用 下载后,我们下一步就是要把这个JS文件引用到我们的index.html中。 ~~~ <script src="bower_components/angular/angular.js"></script> <script src="bower_components/angular-route/angular-route.js"></script> <script src="app.moudle.js"></script> ~~~ 刷新页面,测试: ![](https://box.kancloud.cn/2016-07-29_579afa24133ac.png) > 第一次加载时,status应该为200。非首次加载时,浏览器会将当前缓存中的文件信息(Last-Modified及If-None-Match)发送给服务器,服务器跟据这两个值来判断浏览器中的缓存文件是否为最新的,如果是最新的,则会发送304。如果不是,则会重新传送用户的请求文件。 【注意】 angular-route是angularjs的一个模块,所以在引用顺序上,要先引用angularjs,然后再引用angular-route,否则,将会产生错误。 > angular-route官方文档: https://docs.angularjs.org/api/ngRoute ## 依赖注入 下面,我们将angular-route注入到我们的项目模块 `app.moudle.js` ~~~ // 定义模块 var phonecatApp = angular.module('phonecatApp', ['yunZhi', 'ngRoute']); ~~~ 有了前面注入yunZhi的经验,这个ngRoute写起来就很轻松了。 刷新页面,看控制台没报出任何信息。注入成功。 ## 增加路由 路由这个名称,可能我们开始摸不清门路。如果你读过ThinkPHP的开发手册,相信对于这个名词一定不陌生。 简单来说,路由会根据当前的URL来触发软件的某个特定的功能。在angularjs中,也是如此。 ### ng-view 在ngRoute模块中,有一个ng-view组件。ngRoute会依据不同的URL请求,将待定的内容放置于ng-view中。 为此,我们在index.html增加一个ng-view标签,用来显示ngRoute按不同的URL为我们生成的HTML。 ![](https://box.kancloud.cn/2016-07-29_579afa243588b.png) 上图中,简单说明了<phone-list>标签被移除后,再通过ng-view载入的过程。 ### 增加路由配置 在配置路由以前,我们还回想一下`phone-list`组件: ~~~ angular. module('yunZhi'). component('phoneList', { templateUrl: 'yun-zhi/phone-list.template.html', controller: ['$http', function PhoneListController(param1) { var self = this; self.orderProp = 'age'; param1.get('phones/phones.json').then(function(response) { self.phones = response.data; }); } ] }); ~~~ 模块配置调用的是component(),路由的配置调用的是config() 我们新建一个`app.config.js`,并且调用config()来进行路由的配置. 1. 初始化 `app.config.js` ~~~ angular. module('phonecatApp'). config(['$routeProvider', function($routeProvider){}]); ~~~ 2.增加配置 `app.config.js' ~~~ angular. module('phonecatApp'). config(['$routeProvider', function($routeProvider) { $routeProvider. when('/', {template:'hello yunzhi'}). when('/yunzhi', {template:'hello dreamer'}). otherwise('/'); } ]); ~~~ $routeRrovider提供两个方法. `when('/', {template:'hello yunzhi'})`当锚点为/时,输出'hello yunzhi' `.otherwise('/')`, 当URL不是`/`, 也不是`/yunzhi`时。则调用`when('/')`的模板。 ![](https://box.kancloud.cn/2016-07-29_579afa245b6f0.png) ![](https://box.kancloud.cn/2016-07-29_579afa2475012.png) ## 整理路由表 我们现在需要两个基本的页面,第1个是手机列表页,不需要传入任何参数。第2个是手机详情页,需要传入手机ID以确定显示哪个手机的详情。 手机列表页 url -> /phones 手机详情页 url -> /phones/:phoneId phoneId为手机关键字信息,类似于我们以前的GET信息。 按整理的路由表,重写config `app.config.js` ~~~ angular. module('phonecatApp'). config(['$routeProvider', function($routeProvider) { $routeProvider. when('/phones', {template:'<phone-list></phone-list>'}). when('/phones/:phoneId', {template:'hello dreamer'}). otherwise('/phones'); } ]); ~~~ 同时,我们在模板的属性中,恢复为<phone-list>: ![](https://box.kancloud.cn/2016-07-29_579afa24940d5.png) ![](https://box.kancloud.cn/2016-07-29_579afa24b13a0.png) ### 建立模块 `yun-zhi/phone-detail.component.js` ~~~ angular. module('yunZhi'). component('phoneDetail', { template: 'id:{{$ctrl.phoneId}}', controller: ['$routeParams', function PhoneListController($routeParams) { // 使用$routeParam获取phoneId // $routeParam存在于angular-route模块中 this.phoneId = $routeParams.phoneId; } ] }); ~~~ ### 引入js文件 ~~~ <script src="yun-zhi/hello-yunzhi.component.js"></script> <script src="yun-zhi/phone-detail.component.js"></script> </head> ~~~ 这里,我们使用$routeParams来获取phoneId的值 ![](https://box.kancloud.cn/2016-07-29_579b077350890.png) ## 增加锚点前缀 `!` 前面,我们要求使用ng-src和ng-href。在这,我们在加一个前缀`!`。比如以前这样使用`#/lists`,增加前缀后我们这样使用`#!/list`至于为什么要这么用。现在我们还不需要知道原因。 官方文档如是说: > We also used $locationProvider.hashPrefix() to set the hash-prefix to !. This prefix will appear in the links to our client-side routes, right after the hash (#) symbol and before the actual path (e.g. index.html#!/some/path). Setting a prefix is not necessary, but it is considered a good practice (for reasons that are outside the scope of this tutorial). ! is the most commonly used prefix. ## 使用$locationProvider增加!前缀。 修改配置信息。 > 所有 注入模块 的配置信息,都需要在.config()中进行定义。比如,我们在config()中定义, ngRoute模块的配置信息。 `app.config.js` ~~~ angular. module('phonecatApp'). config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) { $locationProvider.hashPrefix('!'); $routeProvider. when('/phones', {template:'<phone-list></phone-list>'}). when('/phones/:phoneId', {template:'<phone-detail></phone-detail>'}). otherwise('/phones'); } ]); ~~~ 定义完前缀后,我们就需要使用带有这个前缀的信息来定义锚点了。 修改锚点信息 `phone-list.template.html` ~~~ ... <a ng-href="#!/phones/{{phone.id}}" class="thumb"> ... <a ng-href="#!/phones/{{phone.id}}"> ... ~~~ ![](https://box.kancloud.cn/2016-08-03_57a1591f9a6c0.png) ## 完善依赖注入 我们在`yun-zhi/phone-detail.component.js`中使用了`$routeParams`对象,此对象位于`angular-route`中。在本节上,使其生效的方法是:在`app.moudle.js`注入了`ng-route`。但如果想将yun-zhi做为一个单独的模块发布的话,则需要我们在yun-zhi模块中解决这个依赖。 `yun-zhi.moudle.js` ~~~ - angular.module('yunZhi', []); + angular.module('yunZhi', ['ngRoute']); ~~~ 这样,我们便在模块中注入了这个`ngRoute`,可以将模块代码打包发布后供其它应用使用了。 有人说,如果这样做,我们在app.moudle.js中注入了一次,在这再注入一次,相同的操作会不会被执行两次,从而造了两个相同的轮子。angularjs当然想到了这点,它的解决方案叫做:惰性加载。简单的说是,不管你注入多少次,我们只会在需要使用的时候加载一次。 * * * * * `phone-list.template.html` ~~~ <div class="container-fluid"> <div class="row"> <div class="col-md-2"> <p> Search: <input ng-model="$ctrl.query" /> </p> <p> Sort By: <select ng-model="$ctrl.orderProp"> <option value="name">Alphabetical</option> <option value="age">Newest</option> </select> </p> </div> <div class="col-md-10"> <!--Body content--> <ul class="phones"> <li ng-repeat="phone in $ctrl.phones | filter:$ctrl.query | orderBy:$ctrl.orderProp" class="thumbnail"> <a ng-href="#!/phones/{{phone.id}}" class="thumb"> <img ng-src="{{phone.imageUrl}}" alt="{{phone.name}}" class="thumb"/> </a> <a ng-href="#!/phones/{{phone.id}}">{{phone.name}}</a> <p>{{phone.snippet}}</p> </li> </ul> </div> </div> </div> ~~~ `phone-detail.component.js` ~~~ angular. module('yunZhi'). component('phoneDetail', { template: 'id:{{$ctrl.phoneId}}', controller: ['$routeParams', function PhoneListController($routeParams) { // 使用$routeParam获取phoneId // $routeParam存在于angular-route模块中 this.phoneId = $routeParams.phoneId; } ] }); ~~~