angular學習筆記(三十一)-$location(2)


之前已經介紹了$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服務進行配置:

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

$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標簽里添加如下標簽:

<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鏈接可以這么寫:

<a href="some1?foo=bar">/some1?foo=bar</a> 
<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雙向綁定:

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

 

綜合實例

最后,我用所有關於$location的知識寫一個demo:

這個實例在angular學習筆記(三十一)-$location(1)的基礎上新增了本篇講到的知識:

1.添加base href標簽

2.hashbang和html5 history api處理瀏覽器兼容

3.$location雙向數據綁定

4.a鏈接改變url

 

html:

<!DOCTYPE html>
<html ng-app="locationApp">
<head>
  <title>21.1 $location</title>
  <meta charset="utf-8">
  <base href="/$location/base/">
  <script src="../angular-1.3.2.js"></script>
  <script src="../script.js"></script>
</head>

<body ng-controller="locationCtrl">
<input type="text" ng-model="location" ng-model-options="{getterSetter:true}"/> <p>完整url路徑: <span>{{absurl}}</span></p> <p>url路徑(當前url#后面的內容,包括參數和哈希值): <span>{{url}}</span> <button ng-click="changeUrl()">改變</button> </p> <p>相對路徑(也就是當前url#后面的內容,不包括參數): <span>{{path}}</span> <button ng-click="changePath()">改變</button> </p> <p>協議(比如http,https): <span>{{protocol}}</span></p> <p>主機名: <span>{{host}}</span></p> <p>端口號: <span>{{port}}</span></p> <p>哈希值: <span>{{hash}}</span> <button ng-click="changeHash()">改變</button> </p> <p>search值序列化json: <span>{{search}}</span> <button ng-click="changeSearch_1()">改變1</button> <button ng-click="changeSearch_2()">改變2</button> <button ng-click="changeSearch_3()">改變3</button> <button ng-click="changeSearch_4()">改變4</button> <button ng-click="changeSearch_5()">改變5</button> <button ng-click="changeSearch_6()">改變6</button> </p> <a href="some1?foo=bar">/some1?foo=bar</a> | <a href="some2?foo=bar#myhash">/some2?foo=bar#myhash</a> | <a href="http://www.baidu.com">外部鏈接</a>

</body> </html>

js:

var locationApp = angular.module('locationApp',[]); locationApp.config(function($locationProvider){ $locationProvider.html5Mode(true).hashPrefix('!'); }); locationApp.controller('locationCtrl',function($scope,$location,$timeout,$rootScope){ $scope.location=function(newLocation){ return $location.url(newLocation); }; $scope.absurl = $location.absUrl(); $scope.url = $location.url(); $scope.path = $location.path(); $scope.protocol = $location.protocol(); $scope.host = $location.host(); $scope.port = $location.port(); $scope.hash = $location.hash(); $scope.search = $location.search(); $scope.refresh = function(){ $scope.absurl = $location.absUrl(); $scope.url = $location.url(); $scope.path = $location.path(); $scope.hash = $location.hash(); $scope.search = $location.search(); }; //重寫url部分,相應的absurl,url,path,hash,search都會改變
    $scope.changeUrl = function(){ $location.url('/foo2?name=bunny2&age=12#myhash2'); }; //重寫path部分,相應的absurl,url,path都會改變
    $scope.changePath = function(){ $location.path('/foo2/foo3'); }; //重寫hash部分,相應的absurl,url,hash都會改變
    $scope.changeHash = function(){ $location.hash('myhash3'); }; //修改search部分(方法1),相應的absurl,url,search,hash都會改變
    //指定兩個參數,第一個參數是屬性名,第二個參數是屬性值.
    //如果屬性名是已有屬性,則修改,如果屬性名不是已有的,則新增.
    //屬性值也可以是一個數組,參考方法6
    $scope.changeSearch_1 = function(){ $location.search('name','code_bunny'); }; //修改search部分(方法2),相應的absurl,url,search,hash都會改變
    //指定兩個參數,第二個參數是null:刪除第一個參數所指定的屬性名.不再有這個屬性
    //若第一個參數不是已有的,則不發生任何改變
    $scope.changeSearch_2 = function(){ $location.search('age',null); }; //修改search部分(方法3),相應的absurl,url,search,hash都會改變
    //指定一個參數,json對象,直接重寫整個search部分.不管是不是已有屬性,全部重寫.
    //這里屬性的值可以是一個數組,參考方法5
    $scope.changeSearch_3 = function(){ $location.search({name:'papamibunny',age:16,love:'zxg'}); }; //修改search部分(方法4),相應的absurl,url,search,hash都會改變
    //指定一個參數,字符串,整個search部分就變為這個字符串.注意是沒有屬性值的.
    $scope.changeSearch_4 = function(){ $location.search('bunnybaobao'); }; //修改search部分(方法5),相應的absurl,url,search,hash都會改變
    //其余和方法3一樣.全部重寫search:
    //指定一個參數,json格式,屬性值是一個數組,那么最后的search會變成name=code_bunny&name=white_bunny&name=hua_bunny
    $scope.changeSearch_5 = function(){ $location.search({name:['code_bunny','white_bunny','hua_bunny']}); }; //修改search部分(方法6),相應的absurl,url,search,hash都會改變
    //其余和方法1一樣,修改指定的屬性名(或新增)
    //第二個參數是一個數組,最后search中的love部分會變成love=zxg&love=mitu
    //它和方法5的區別,就像方法1和方法3的區別,一個是修改或新增某個屬性值,一個是重置整個search
    $scope.changeSearch_6 = function(){ $location.search('love',['zxg','mitu']).replace(); }; //使用$location.replace(),則這一次的修改路徑不會被記錄到歷史記錄中,點擊后退,不會后退到改變前的路徑,而是后退到改變前的路徑的改變前的路徑
 $rootScope.$on('$locationChangeStart',function(){ console.log('開始改變$location') }); $rootScope.$on('$locationChangeSuccess',function(){ $scope.refresh(); console.log('結束改變$location') }); //這里就算綁定了$routeChangeStart和$routeChangeSuccess,也不會被觸發,因為這里沒有$route相關的服務.
});

*以上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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM