之前已经介绍了$location服务的基本用法:angular学习笔记(三十一)-$location(1).

这篇是上一篇的进阶,介绍$location的配置,兼容各版本浏览器,等.

*注意,这里介绍的是基于angular-1.3.2版本的,低版本的$location可能会有问题.

hashbang模式和history api创建单页应用

首先,$location是用在单页应用里的...(废话,angular就是用在单页的)...所以,$location处理的是url改变,但是不刷新页面的情况.那么我们知道,不刷新页面但是请求ajax改变url,需要存入历史记录.这样的话,需要使用html5的history api,但是对于不支持history api的浏览器(也就是ie8,9吧,反正ie7angular本来就不支持,而且看了花瓣网,它在ie7,8,也没有使用单页,而是新开链接),则需要使用hashbang模式.

什么叫hashbang模式?在网上查阅了很多也没有查到具体的说明.只能按照自己理解的来总结:

比如一个url: http://localhost:801/$location/index.html,这个页面,在垃圾浏览器里,需要实现发送ajax,改变url,但是不刷新页面,并且可以使用后退前进按钮,怎么做呢? 就在url后面使用'#'加一个标识符'!',再加上路径,参数,哈希值等...这样,因为使用了'#',所以页面不会刷新,而url也改变了,可以存入历史记录.其中'#'代表了hash,'!'代表了bang,所以这种模式被称为hashbang模式.注意,'!'不是固定的,可以是任意的标识符,也可以为空的.而且,当我刷新http://localhost:801/$location/index.html#!/foo?name=code_bunny#bunny时,可以访问到http://localhost:801/$location/index.html.

这就是我自己总结的传统的hashbang模式.而使用HTML5 history api,则不需要'#'和标示符.只需要直接在url后面加上路径,参数,哈希值,等...但是当我刷新http://localhost:801/$location/index.html/foo?name=code_bunny#bunny时,是不能访问到http://localhost:801/$location/index.html页面的,需要服务端进行配置,使应用能够接受来自http://localhost:801/$location/index.html/foo?name=code_bunny#bunny的请求.

介绍完了hashbang和history api,接下来来看下angular是怎么处理它们的.

配置$location服务

可以看到,在 angular学习笔记(三十一)-$location(1)这篇文章里,所有例子的url都是带有#的,(bang标识符为空),无论在任何浏览器里,它都是这样的,而没有使用history api. 如果需要使用history api,或者配置bang标识符,可以对$location服务进行配置:

  1. var locationApp = angular.module('locationApp',[]);
  2. locationApp.config(function($locationProvider){
  3. $locationProvider.html5Mode(true).hashPrefix('!');
  4. });

$locationProvider有两个方法可以配置:

$locationProvider.html5Mode(boolean || obj)

1.$locationProvider.html5Mode(true): 开启html5的history api

2.$locationProvider.html5Mode(false): 关闭html5的history api

3.$locationProvider.html5Mode({enabled:true}): 同1

4.$locationProvider.html5Mode({enabled:false}): 同2(默认配置)

5.$locationProvider.html5Mode({requireBase:true}): 设置为'需要定义base href'标签(后面会讲到base href标签)

6.$locationProvider.html5Mode({requireBase:false}): 设置为'不需要定义base href'标签(后面会讲到base href标签)

$locationProvider.hashPrefix('string')

设置bang标识符.不设置的话,默认为空

base href属性的定义

在介绍hashbang和histroy api的时候,我把url分成了两部分颜色显示,第一部分是浅绿色的,第二部分是深绿色的.浅绿色的是固定不变的,而深绿色的,是可以改变的.也就是$location.url()

那么浏览器是如何知道固定的是什么的呢? 就是通过设置base href属性:

在head标签里添加如下标签:

  1. <base href="/$location/base/">

这样,浏览器就知道,不变的部分是 http://localhost:801/$location/base/.注意,一定要有'/'开头,'/'结尾,否则在垃圾浏览器里会有问题.因为它不会自己给它加上'/',但如果你定义的base href是'/$location/base/index.html',那是可以的.

另外,定义了这个base href以后,页面里所有的js,css,都是这个base href的相对路径.

前面提到了$locationProvider.html5Mode的参数的requireBase属性,就是用来定义是否页面中一定要定义base href标签的.

如果不定义,它会自己把页面的根目录作为固定部分,比如 http://localhost:801/$location/base/index.html, 它会认为固定部分是http://localhost:801

总结一下,base href的值,应该是域名部分后面的整个路径部分,以/开始,/结尾,注意一定要加这个属性!!!实际工作中很容易忘记加它导致路由不生效也不报错.被坑过很多次了!!!

a链接的跳转

页面的url改变,肯定是点击了某些元素,最常见的自然是a链接,a链接本来的作用是跳转页面,但在单页应用中,我们只需要让它改变url后面的$location.url()部分,而不刷新页面.所以,a链接可以这么写:

  1. <a href="some1?foo=bar">/some1?foo=bar</a>
  2. <a href="some2?foo=bar#myhash">/some2?foo=bar#myhash</a>

angular会自动处理浏览器的兼容问题.假设点击第一个链接:

在高级浏览器里,url会变成: http://localhost:801/$location/base/some1?foo=bar

在垃圾浏览器里,url会变成: http://localhost:801/$location/base/#!/some1?foo=bar

注意事项:

1.href值以'/'开头,会跳转刷新页面.

2.a链接带有target属性,会跳转刷新页面.

3.外链的话,直接写绝对地址即可跳转刷新页面

$location的双向数据绑定

下面这段代码演示了如何实现让$location.url和地址栏的url双向绑定:

  1. <input type="text" ng-model="location" ng-model-options="{getterSetter:true}"/>
  1. $scope.location=function(newLocation){
  2. return $location.url(newLocation);
  3. };

综合实例

最后,我用所有关于$location的知识写一个demo:

这个实例在angular学习笔记(三十一)-$location(1)的基础上新增了本篇讲到的知识:

1.添加base href标签

2.hashbang和html5 history api处理浏览器兼容

3.$location双向数据绑定

4.a链接改变url

html:

  1. <!DOCTYPE html>
  2. <html ng-app="locationApp">
  3. <head>
  4. <title>21.1 $location</title>
  5. <meta charset="utf-8">
  6. <base href="/$location/base/">
  7. <script src="../angular-1.3.2.js"></script>
  8. <script src="../script.js"></script>
  9. </head>

  10. <body ng-controller="locationCtrl">

  11. <input type="text" ng-model="location" ng-model-options="{getterSetter:true}"/>
  12.  
  13. <p>完整url路径: <span>{{absurl}}</span></p>
  14.  
  15. <p>url路径(当前url#后面的内容,包括参数和哈希值): <span>{{url}}</span>
  16. <button ng-click="changeUrl()">改变</button>
  17. </p>
  18.  
  19. <p>相对路径(也就是当前url#后面的内容,不包括参数): <span>{{path}}</span>
  20. <button ng-click="changePath()">改变</button>
  21. </p>
  22.  
  23. <p>协议(比如http,https): <span>{{protocol}}</span></p>
  24.  
  25. <p>主机名: <span>{{host}}</span></p>
  26.  
  27. <p>端口号: <span>{{port}}</span></p>
  28.  
  29. <p>哈希值: <span>{{hash}}</span>
  30. <button ng-click="changeHash()">改变</button>
  31. </p>
  32.  
  33. <p>search值序列化json:
  34. <span>{{search}}</span>
  35. <button ng-click="changeSearch_1()">改变1</button>
  36. <button ng-click="changeSearch_2()">改变2</button>
  37. <button ng-click="changeSearch_3()">改变3</button>
  38. <button ng-click="changeSearch_4()">改变4</button>
  39. <button ng-click="changeSearch_5()">改变5</button>
  40. <button ng-click="changeSearch_6()">改变6</button>
  41. </p>
  42.  
  43. <a href="some1?foo=bar">/some1?foo=bar</a> |
  44. <a href="some2?foo=bar#myhash">/some2?foo=bar#myhash</a> |
  45. <a href="http://www.baidu.com">外部链接</a>
  46.  
  47. </body>
  48. </html>

js:

  1. var locationApp = angular.module('locationApp',[]);
  2. locationApp.config(function($locationProvider){
  3. $locationProvider.html5Mode(true).hashPrefix('!');
  4. });
  5. locationApp.controller('locationCtrl',function($scope,$location,$timeout,$rootScope){
  6. $scope.location=function(newLocation){
  7. return $location.url(newLocation);
  8. };
  9. $scope.absurl = $location.absUrl();
  10. $scope.url = $location.url();
  11. $scope.path = $location.path();
  12. $scope.protocol = $location.protocol();
  13. $scope.host = $location.host();
  14. $scope.port = $location.port();
  15. $scope.hash = $location.hash();
  16. $scope.search = $location.search();
  17.  
  18. $scope.refresh = function(){
  19. $scope.absurl = $location.absUrl();
  20. $scope.url = $location.url();
  21. $scope.path = $location.path();
  22. $scope.hash = $location.hash();
  23. $scope.search = $location.search();
  24. };
  25.  
  26. //重写url部分,相应的absurl,url,path,hash,search都会改变
  27. $scope.changeUrl = function(){
  28. $location.url('/foo2?name=bunny2&age=12#myhash2');
  29. };
  30.  
  31. //重写path部分,相应的absurl,url,path都会改变
  32. $scope.changePath = function(){
  33. $location.path('/foo2/foo3');
  34. };
  35.  
  36. //重写hash部分,相应的absurl,url,hash都会改变
  37. $scope.changeHash = function(){
  38. $location.hash('myhash3');
  39. };
  40.  
  41. //修改search部分(方法1),相应的absurl,url,search,hash都会改变
  42. //指定两个参数,第一个参数是属性名,第二个参数是属性值.
  43. //如果属性名是已有属性,则修改,如果属性名不是已有的,则新增.
  44. //属性值也可以是一个数组,参考方法6
  45. $scope.changeSearch_1 = function(){
  46. $location.search('name','code_bunny');
  47. };
  48.  
  49. //修改search部分(方法2),相应的absurl,url,search,hash都会改变
  50. //指定两个参数,第二个参数是null:删除第一个参数所指定的属性名.不再有这个属性
  51. //若第一个参数不是已有的,则不发生任何改变
  52. $scope.changeSearch_2 = function(){
  53. $location.search('age',null);
  54. };
  55.  
  56. //修改search部分(方法3),相应的absurl,url,search,hash都会改变
  57. //指定一个参数,json对象,直接重写整个search部分.不管是不是已有属性,全部重写.
  58. //这里属性的值可以是一个数组,参考方法5
  59. $scope.changeSearch_3 = function(){
  60. $location.search({name:'papamibunny',age:16,love:'zxg'});
  61. };
  62.  
  63. //修改search部分(方法4),相应的absurl,url,search,hash都会改变
  64. //指定一个参数,字符串,整个search部分就变为这个字符串.注意是没有属性值的.
  65. $scope.changeSearch_4 = function(){
  66. $location.search('bunnybaobao');
  67. };
  68.  
  69. //修改search部分(方法5),相应的absurl,url,search,hash都会改变
  70. //其余和方法3一样.全部重写search:
  71. //指定一个参数,json格式,属性值是一个数组,那么最后的search会变成name=code_bunny&name=white_bunny&name=hua_bunny
  72. $scope.changeSearch_5 = function(){
  73. $location.search({name:['code_bunny','white_bunny','hua_bunny']});
  74. };
  75.  
  76. //修改search部分(方法6),相应的absurl,url,search,hash都会改变
  77. //其余和方法1一样,修改指定的属性名(或新增)
  78. //第二个参数是一个数组,最后search中的love部分会变成love=zxg&love=mitu
  79. //它和方法5的区别,就像方法1和方法3的区别,一个是修改或新增某个属性值,一个是重置整个search
  80. $scope.changeSearch_6 = function(){
  81. $location.search('love',['zxg','mitu']).replace();
  82. };
  83.  
  84. //使用$location.replace(),则这一次的修改路径不会被记录到历史记录中,点击后退,不会后退到改变前的路径,而是后退到改变前的路径的改变前的路径
  85.  
  86. $rootScope.$on('$locationChangeStart',function(){
  87. console.log('开始改变$location')
  88. });
  89. $rootScope.$on('$locationChangeSuccess',function(){
  90. $scope.refresh();
  91. console.log('结束改变$location')
  92. });
  93. //这里就算绑定了$routeChangeStart和$routeChangeSuccess,也不会被触发,因为这里没有$route相关的服务.
  94. });

*以上demo运行在我本地的http://localhost:801/这个wamp服务下.

1.添加base href标签

添加了base href标签后,页面的基础url就变为: http://localhost:801/$location/base/

2.hashbang和html5 history api处理浏览器兼容

设置启用html5的history api,设置bang标识符为'!'

完成了1,2以后,在浏览器里打开 http://localhost:801/$location/base

在高级浏览器里: 

在ie9模式:

这里初始状态的$location.url,一个有'/',一个没有'/',不过这个不用介意.不影响实际的使用的.

需要重视的是,当链接发生过改变(比如我点击了链接1/some1?foo=bar),然后刷新页面,在ie9下,它依然可以加载到该页面,但是在chrome下是不行的,所以,在使用了history api的浏览器里,需要服务端配置.通常,你要把所有链接都转给应用的入口点(比如index.html)。

3.$location双向数据绑定

可以使用1.3提供的ng-model-options的getterSetter,给input绑定location方法.这样,input输入内容,就会调用$location.url(newLocation)设置url,当调用$location.url('...')的时候,input元素的value值也会被同步.需要注意,直接在地址栏输入url是没用的,不过这无所谓,因为应用中也不会有人要跳转的时候手动输地址...

4.a链接改变url

点击前面两个链接,url会改变,不刷新页面.

完整代码:https://github.com/OOP-Code-Bunny/angular/tree/master/%24location

参考文献:http://www.ngnice.com/docs/guide/$location (中文版的是1.2的,不是最新的) https://docs.angularjs.org/guide/$location

angular学习笔记(三十一)-$location(2)的更多相关文章

  1. angular学习笔记(三十一)-$location(1)

    本篇介绍angular中的$location服务的基本用法,下一篇介绍它的复杂的用法. $location服务的主要作用是用于获取当前url以及改变当前的url,并且存入历史记录. 一. 获取url的 ...

  2. angular学习笔记(三十)-指令(10)-require和controller

    本篇介绍指令的最后两个属性,require和controller 当一个指令需要和父元素指令进行通信的时候,它们就会用到这两个属性,什么意思还是要看栗子: html: <outer‐direct ...

  3. angular学习笔记(三十)-指令(7)-compile和link(2)

    继续上一篇:angular学习笔记(三十)-指令(7)-compile和link(1) 上一篇讲了compile函数的基本概念,接下来详细讲解compile和link的执行顺序. 看一段三个指令嵌套的 ...

  4. angular学习笔记(三十)-指令(7)-compile和link(1)

    这篇主要讲解指令中的compile,以及它和link的微妙的关系. link函数在之前已经讲过了,而compile函数,它和link函数是不能共存的,如果定义了compile属性又定义link属性,那 ...

  5. angular学习笔记(三十)-指令(6)-transclude()方法(又称linker()方法)-模拟ng-repeat指令

    在angular学习笔记(三十)-指令(4)-transclude文章的末尾提到了,如果在指令中需要反复使用被嵌套的那一坨,需要使用transclude()方法. 在angular学习笔记(三十)-指 ...

  6. angular学习笔记(三十)-指令(5)-link

    这篇主要介绍angular指令中的link属性: link:function(scope,iEle,iAttrs,ctrl,linker){ .... } link属性值为一个函数,这个函数有五个参数 ...

  7. angular学习笔记(三十)-指令(2)-restrice,replace,template

    本篇主要讲解指令中的 restrict属性, replace属性, template属性 这三个属性 一. restrict: 字符串.定义指令在视图中的使用方式,一共有四种使用方式: 1. 元素: ...

  8. angular学习笔记(三十)-指令(1)-概述

    之前在 angular学习笔记(十九)-指令修改dom 里面已经简单的提到了angular中的指令,现在来详细的介绍 '指令' 一.指令的创建: dirAppModule.directive('dir ...

  9. angular学习笔记(三十)-指令(8)-scope

    本篇讲解指令的scope属性: scope属性值可以有三种: 一.scope:false 默认值,这种情况下,指令的作用域就是指令元素当前所在的作用域. 二.scope:true 创建一个继承了父作用 ...

随机推荐

  1. linux 上配置tomcat、mysql 开机启动

    1.tomcat 开机启动 方法一.修改系统文件 (已经尝试,可以) 1.修改/etc/rc.d/rc.local vi /etc/rc.d/rc.local 2.添加下面两行脚本,记住是两行,仅仅第 ...

  2. Oracle用分区表分区交换做历史数据迁移

    一. 说明: OLTP库中有些表数据量大,且每月有持续的大量数据添加.因为历史数据在此库中不再做訪问,而是在另1个OLAP库中做分析.所以会对历史数据迁移至OLAP库中.对这样的历史数据迁移的操作.较 ...

  3. codeM 2018 资格赛

    比赛链接:https://www.nowcoder.com/activity/2018codem/index?from=meituan 1.下单 给定若干商品,可以选择打折.满减两种方式. #incl ...

  4. [转]webMethods公司简介

    原文链接 webMethods公司简介 webMethods,Inc.(美国纳斯达克股市上市代号:WEBM)为著名业务整合软件供应商之一.公司于1996年创立,总部位于美国佛吉尼亚州(Virginia ...

  5. Android启动过程深入解析

    本文由 伯乐在线 - 云海之巅 翻译.未经许可,禁止转载!英文出处:kpbird.欢迎加入翻译小组. 当按下Android设备电源键时究竟发生了什么? Android的启动过程是怎么样的? 什么是Li ...

  6. C# 因IIS回收导致定时器失效的解决方案

    首先不要设置iis自动回收,一般设置凌晨1-2点左右回收一次,当凌晨iis回收应用程序池的时候,会调用Application_End,执行里面的代码, 重新启动网站,建议定时器的代码放在Session ...

  7. 【Redis】Redis的常规操作命令

    NoSQL是一种非关系型数据库,非关系型数据库库和传统的关系型数据库不同,非关系性体现在不需要依赖表进行数据存储.常见的非关系型数据库有Redis.MonoDB.HBase等,这些是基于key-val ...

  8. C++ 虚函数表浅析

    一.背景知识(一些基本概念) 虚函数(Virtual Function):在基类中声明为 virtual 并在一个或多个派生类中被重新定义的成员函数. 纯虚函数(Pure Virtual Functi ...

  9. xcode修改项目名后反复出现 clang error

    xcode修改项目名后反复出现 clang error,  提示 ld: file not found . 并且该错误并不是出现在项目编译阶段,而是项目的Tests 的link阶段, 同时提示 xct ...

  10. VC下加载JPG/GIF/PNG图片的两种方法

    转载自:http://blog.sina.com.cn/s/blog_6582aa410100huil.html 仅管VC有提供相应的API和类来操作bmp位图.图标和(增强)元文件,但却不支持jpg ...