AngularJs Using $location詳解及示例代碼
一、What does it do?
$location服務分析瀏覽器地址欄中的URL(基于window.location),讓我們可以在應用中較為方便地使用URL里面的東東。在地址欄中更改URL,會響應到$location服務中,而在$location中修改URL,也會響應到地址欄中。
$location服務:
暴露當前瀏覽器地址欄的URL,所以我們可以
1.注意和觀察URL
2.改變URL
當用戶做以下操作時,與瀏覽器一起同步URL:
1.改變地址欄
2.單擊后退或者前進按鈕(或者點擊一個歷史鏈接)。
3.單擊一個鏈接
將URL對象描繪為一系列的方法(protocol,host,path,search,hash)。
1. 比較$location和window.location
1) 目的:window.location和$location服務,都允許對當前瀏覽器的location進行讀寫訪問。
2) API:window.location暴露一個未經(jīng)加工的對象,附帶一些可以直接修改的屬性;而$location服務則是暴露一些jQuery風格的getter/setter方法。
3) 與angular應用聲明周期的整合:$location知道關于所有內部聲明周期的階段,與$watch等整合;而window.location則不行。
4) 與HTML5 API無縫結合:是(with a fallback for legacy browsers,對于低版本的瀏覽器有兼容手段?);而window.location則沒有。
5) 知道應用加載的文檔根目錄(docroot)或者上下文(context):window.location不行,wnidow.location.path會返回”/docroot/子路徑”;而$location.path()返回真實的docroot。
2. When should I use $location?
在應用中,任何需要對當前URL的改變作出響應,或者想去改變當前瀏覽器的URL的時候。
3. What does it not do?
當瀏覽器URL改變的時候,不會導致頁面重新加載(page reload)。如果需要做這件事情(更改地址,實現(xiàn)page reload),請使用較低級別的API,$window.location.href。
二、General overview of the API(API的總體概述)
$location 服務可以根據(jù)它初始化時的配置而有不同的表現(xiàn)。默認配置是適合大多數(shù)應用的,其他配置定制,可以開啟一些新特性。
當$location服務初始化完畢,我們可以以jQuery風格的getter、setter方法來使用它,允許我們獲得或者改變當前瀏覽器的URl。
1. $location service configuration
想配置$location服務,需要獲得$locationProvider(http://code.angularjs.org/1.0.2/docs/api/ng.$locationProvider),并設置以下參數(shù):
html5Mode(mode):{boolean},true - see HTML5 mode;false - see Hashbang mode,默認: false。(下面的章節(jié)會解釋各種mode)
hashPrefix(prefix):{string},hashbang使用的前綴(html5Mode為false時,使用hashbang mode,以適應不支持HTML5 mode的瀏覽器),默認:'!'
2. Getter and setter methods
$location 服務為只讀的URL部分(absUrl,protocol,host,port)提供getter方法,也提供url,path,search,hash的getter、setter方法。
// get the current path $location.path(); // change the path $location.path('/newValue')
所有setter方法都返回同一個$location對象,以實現(xiàn)鏈式語法。例如,在一句里面修改多個屬性,鏈式setter方法類似:
$location.path(‘/newValue').search({key:value});
有一個特別的replace方法,可以用作告訴$location服務,在下一次與瀏覽器同步時,使用某個路徑代替最新的歷史記錄,而不是創(chuàng)建一個新的歷史記錄。當我們想實現(xiàn)重定向(redirection)而又不想使后退按鈕(后退按鈕回重新觸發(fā)重定向)失效時,replace方法就很有用了。想改變當前URL而不創(chuàng)建新的歷史記錄的話,我們可以這樣做:
$location.path(‘/someNewPath').replace();
注意,setter方法不會馬上更新window.location。相反,$location服務會知道scope生命周期以及合并多個$location變化為一個,并在scope的$digest階段一并提交到window.location對象中。正因為$location多個狀態(tài)的變化會合并為一個變化,到瀏覽器中,只調用一次replace()方法,讓整個commit只有一個replace(),這樣不會使瀏覽器創(chuàng)建額外的歷史記錄。一旦瀏覽器更新了,$location服務會通過replace()方法重置標志位,將來的變化將會創(chuàng)建一個新的歷史記錄,除非replace()被再次調用。
Setter and character encoding
我們可以傳入特殊字符到$location服務中,服務會按照RFC3986標準,自動對它們進行編碼。當我們訪問這些方法時:
- 所有傳入$location的setter方法的值,path()、search()、hash(),都會被編碼。
- getter方法(沒參數(shù))返回的值都是經(jīng)過解碼的,如path(),search(),hash()。
- 當我們調用absUrl()方法時,返回的值是包含已編碼部分的完整url。
- 當我們調用url()方法時,返回的是包含path、search和hash部分的已經(jīng)編碼的url,如/path?search=1&b=c#hash。
三、Hashbang and HTML5 Modes
$location服務有兩個配置模式,可以控制瀏覽器地址欄的URL格式:Hashbang mode(默認)與基于使用HTML5 History API的HTML5 mode。在兩種模式下,應用都使用相同的API,$location服務會與正確的URL片段、瀏覽器API一起協(xié)作,幫助我們進行瀏覽器URL變更以及歷史管理。
1. Hashbang mode (default mode)
在這個模式中,$location在所有瀏覽器中都使用Hashbang URL。查看下面的代碼片段,可以了解更多:
it('should show example', inject( function($locationProvider) { $locationProvider.html5mode = false; $locationProvider.hashPrefix = '!'; }, function($location) { // open http://host.com/base/index.html#!/a $location.absUrl() == 'http://host.com/base/index.html#!/a'; $location.path() == '/a'; $location.path('/foo'); $location.absUrl() == 'http://host.com/base/index.html#!/foo'; $location.search() == {};//search沒東東的時候,返回空對象 $location.search({a: 'b', c: true}); $location.absUrl() == 'http://host.com/base/index.html#!/foo?a=b&c'; $location.path('/new').search('x=y');//可以用字符串的方式更改search,每次設置search,都會覆蓋之前的search $location.absUrl() == 'http://host.com/base/index.html#!/new?x=y'; } ));
Crawling your app(讓google能夠對我們的應用進行索引)
如果我們想讓我們的Ajax應用能夠被索引,我們需要在head中增加一個特殊的meta標簽:
<meta name="fragment" content="!" />
這樣做,將讓爬蟲機器人使用_escaped_fragment_參數(shù)請求當前的鏈接,讓我們的服務器認識爬蟲機器人,并提供對應的HTML快照。想了解更多關于這個技術的信息,可以查看https://developers.google.com/webmasters/ajax-crawling/docs/specification?hl=zh-CN
四、HTML5 mode
在HTML5模式中,$location服務的getter、setter通過HTML5的History API與瀏覽器URL進行交互,允許使用正規(guī)的path、search模塊,代替hashbang的模式。如果部分瀏覽器不支持HTML5 History API,$location服務會自動退回到使用hashbang URL的模式。為了讓我們能夠從不清楚顯示我們的應用的瀏覽器是否支持history API的擔心中解脫出來,使用$location服務是一個正確的、最佳的選擇。
在舊版瀏覽器中打開一個正規(guī)的URL會轉換為hashbangURL。
在現(xiàn)代瀏覽器中打開一個hashbangURL,會重寫為一個正規(guī)的URL。
1. 向前兼容舊版瀏覽器
對于支持HTML5 history API的瀏覽器,$location回使用它們去寫path和search。如果瀏覽器不支持history API,$location會轉為提供Hashbang URL。這是$location服務自動轉換的。
2. HTML link rewriting
當我們使用history API mode的時候,我們對于不同的瀏覽器,需要不同的鏈接,但我們只需要提供正規(guī)的URL即可,例如<a href=”/some?foo=bar”>link</a>
當用戶單擊這個超鏈接時:
在舊的瀏覽器中,URL會改為/index.html#!/some?foo=bar
在現(xiàn)代瀏覽器中,URL會改為/some?foo=bar
在下面的情況中,鏈接不會被重寫,而是會使頁面加載到對應Url中:
包含target的超鏈接:<a href="/ext/link?a=b" target="_self">link</a>
到不同domain的絕對鏈接:<a >link</a>
設置了base路徑后,通過” /”開頭的鏈接到不同base路徑的超鏈接:<a href="/not-my-base/link">link</a>
3. server side
使用這個方式,在服務端請求URL重定向,通常,我們需要重定向我們所有的鏈接到我們的應用中。(例如index.html)。
4. Crawling your app
與之前相同
5. Relative links
確保檢查所有相對鏈接、圖片、腳本等。我們必須在<head>中指定base url(<base href="/my-base">),并在所有地方使用絕對url(以/開頭)。因為相對URL會根據(jù)document的初始路徑(通常與應用的root有所不同),轉化為絕對url。(relative urls will be resolved to absolute urls using the initial absolute url of the document, which is often different from the root of the application)。
我們十分鼓勵在document root中運行允許使用History API的angular應用,因為這很好地照顧到相對鏈接的問題。
6. Sending links among different browsers
?。ㄟ@里講解了兩種模式的地址可以適應不同瀏覽器,自動轉換,又重復講一次……)
7. 例子
在這例子中,可以看到兩個$location實例,兩個都是html5 mode,但在不同的瀏覽器上,所以我們可以看到兩者之間的不同點。這些$location服務與兩個假的“瀏覽器”連接。每一個input代表瀏覽器的地址欄。
注意,當我們輸入hashbang地址到第一個“瀏覽器”(或者第二個?),它不會重寫或重定向另外的Url,這個轉換過程只會發(fā)生在page reload的時候。
<!DOCTYPE html> <html ng-app> <head> <base href=""/> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title>fake-browser</title> <meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible"> <style type="text/css"> .ng-cloak { display: none; } </style> </head> <body> <div ng-non-bindable class="html5-hashbang-example"> <div id="html5-mode" ng-controller="Html5Cntl"> <h4>Browser with History API</h4> <div ng-address-bar browser="html5"></div><br><br> $location.protocol() = {{$location.protocol()}}<br> $location.host() = {{$location.host()}}<br> $location.port() = {{$location.port()}}<br> $location.path() = {{$location.path()}}<br> $location.search() = {{$location.search()}}<br> $location.hash() = {{$location.hash()}}<br> <a >/base/first?a=b</a> | <a >sec/ond?flag#hash</a> | <a href="/other-base/another?search">external</a> </div> <div id="hashbang-mode" ng-controller="HashbangCntl"> <h4>Browser without History API</h4> <div ng-address-bar browser="hashbang"></div><br><br> $location.protocol() = {{$location.protocol()}}<br> $location.host() = {{$location.host()}}<br> $location.port() = {{$location.port()}}<br> $location.path() = {{$location.path()}}<br> $location.search() = {{$location.search()}}<br> $location.hash() = {{$location.hash()}}<br> <a >/base/first?a=b</a> | <a >sec/ond?flag#hash</a> | <a href="/other-base/another?search">external</a> </div> </div> <script src="../angular.js" type="text/javascript"></script> <script type="text/javascript"> function FakeBrowser(initUrl, baseHref) { this.onUrlChange = function(fn) { this.urlChange = fn; }; this.url = function() { return initUrl; }; this.defer = function(fn, delay) { setTimeout(function() { fn(); }, delay || 0); }; this.baseHref = function() { return baseHref; }; this.notifyWhenOutstandingRequests = angular.noop; } var browsers = { html5: new FakeBrowser('http://www.host.com/base/path?a=b#h', '/base/index.html'), hashbang: new FakeBrowser('http://www.host.com/base/index.html#!/path?a=b#h', '/base/index.html') }; function Html5Cntl($scope, $location) { $scope.$location = $location; } function HashbangCntl($scope, $location) { $scope.$location = $location; } function initEnv(name) { var root = angular.element(document.getElementById(name + '-mode')); angular.bootstrap(root, [ function ($compileProvider, $locationProvider, $provide) { debugger; $locationProvider.html5Mode(true).hashPrefix('!'); $provide.value('$browser', browsers[name]); $provide.value('$document', root); $provide.value('$sniffer', {history:name == 'html5'}); $compileProvider.directive('ngAddressBar', function () { return function (scope, elm, attrs) { var browser = browsers[attrs.browser], input = angular.element('<input type="text" style="width:400px;">').val(browser.url()), delay; input.bind('keypress keyup keydown', function () { if (!delay) { delay = setTimeout(fireUrlChange, 250); } }); browser.url = function (url) { return input.val(url); }; elm.append('Address: ').append(input); function fireUrlChange() { delay = null; browser.urlChange(input.val()); } }; }); } ]); root.bind('click', function (e) { e.stopPropagation(); }); } initEnv('html5'); initEnv('hashbang'); </script> </body> </html>
五、附加說明
1. Page reload navigation
$location服務僅僅允許我們改變URl;它不允許我們重新加載頁面(reload the page)。當我們需要改變URL且reload page或者跳轉到其他頁面時,我們需要使用低級點得API,$window.location.href。
2. Using $location outside of the scope life-cycle
$location知道angular的scope life-cycle。當瀏覽器的URL發(fā)生改變時,它會更新$location,并且調用$apply,所以所有$watcher和$observer都會得到通知。當我們再$digest階段中修改$location,不會出現(xiàn)任何問題;$location會將這次修改傳播到瀏覽器中,并且通知所有$watcher、$observer。當我們需要在angular外面改變$location時(例如在DOM事件中或者在測試中),我們必須調用$apply,以傳播這個變化。
3. $location.path() and ! or / prefixes
path可以直接使用”/”開始;$location.path()setter會在value沒有以”/”開頭時自動補上。
注意”!”前綴,在Hashbang mode中,不屬于$location.path()的一部分。它僅僅是hashPrefix。
六、Testing with the $location service
在測試中使用$location服務的時候,是處于angular scope life-cycle外面的。這意味著我們需要負責調用scope.apply()。
describe('serviceUnderTest', function() { beforeEach(module(function($provide) { $provide.factory('serviceUnderTest', function($location){ // whatever it does... }); }); it('should...', inject(function($location, $rootScope, serviceUnderTest) { $location.path('/new/path'); $rootScope.$apply(); // test whatever the service should do... })); });
七、Migrating from earlier AngularJS releases
在早期的angular中,$location使用hashPath或者hashSearch去處理path和search方法。在這個release中,當有需要的時候,$location服務處理path和search方法,然后使用那些獲得得信息去構成hashbang URL(例如http://server.com/#!/path?search=a)。
八、Two-way binding to $location
angular compiler當前不支持方法的雙向綁定(https://github.com/angular/angular.js/issues/404)。如果我們希望對$location對象實現(xiàn)雙向綁定(在input中使用ngModel directive),我們需要指定一個額外的model屬性(例如:locationPath),并加入兩個$watch,監(jiān)聽兩個方向上的$location更新,例如:
<input type="text" ng-model="locationPath" />
// js - controller $scope.$watch('locationPath', function(path) { $location.path(path); ); $scope.$watch('$location.path()', function(path) { scope.locationPath = path; });
以上就是關于AngularJs Using $location的資料整理,后續(xù)繼續(xù)補充相關資料,謝謝大家對本站的支持!
相關文章
Angular2中如何使用ngx-translate進行國際化
本篇文章主要介紹了Angular2中使用ngx-translate進行國際化,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-05-05Angular 理解module和injector,即依賴注入
本文主要介紹Angular 理解module和injector的知識,這里整理了相關知識,并詳細介紹了依賴注入的問題,有興趣的小伙伴可以參考下2016-09-09