安裝
$ bower install angular-ui-router --save
<script type="text/javascript"
src="app/bower_components/angular-ui-router/release/angular-ui-router.js"></script>
angular.module('myApp', ['ui.router']);
現(xiàn)在,不同于內(nèi)置的ngRoute
服務,由于ui-router
基于狀態(tài)工作他宛,而不是簡單的url
,因此可以將它嵌套在視圖中欠气。
在處理ngRoute
服務時我們不再使用ng-view
厅各,而改為使用ui-view
指令。
在ui-router
內(nèi)處理路由和狀態(tài)時预柒,我們主要關心的是應用程序處在哪個狀態(tài)以及Web應用當前處在哪個路由位置队塘。
<div ng-controller="DemoController">
<div ui-view></div>
</div>
定義在任意給定狀態(tài)內(nèi)的模板都處在<div ui-view></div>
元素內(nèi)。此外卫旱,每個模板都可以包含自己的ui-view
人灼。 這事實上就允許你在路由中嵌套視圖。定義路由:
.config(function($stateProvider,$urlRouterProvider) {
$stateProvider.state('start', {
url: '/start',
templateUrl: 'partials/start.html'
})
});
這一步給狀態(tài)配置對象分配了一個名為start
的狀態(tài)顾翼。這個狀態(tài)配置對象的參數(shù)如下:
1.template投放、templateUrl、templateProvider
在每個視圖上設置模板的方式有三種适贸。
- template:一個HTML內(nèi)容字符串或者返回HTML的函數(shù)
- templateUrl:一個模板URL路徑字符串或者是返回URL路徑字符串的函數(shù)
- templateProvider:一個返回HTML內(nèi)容字符串的函數(shù)
2.controller
可以給已經(jīng)注冊好的控制器關聯(lián)一個URL(使用字符串)灸芳,也可以創(chuàng)建一個控制器函數(shù)作為狀態(tài)控制器。如果沒有定義模板拜姿,就不會創(chuàng)建這個控制器烙样。
3.resolve
我們還可以使用resolve
功能解析要注入到控制器中的依賴列表。這個resolve
選項就是一個對象蕊肥,其中鍵就是要注入到控制器中的依賴名稱谒获,而其值就是待解析的factories
。
如果傳入一個字符串壁却,angular-route
會嘗試匹配一個現(xiàn)有的已注冊的服務批狱。如果傳入一個函數(shù),則注入這個函數(shù)展东,而函數(shù)的返回值就是依賴赔硫。如果這個函數(shù)返回一個promise
,它會在控制器被實例化之前解析盐肃,同時其值(就像ngRoute
)會注入到控制器中爪膊。
$stateProvider.state('home', {
resolve: {
// 當結果不是promise時立即返回
person: function() {
return {
name: "Ari",
email: "ari@fullstack.io"
}
},
// 這個函數(shù)返回一個promise权悟,它會在控制器實例化之前解析
currentDetails: function($http) {
return $http({
method: 'JSONP',
url: '/current_details'
});
},
// 還可以在另一個解析中使用上面返回的promise
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;
}
});
4.url
url
選項可以給應用程序的狀態(tài)分配一個唯一的URL。這個url
選項提供了與深度鏈接同樣的功能推盛,它通過狀態(tài)導航應用峦阁,而不是簡單地通過URL導航。
基本路由可以像這樣指定:
$stateProvider
.state('inbox', {
url: '/inbox',
template: '<h1>Welcome to your inbox</h1>'
});
當用戶導航到/inbox
時小槐,應用會轉換到inbox
狀態(tài)拇派,然后使用模板內(nèi)容填充主要的ui-view
指令。
URL可以接受一系列不同的選項凿跳,它還可以在url
中設置基本的參數(shù)件豌。
$stateProvider
.state('inbox', {
url: '/inbox/:inboxId',
template: '<h1>Welcome to your inbox</h1>',
controller: function($scope, $stateParams) {
$scope.inboxId = $stateParams.inboxId;
}
});
應用會捕獲作為URL第二個組成部分的:inboxId
。例如控嗜,如果用戶轉換到/inbox/1
茧彤,$stateParams.inboxId
就會變成1(因為$stateParams
為{inboxId: 1}
)。
還可以使用不同的語法:
url: '/inbox/{inboxId}'
這里路徑必須與URL精確匹配疆栏。和ngRoute
不同曾掂,如果用戶導航到/inbox/
,上面的路徑能夠正常工作壁顶。但是珠洗,當導航導到/inbox
時,上述示例配置中的狀態(tài)不會被激活若专。
此外许蓖,你還可以在路徑參數(shù)內(nèi)使用正則表達式,因此你可以設置一個匹配路由的規(guī)則调衰。
// 只匹配包含6個十六進制數(shù)字的inbox ID
url: '/inbox/{inboxId: [0-9a-fA-f]{6}}',
// 或者匹配每個URL中`/inbox`后面的`inboxId`(全部捕獲)
url: '/inbox/{inboxId:.*} '
注意膊爪,不能在路由內(nèi)使用正則捕獲組,因為路由解析器將無法解析這個路由嚎莉。
甚至還可以在路由中指定查詢參數(shù):
// 匹配諸如/inbox?sort=ascending形式的路由
url: '/inbox?sort'
5.嵌套路由
你可以使用url
參數(shù)以插入路由的方式提供嵌套路由米酬。這讓你可以在頁面或者模板內(nèi)有多個ui-views
。
$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>'
});
第一個路由會按預期匹配∏髀幔現(xiàn)在這里有了第二個路由赃额,也就是一個匹配父路由inbox
之下的子路由。
-
/inbox/1
匹配第一個狀態(tài)叫确。 -
/inbox/1/priority
匹配第二個狀態(tài)跳芳。
使用這種語法,你可以在父路由內(nèi)嵌套URL启妹。父視圖中的ui-view
會解析priority
收件箱。
6.params
params
選項是一個參數(shù)名數(shù)組或者是一個正則表達式數(shù)組醉旦。不能將這個選項與url
選項聯(lián)合使用饶米。當狀態(tài)被激活時桨啃,這些參數(shù)會被填充到$stateParams
服務中。
7.views
ui-router
的一個強大的特性就是可以在一個狀態(tài)內(nèi)設置多個命名視圖檬输。在獨立的視圖內(nèi)照瘾,你可以在獨立模板中定義多個要引用的視圖。
如果設置了views
參數(shù)丧慈,那么狀態(tài)的templateUrl
析命、template
和templateProvider
就會被忽略。如果你想在路由中包含父模板逃默,就需要創(chuàng)建一個包含模板的抽象狀態(tài)鹃愤。
比方說我們有一個視圖看起來像這樣:
<div>
<div ui-view="filters"></div>
<div ui-view="mailbox"></div>
<div ui-view="priority"></div>
</div>
現(xiàn)在,你可以創(chuàng)建命名視圖來填充每個獨立的模板完域。每個子視圖都可以包含它自己的模板软吐、控制器和使用resolve
關鍵字解析的數(shù)據(jù)。
$stateProvider.state('inbox', {
views: {
'filters': {
template: '<h4>Filter inbox</h4>',
controller: function($scope) {}
},
'mailbox': {template: 'partials/mailbox.html'},
'priority': {
template: '<h4>Priority inbox</h4>',
resolve: {
facebook: function() {return FB.messages(); }
}
}
}
});
8.abstract
抽象模板永遠不能直接激活吟税,但是可以設置被激活的子節(jié)點凹耙。
你可以使用抽象模板提供一個模板包裝器來包裹多個命名視圖,或者傳遞$scope
對象給子節(jié)點肠仪。你還可以使用它們來傳遞解析后的依賴或者自定義數(shù)據(jù)肖抱,或者在同一url
下嵌套多個路由(比如,所有的路由都在/adminURL
之下)异旧。
設置抽象模板與設置常規(guī)狀態(tài)一樣意述,區(qū)別只在于設置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>'
});
9.onEnter、onExit
Angular會在用戶(分別)進入或者離開視圖時調用這些回調函數(shù)泽艘。對于這兩個選項欲险,你可以設置希望調用的函數(shù)。這些函數(shù)可以訪問被解析的數(shù)據(jù)匹涮。
這些回調函數(shù)讓你可以在新視圖上或者進入另一個狀態(tài)時觸發(fā)某個行為天试。使用它們可以很好地實現(xiàn)一個“你確定嗎?”形式的模態(tài)視圖然低,或者在用戶進入這個狀態(tài)之前要求用戶登錄喜每。
10.data
你可以附加任意數(shù)據(jù)給你的狀態(tài)配置對象configObject
。這個選項跟resolve
屬性很像雳攘,但是它的數(shù)據(jù)不會被注入到控制器中带兜,promise
也不會被解析。
當需要從父狀態(tài)給子狀態(tài)傳遞數(shù)據(jù)時吨灭,這個選項特別有用刚照。
事件
angular-route
服務會在狀態(tài)生命周期的不同階段觸發(fā)不同的事件。
在應用程序內(nèi)可以通過監(jiān)聽$scope
對象的方式附加函數(shù)給這些事件喧兄。以下所有事件都會觸發(fā)在$ootScope
上无畔,因此可以在任意$scope
對象上監(jiān)聽這些事件啊楚。
1.狀態(tài)改變事件
可以使用如下方式監(jiān)聽這個事件:
$scope.$on('$stateChangeStart',
function(evt, toState, roParams, fromState, fromParams) {
// 可以阻止這一狀態(tài)完成
evt.preventDefault();
});
這個事件可能會以如下方式觸發(fā)。
$stateChangeStart
從一個狀態(tài)開始過渡到另一個狀態(tài)時觸發(fā)這個事件浑彰。 $stateChangeSuccess
從一個狀態(tài)過渡到下一個狀態(tài)完成時觸發(fā)這個事件恭理。 $stateChangeError
當過渡期間發(fā)生錯誤時觸發(fā)這個事件。通常郭变,模板不能被解析或者解析promise
失敗時會引發(fā)錯誤颜价。
2.視圖加載事件
ui-router
還在視圖加載階段提供了事件。
$viewContentLoading
視圖開始加載時诉濒,DOM被渲染之前周伦,觸發(fā)這個事件。
你可以像這樣監(jiān)聽這個事件:
$scope.$on('$viewContentLoading',
function(event, viewConfig) {
// 在這里可以訪問所有視圖配置屬性
// 以及一個特殊的“targetView”屬性
// viewConfig.targetView
});
$viewContentLoaded
在視圖加載完成以及DOM渲染之后觸發(fā)這個事件循诉。
$stateParams
$stateParams
服務展示了如何根據(jù)URL的不同組成部分處理數(shù)據(jù)横辆。
例如,如果在inbox
狀態(tài)中有個看起來像這樣的URL:
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
你可以使用路由提供程序構建規(guī)則茄猫,規(guī)定當特定的URL被激活時會發(fā)生什么狈蚤。 創(chuàng)建的這些狀態(tài)負責在不同的URL中激活自身,因此不一定需要$urlRouterProvider
來管理激活和加載狀態(tài)划纽。當你想要管理發(fā)生在狀態(tài)作用域之外的行為時脆侮,它就可以派上用場了,比如重定向或者身份驗證勇劣。
你可以在模塊配置函數(shù)中使用$urlRouterProvider
靖避。
when()
when()
接受兩個參數(shù):想要匹配的入口路徑和用于重定向的路徑(或者是在路徑匹配時調用的函數(shù))。
為了設置重定向比默,需要給when
方法設置一個字符串參數(shù)幻捏。
例如,如果想將一個空路由重定向到/inbox:
.config(function($urlRouterProvider) {
$urlRouterProvider.when('', '/inbox');
});
如果傳入一個函數(shù)命咐,它會在路徑匹配時調用篡九。這個處理程序可能返回以下三個值中的一個。
- falsy:這個值告訴
$urlRouter
該規(guī)則不匹配醋奠,同時它應該嘗試找到一個不同的狀態(tài)來匹配榛臼。如果想要確保用戶可以正確地訪問一個URL,它將很有幫助窜司。 - 字符串:
$urlRouter
會把這個字符串值當作重定向的URL沛善。 - truthy or undefined:這個值讓
$urlRouter
知道已經(jīng)處理了URL。
otherwise()
otherwise()
方法在沒有其他路由匹配時發(fā)起重定向塞祈。這個方法是創(chuàng)建默認URL的一種很好的方式金刁。
otherwise()
方法接受一個參數(shù):一個字符串或者函數(shù)。
如果傳入一個字符串,任何無效或者不匹配的路由都會重定向到字符串指定的URL尤蛮。
如果傳入一個函數(shù)漠秋,它會在沒有其他路由匹配時被調用,同時負責處理返回結果抵屿。
.config(function() {
$urlRouterProvider.otherwise('/');
// 或者
$urlRouterProvider.otherwise(function($injector, $location) {
$location.path('/');
});
});
rule()
如果想要繞過所有的URL匹配,或者想要在操作其他路由之前對路由做一些操作捅位, 可以使用rule()
函數(shù)轧葛。
使用rule()
函數(shù)時必須返回一個有效路徑字符串。
.config(function($urlRouterProvider) {
$rulRouterProvider.rule(function($injector, $location) {
return '/index';
});
});
創(chuàng)建一個導航程序
當我們想要為用戶創(chuàng)建一個注冊向導的時候艇搀,就需要使用ui-router
了尿扯,這是一個非常合適的應用場景。
我們將使用ui-router
創(chuàng)建一個快速注冊服務焰雕,它包含一個控制器衷笋,用于處理注冊任務。首先矩屁,需要創(chuàng)建應用視圖:
<div ng-controller="WizardSignupController">
<h2>Signup wizard</h2>
<div ui-view></div>
</div>
接下來辟宗,在這個注冊向導中還需要有三個階段。
- start:在這個階段吝秕,我們獲取用戶名并向其介紹注冊向導泊脐。
- email:在這里,接受用戶的郵件烁峭。
- finish:此時容客,用戶完成注冊過程,我們要向其展示一個完整的頁面约郁。
在真實的應用中缩挑,finish
階段應該將注冊資料發(fā)送給服務器,同時進行真實的注冊 操作鬓梅。在這里供置,由于沒有后端,因此暫時只顯示這個視圖己肮。
這個注冊程序依賴于wizardapp.controllers
模塊士袄,我們將在其中編寫包含控制器:WizardSignupController
。
angular.module('wizardApp', [
'ui.router',
'wizardapp.controllers'
]);
WizardSignupController
簡單地提供了$scope.user
對象谎僻,在注冊過程以及注冊行為中娄柳,我 們都會使用這個對象。
angular.module('wizardapp.controllers', [])
.controller('WizardSignupController',
function($scope, $state) {
$scope.user = {};
$scope.signup = function() {}
});
向導程序邏輯覆蓋了大部分工作艘绍。你可以將這些邏輯設置到應用的config()
函數(shù)中:
angular.module('wizardApp', [
'ui.router', 'wizardapp.controllers'
])
.config(function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('start', {
url: '/step_1',
/templateUrl: 'partials/wizard/step_1.html'
})
.state('email', {
url: '/step_2',
templateUrl: 'partials/wizard/step_2.html'
})
.state('finish', {
url: '/finish',
templateUrl: 'partials/wizard/step_3.html'
});
});
設置這些選項之后赤拒,基本流程就全部完成了。現(xiàn)在,如果用戶導航到路由/step_1
挎挖,他們將被定向到流程的起點这敬。盡管整個流程也可以都發(fā)生在根URL上(即/step_1
),但你可能更希望將 它們放在子路由中(如/wizard/step_1
)蕉朵。
為此崔涂,只需要設置一個包裝其他步驟的abstract
狀態(tài)就可以了。
.config(function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('wizard', {
abstract: true,
url: '/wizard',
template: '<div><div ui-view></div></div>'
})
.state('wizard.start', {
url: '/step_1',
templateUrl: 'partials/wizard/step_1.html'
})
.state('wizard.email', {
url: '/step_2',
templateUrl: 'partials/wizard/step_2.html'
})
.state('wizard.finish', {
url: '/finish',
templateUrl: 'partials/wizard/step_3.html'
});
});
現(xiàn)在始衅,這些路由不再定義在頂級路由中了冷蚂,你可以將它們(子路由)安全地嵌套在/wizard
URL內(nèi)。
此外汛闸,我們還想在注冊程序的尾部附加一個功能:在父控制器WizardSignupController
上調用signup
函數(shù)蝙茶。我們只需在向導程序的尾部設置一個控制器來調用$scope
上的函數(shù)就行了。 由于整個向導程序都封裝在WizardSignupController
中诸老,這就表示可以正常使用作用域嵌套作用域屬性隆夯。
.state('wizard.finish',{
url: '/finish',
templateUrl: 'partials/wizard/step_3.html',
controller: function($scope) {
$scope.signup();
}