# 深入理解ANGULARUI路由_UI-ROUTER
最近在用?ionic寫個webapp?看到幾個demo中路由有好幾種,搞的有點暈,查下資料研究下,做個筆記,其中大部分為摘抄別人的,做個說明免得被人吐槽。
### Angularjs?ui-router?-?組件:
### $state?/?$stateProvider:
管理狀態定義、當前狀態和狀態轉換。包含觸發狀態轉換的事件和回調函數,異步解決目標狀態的任何依賴項,更新$location到當前狀態。由于狀態包含關聯的?url,通過$urlRouterProvider生成一個路由規則來執行轉換的狀態。
### ui-view指示器:
渲染狀態中定義的視圖,是狀態中定義的視圖的一個占位符。
### $urlRouter?/?$urlRouterProvider:
管理了一套路由規則列表來處理當$location發生變化時如何跳轉。最低級的方式是,規則可以是任意函數,來檢查$location,并在處理完成時候返回true。支持正則表達式規則和通過$urlMatcherFactory編譯的UrlMatcher對象的?url?占位符規則。
### $urlMatcherFactory:
將?url和占位符編譯為UrlMatcher對象。除了$routeProvider支持的占位符語法之外,它還支持擴展語法,允許一個正則表達式指定占位符,并且能夠提取命名參數和查詢url的一部分。
### $templateFactory:
?通過$http?/?$templateCache來加載模板,供狀態配置中使用。
AngularJS不需要任何第三方庫,利用自身集成的各個模塊便可開發出功能齊全的web應用,不過活躍的AngularJS社區也開發了很多能夠最大限度強化web應用的編程庫。本文帶讀者了解專業開發使用的模塊AngularUI中的ui-路由(ui-router)。AngularUI庫已經被分成了幾個模塊,用戶可以只選擇載入自己感興趣的模塊,而不用載入整個庫。
### UI-Router
UI-Router被認為是AngularUI為開發者提供的最實用的一個模塊,它是一個讓開發者能夠根據URL狀態或者說是'機器狀態'來組織和控制界面UI的渲染,而不是僅僅只改變路由(傳統AngularJS應用實用的方式)。該模塊為開發者提供了很多對視圖(view)額外的控制。開發者可以創建嵌套分層的視圖、在同一個頁面使用多個視圖、讓多個視圖控制某個視圖等更多的功能。即使是非常復雜的web應用,UI-Router也可以極佳地駕馭。
### 安裝
安裝方式可以選擇下載發行版本或者使用Bower(前端包管理器):
$?bower?install?angular-ui-router?--save
同時也需要將源文件包含到頁面中:
<script?type="text/javascript"?src="app/bower_components/angular-ui-router/release/angular-ui-router.js"></script>
接下來,將UI-Router作為web應用的依賴,注入到主程序:
angular.module('myApp',?['ui.router']);
與集成的ngRoute服務不同的是,UI-Router可以將視圖嵌套,因為它基于的是操作狀態而僅非URL。與傳統做法使用ng-view不同的是,在ngRoute里需要使用ui-view服務。當在ui-router中處理路由和狀態時,開發者的重心是當前的狀態是什么以及在哪一個頁面里。
~~~
????<div?ng-controller="DemoController">
??????<div?ui-view></div>
????</div>
~~~
和ngRoute一樣,為特定狀態指定的模板將會放在<div?ui-view></div>元素中。在這些模板中也可以包含自己的ui-view,這就是在同一個路由下實現嵌套視圖的方法。要定義一個路由,與傳統的方法相同:使用.config方式,但使用的不是$routeProvider而是$stateProvider。
~~~
????.config(function($stateProvider,?$urlRouterProvider)?{
??????$stateProvider
????????.state('start',?{
??????????url:?'/start',
??????????templateUrl:?'partials/start.html'
????????})
????});
~~~
上述代碼在設置對象上定義了一個叫start的狀態。設置對象stateConfig和路由設置對象的選項是非常相似的。
### 模板,模板路徑,模板Provider
開發者可以在每個視圖下使用如下方式來設置模板?-?template?-?HTML字符串,或者是返回HTML字符串的函數?-?templateUrl?-?HTML模板的路徑,或者是返回HTML模板路徑的函數?-?templateProvider?-?返回HTML字符串的函數?例如:
~~~
?????$stateProvider.state('home',?{
??????template:?'<h1>Hello?{{?name?}}</h1>'
????});
~~~
### 控制器
和ngRoute相似,開發者可以指定任何已經被注冊的控制器,或者在路由里面創建一個作為控制器的函數。但如果沒有定義模板,控制器將無效。
### 預載入Resolve
使用預載入功能,開發者可以預先載入一系列依賴或者數據,然后注入到控制器中。在ngRoute中resolve選項可以允許開發者在路由到達前載入數據保證(promises)。在使用這個選項時比使用angular-route有更大的自由度。
預載入選項需要一個對象,這個對象的key即要注入到控制器的依賴,這個對象的value為需要被載入的factory服務。
如果傳入的是字符串,angular-route會試圖匹配已經注冊的服務。如果傳入的是函數,該函數將會被注入,并且該函數返回的值便是控制器的依賴之一。如果該函數返回一個數據保證(promise),這個數據保證將在控制器被實例化前被預先載入并且數據會被注入到控制器中。
~~~
????$stateProvider.state('home',?{
??????resolve:?{
????????//這個函數的值會被直接返回,因為它不是數據保證
????????person:?function()?{
??????????return?{
????????????name:?"Ari",
????????????email:?"ari@fullstack.io"
??????????}
????????},
????????//這個函數為數據保證,?因此它將在控制器被實例化之前載入。
????????currentDetails:?function($http)?{
??????????return?$http({
????????????method:?'JSONP',
????????????url:?'/current_details'
??????????});
????????},
????????//前一個數據保證也可作為依賴注入到其他數據保證中!(這個非常實用)
????????facebookId:?function($http,?currentDetails)?{
??????????$http({
????????????method:?'GET',
????????????url:?'http://facebook.com/api/current_user',
????????????params:?{
??????????????email:?currentDetails.data.emails[0]
????????????}
??????????})
????????}
??????},
??????//定義控制器
??????controller:?function($scope,?person,?currentDetails,?facebookId)?{
??????????$scope.person?=?person;
??????}
????})
~~~
### URL
url選項將會為該應用的狀態指定一個URL基于用戶瀏覽該應用所在的狀態。這樣當在瀏覽該應用的時候便能實現深度鏈接的效果。?該選項與ngRoute的URL相似,但可以被視為對ngRoute主要的升級,在接下來的文章里你便會認可這一點。開發者可以這樣指定一個基本的路由。
~~~
????$stateProvider
??????.state('inbox',?{
????????url:?'/inbox',
????????template:?'<h1>Welcome?to?your?inbox</h1>'
??????});
~~~
當用戶瀏覽到/inbox時,該應用將狀態改為inbox同時向主ui-view元素中插入模板中的內容('Welcome?to?your?inbox')。URL參數有多個選項,因此它非常強大。開發者可以像設置ngRoute一樣設置最基本的參數:
~~~
????$stateProvider
??????.state('inbox',?{
????????url:?'/inbox/:inboxId',
????????template:?'<h1>Welcome?to?your?inbox</h1>',
????????controller:?function($scope,?$stateParams)?{
??????????$scope.inboxId?=?$stateParams.inboxId;
????????}
??????});
~~~
現在將:inboxId作為URL的第二個部分,例如:訪問/inbox/1,那么$stateParams.inboxId就為1($stateParams為{inboxId:1})。同時也可使用不同的語法:
????url:?'/inbox/{inboxId}'
路徑必須匹配URL,與ngRoute不同的是,當用戶訪問到/inbox/時,上面的的路徑會被激活,然而當訪問到/inbox時不會被激活。路徑同時也使開發者可以使用正則表達式來匹配,例如:
~~~
????//?限定id為6位16進制數字
????url:?'/inbox/{inboxId:[0-9a-fA-F]{6}}',
????//?或者
????//?匹配任何在?`/inbox`后面的url(慎用)并匹配值到indexId
????url:?'/inbox/{inboxId:.*}'
注意,在路由中目前還無法使用路由組,路由數據預載入器無法預載入。
在路徑里可以指定查詢參數:
????//?/inbox?sort=ascending?將會被匹配
????url:?'/inbox?sort'
~~~
### 嵌套路由
使用url參數可以實現嵌套的路由,有了嵌套路由便可在同一個模板同一個路由實現多層次的ui-view,例如在/inbox中嵌入更多路由:
~~~
????$stateProvider
??????.state('inbox',?{
????????url:?'/inbox/:inboxId',
????????template:?'<div><h1>Welcome?to?your?inbox</h1>\
????????????????<a?ui-sref="inbox.priority">Show?priority</a>\
????????????????<div?ui-view></div>\
????????????????</div>',
????????controller:?function($scope,?$stateParams)?{
??????????$scope.inboxId?=?$stateParams.inboxId;
????????}
??????})
??????.state('inbox.priority',?{
????????url:?'/priority',
????????template:?'<h2>Your?priority?inbox</h2>'
??????});
~~~
第一個路由是傳統的,注意第二個,它是/inbox下的一個子路由:state(?.?)語法指定了它使子路由。/inbox/1將匹配第一個路由,而/index/1/priority會匹配第二個路由。使用這種語法,在父視圖中的ui-view元素將會由第二個路由控制。
Params?路由參數
params選項是一個包含路徑中的參數和正則表達式匹配結果的數組。該選項不能和url選項混用!當某狀態被激活時,應用將這個數組賦值給$stateParams服務。
Views?視圖
開發者可以在一個狀態中設置多個有名稱的視圖。該功能在ui-router中很強大,開發者可以在同一個模板中改變和切換不同的視圖。
<如果設置了視圖選項,則該狀態的‘template’,‘templateUrl’及‘templateProvider’將被忽略。如果想在路由里包含父級模板,就需要創建一個包含模板的抽象模板。
例如有這樣的視圖:
~~~
<div>
??<div?ui-view="filters"></div>
??<div?ui-view="mailbox"></div>
??<div?ui-view="priority"></div>
</div>
~~~
接下來就可以創建將被分別被插入到上述ui-view的有命名的視圖了,每個子視圖可以包含自己的模板、控制器和預載入數據。
~~~
$stateProvider
??.state('inbox',?{
????views:?{
??????'filters':?{
????????template:?'<h4>Filter?inbox</h4>',
????????controller:?function($scope)?{}
??????},
??????'mailbox':?{
????????templateUrl:?'partials/mailbox.html'
??????},
??????'priority':?{
????????template:?'<h4>Priority?inbox</h4>',
????????resolve:?{
??????????facebook:?function()?{
????????????return?FB.messages();
??????????}
????????}
??????}
????}
??});
~~~
### abstract?抽象模板
抽象模板不能被激活,但是它的子模板可以被激活。抽象模板可以提供一個包括了多個有名的視圖的模板,或者它可以傳遞作用域變量$scope給子模板。使用它可以在同一個url下傳遞自定義數據或者預載入的依賴。除了需要添加abstract屬性外,其他設置和設定一個常規狀態是相同的:
~~~
$stateProvider
??.state('admin',?{
????abstract:?true,
????url:?'/admin',
????template:?'<div?ui-view></div>'
??})
??.state('admin.index',?{
????url:?'/index',
????template:?'<h3>Admin?index</h3>'
??})
??.state('admin.users',?{
????url:?'/users',
????template:?'<ul>...</ul>'
??});
~~~
### onEnter,onExit?回調函數
當應用進入或者離開當前狀態的視圖時會調用這兩個函數。這兩個函數可以訪問預載入的數據。這兩個回調函數使開發者可以根據狀態改變來采取某些動作,例如在用戶要離開時可以彈出對話框‘你確定嗎?’以及防止意外操作等。
### Data?數據
自定義數據也可以被附加到狀態控制對象state?configObject.該數據和預載入數據resolve屬性相似,但是該數據不會被注入到控制器中,promise也不會被預載入,它的用途是從父狀態傳遞數據到子狀態。
### 事件
和ngRoute相同的是,angular-route服務會在不同的狀態生命周期lifecycle里啟動某些事件events。監聽$scope對象便可以捕獲這些事件然后采取不同的響應或者操作。如下的事件將會在$rootScope上觸發,因此在任何$scope對象上都可以監聽到這些事件。
### 狀態改變事件
~~~
$scope.$on('$stateChangeStart',?
function(evt,?toState,?toParams,?fromState,?fromParams),?{
??//?如果需要阻止事件的完成
??evt.preventDefault();
});
~~~
可以觸發的事件包括:
stateChangeStart
?
當狀態改變開始的時候被觸發
$stateChangeSuccess
當狀態改變成功后被觸發
$stateChangeError
當狀態改變遇到錯誤時被觸發,錯誤通常是目標無法載入,需要預載入的數據無法被載入等。
### 視圖載入事件
視圖載入階段ui-router也提供了一些事件
$viewContentLoading
當視圖正在被載入且在DOM被渲染之前觸發。
~~~
$scope.$on('$viewContentLoading',?
function(event,?viewConfig){?
????//?獲取任何視圖設置的參數,以及一個特殊的屬性:viewConfig.targetView
});
~~~
$viewContentLoaded
當視圖被載入且DOM已經渲染完成后被觸發。
$stateParams?狀態參數
在上面提及使用$stateparams來提取在url中的不同參數。該服務的作用是處理url的不同部分。例如,當上述的inbox狀態是這樣時:
url:?'/inbox/:inboxId/messages/{sorted}?from&to'
//當用戶訪問者鏈接時:
'/inbox/123/messages/ascending?from=10&to=20'
$stateParams對象的值為:
{inboxId:?'123',?sorted:?'ascending',?from:?10,?to:?20}
$urlRouterProvider
和ngRoute一樣,開發者可以在該對象上設定特定的URL被激活時做什么的規則。由于設定好的狀態在特定的url被訪問是會自動激活,所以$urlRouterProvider沒有必要用來管理激活和載入狀態。但當需要管理哪些被發生在當前狀態之外的作用域scope時它會非常有用,例如在重定向或者安全驗證的時候。在模塊的設置函數里便可使用$urlRouterProvider。
when()
該函數需要兩個參數:1.當前的路徑,2.需要重定向到的路徑(或者是需要在路徑被訪問是運行的函數)。設置重定向前需要為$urlRouterProvider設置when函數來接受一個字符串。例如,當希望重定向一個空的路由到/inbox:
~~~
.config(function($urlRouterProvider)?{
??$urlRouterProvider.when('',?'/inbox');
});
~~~
如果傳遞的是函數,在路徑被匹配時該函數會被執行,處理器返回如下3個值中的一個:?-?falsy,該回應告訴$urlRouter沒有匹配到當前url規則,應該嘗試匹配新的路徑,這樣能保證用戶訪問了正常的路徑。?-?字符串,$urlRouter將該字符串當做重定向的路徑。?-?TRUE?或者?undefined,該回應告訴$urlRouter,url已被處理
otherwise()
和ngRoute的otherwise()函數相似,在用戶提交的路徑沒有被定義的時候它將重定向到指定的頁面。這是個創建’默認‘路徑的好方法。?otherwise()只接受一個參數,要么函數要么字符串,字符串必須為合法的url路由地址,函數則會在沒有任何路徑被匹配的時候被運行。
~~~
.config(function($urlRouterProvider)?{
??$urlRouterProvider.otherwise('/');
??//?or
??$urlRouterProvider.otherwise(
????function($injector,?$location)?{
??????$location.path('/');
????});
});
rule()
~~~
如果想越過任何URL的匹配或者在其他路由前做路由修改,則可以使用rule()函數。在使用它的時候必須返回一個合法的代表路徑的字符串。
~~~
app.config(function($urlRouterProvider){
??$urlRouterProvider.rule(
????function($injector,?$location)?{
??????return?'/index';
????});
})
~~~
### 總結
本文涵蓋了ui-router深度及幾乎全部的功能。希望你也發現這個庫的強大和實用,并在下一個項目中實用這些強大的功能。
- 前言
- (一)深入理解ANGULARUI路由_UI-ROUTER
- (二)AngularJS路由問題解決
- (四)ANGULAR.JS實現下拉菜單單選
- (五)Angular實現下拉菜單多選
- (六)AngularJS+BootStrap實現彈出對話框
- (七)實現根據不同條件顯示不同控件
- (十)AngularJS改變元素顯示狀態
- (十四)AngularJS靈異代碼事件
- (十七)在AngularJS應用中實現微信認證授權遇到的坑
- (十八)在AngularJS應用中集成科大訊飛語音輸入功能
- (十九)在AngularJS應用中集成百度地圖實現定位功能
- (二十一)Angularjs中scope與rootscope區別及聯系
- (二十三)ANGULAR三宗罪之版本陷阱
- (二十四)AngularJS與單選框及多選框的雙向動態綁定
- (二十五)JS實現導入文件功能
- (二十七)實現二維碼信息的集成思路
- (二十八)解決AngualrJS頁面刷新導致異常顯示問題
- (二十九)AngularJS項目開發技巧之localStorage存儲
- (三十)AngularJS項目開發技巧之圖片預加載
- (三十一)AngularJS項目開發技巧之獲取模態對話框中的組件ID
- (三十二)書海拾貝之特殊的ng-src和ng-href
- (三十三)書海拾貝之簡介AngularJS中使用factory和service的方法
- (三十四)Angular數據更新不及時問題探討
- (三十六)AngularJS項目開發技巧之利用Service&Promise&Resolve解決圖片預加載問題(后記)