本篇接著上一篇 angular的uiRouter服務學習(1) 繼續講解uiRouter的用法
本篇主要講解uiRouter的巢狀狀態&巢狀檢視
巢狀狀態的方法:
狀態和狀態之間可以互相巢狀,狀態的巢狀共有以下幾種方式:
1.使用'.state()'進行巢狀. 比如 .state('contact',{}).state('contact.list',{})
2.使用 ui-router.stateHelper 來建立狀態巢狀樹. 這種方式需要另外引入依賴,所以很少被使用.也就不具體詳解了
3.定義狀態的'parent'屬性,屬性值是一個字串,就是父狀態的名字.比如 {parent:'contacts'}
4.定義狀態的'parent'屬性,屬性值是一個物件,物件就是父狀態物件.比如 {parent:contacts}
使用.state()來建立巢狀狀態:
可以對$stateProvider使用.state()來指定狀態的巢狀繼承,比如下面的例子:contacts.list就是contacts的一個子狀態:
html:
<div> <a href="contacts">檢視檢視</a> <a href="contacts/list">檢視巢狀檢視</a> <ui-view>點選連結後內容會被載入在這裡</ui-view> </div>
js
var nest = angular.module('nest',['ui.router']); nest.config(function($stateProvider){ $stateProvider.state('contacts',{ url:'/contacts', templateUrl:'contacts.html' }).state('contacts.list',{ url:'/list', templateUrl:'contacts.list.html' }) });
contacts.html
<h4>聯絡人列表:</h4> <ui-view></ui-view>
contacts.list.html
<p>我是contacts.list的內容</p>
*需要注意的是,子狀態的url是以父狀態的url為baseUrl的.所以這裡子狀態的url應該是'/contacts/list'
定義狀態的'parent'屬性,屬性值是一個字串:
可以通過狀態的parent屬性來指定它的父狀態.
js:
nest.config(function($stateProvider){ $stateProvider.state('contacts',{ url:'/contacts', templateUrl:'contacts.html' }).state('list',{ url:'/list', templateUrl:'contacts.list.html', parent:'contacts' }) });
這種方式和上一中方式的區別在於,子狀態不需要使用 '父狀態.子狀態' 這種格式.但是需要指定子狀態的parent屬性為'父狀態名'
基於物件的狀態實現巢狀狀態:
如果你不喜歡上面說到的兩種方式,那麼還可以使用基於物件的狀態: 為狀態新增一個name屬性,然後給子狀態設定parent屬性為父狀態物件.
需要注意的是,使用這種方式,子狀態的name屬性命名規則必須和.state()方法一樣,按照'父狀態.子狀態'的格式來命名
var contacts = { name:'parent', //特意不取名叫'contacts' url:'/contacts', templateUrl:'contacts.html' }; var list = { name:'parent.child', url:'/list', templateUrl:'contacts.list.html', parent:contacts }; nest.config(function($locationProvider,$stateProvider){ $stateProvider.state(contacts).state(list); });
狀態的註冊順序:
狀態的註冊順序無關緊要,你也可以先註冊子狀態再註冊父狀態.它會被放在佇列裡直到父狀態被註冊的時候才被註冊.
父狀態必須存在:
如果你只註冊了一個子狀態,比如contacts.list, 你就必須定義一個叫做contacts的父狀態,(定義先後順序無所謂,但一定要定義),否則的話,將不會有狀態被註冊.contacts.list這個狀態會被放在佇列中,直到contacts狀態被定義. 如果你不定義父狀態,也不會有任何的報錯.所以需要注意,為了讓子狀態生效,必須定義父狀態.
狀態取名規則:
沒有任何兩個狀態可以擁有相同的名字. 當使用.state()來建立巢狀狀態的時候,父狀態沒有被指定,而是通過推斷得到的. 當不使用.state()來建立巢狀狀態的時候,parent屬性必須被定義,但即使是兩個擁有不同父狀態的子狀態,你依然不能給它們取相同的名字.
狀態和檢視的巢狀:
當應用處於一個特定的狀態時(也就是某個狀態被啟用時),這個狀態的所有祖先狀態也都被啟用了. 比如上面的所有例子: 當 'contacts.list' 狀態被啟用,'contacts' 狀態也被隱式的啟用了,因為它是 'contacts.list'的父狀態.
子狀態會把它的檢視模板載入到父狀態的模板的 ui-view 元素中去.
比如上面的這些例子,只要<a href="contacts/list">檢視巢狀檢視</a>被點選,contacts.list狀態被啟用,contacts狀態也會被隱式的啟用.
子狀態從父狀態繼承了什麼?
子狀態從父狀態繼承了以下兩項:
其餘的,比如控制器,模板,url,等都不會被繼承...
resolve繼承的栗子:
核心程式碼:
var contacts = { name:'parent', url:'/contacts', templateUrl:'contacts.html', resolve:{ resA:function(){ return {value:'A'} } }, controller:function($scope,resA){ $scope.a = resA.value } }; var list = { name:'parent.child', url:'/list', templateUrl:'contacts.list.html', parent:contacts, resolve:{ resB:function(resA){ return {value:resA.value+'B'} } }, controller:function($scope,resB){ $scope.b = resB.value } }; nest.config(function($locationProvider,$stateProvider){ $locationProvider.html5Mode({enabled:true}).hashPrefix('!'); $stateProvider.state(contacts).state(list); });
contacts.html:
{{a}} //A
contacts.list.html:
{{a}} //A
{{b}} //AB
可以看到,contacts.list子狀態繼承了contacts父狀態裡resolve的依賴'resA'
需要注意: 如果你想在子狀態例項化之前解析完父狀態的resolve項裡的某個promise依賴,那麼,這個promise依賴必須被注入到子狀態裡去.
data繼承的栗子:
核心程式碼:
var contacts = { name:'parent', url:'/contacts', templateUrl:'contacts.html', data:{ dataA:'a', dataB:'b' }, controller:function($scope,$state){ $scope.a = $state.current.data.dataA; } }; var list = { name:'parent.child', url:'/list', templateUrl:'contacts.list.html', parent:contacts, controller:function($scope,$state){ $scope.a = $state.current.data.dataA; $scope.b = $state.current.data.dataB; } }; nest.config(function($locationProvider,$stateProvider){ $locationProvider.html5Mode({enabled:true}).hashPrefix('!'); $stateProvider.state(contacts).state(list); });
contacts.html:
{{a}} //a
contacts.list.html:
{{a}} //a
{{b}} //b
可以看到,父狀態的data屬性都被子狀態繼承了
狀態的繼承是基於檢視的繼承的
注意,scope作用域的繼承和狀態的繼承無關,它只和檢視之間的巢狀關係有關.
很有可能有這種情況: 子狀態的檢視不僅用於被填充到父狀態的檢視中,也可能被填充到很多其他地方.在這種情況下.只有被填充到父狀態的子狀態的檢視,才能繼承父狀態的scope,這種繼承只是angular本身的scope繼承機制,和狀態繼承機制是無關的.所以被填充到其他地方的檢視是不能訪問到父狀態的scope的.
抽象狀態
一個抽象狀態它擁有自己的子狀態,但是它不能啟用自己. 它只能當子狀態被啟用的時候,隱式的被啟用.
如果要讓一個狀態成為抽象狀態,需要給它設定abstract屬性為true
下面是幾種抽象狀態常用的場合:
- 給子狀態們的url提供一個基礎url.
- 插入帶有ui-view(s)的檢視模板,顯示子狀態們通用的內容,而ui-view部分供各個子狀態不同的檢視模板來填充.
- 可以給檢視新增一個控制器
- 另外,這樣也可以把$scope物件繼承給子狀態.但是注意,就如同上一點所說的,這是通過angular的scope的檢視繼承機制實現的,不是狀態繼承
- 通過data屬性,給子狀態們提供資料.
- 通過resolve屬性,給子狀態們提供依賴.
- 通過觸發onEnter和onExit回撥,來處理一些事情.
- 以上幾種情況的混合.
注意: 抽象狀態也需要帶有<ui-view>元素來讓子狀態檢視填充,所以,如果你建立抽象狀態是為了給子狀態提供基礎url,提供resolve依賴,data資料,或者呼叫onEnter和onExit回撥,那麼你需要在template屬性中設定檢視模板為:''<ui-view/>".
下面簡單舉幾個栗子:
栗子1:給子狀態們的url提供一個基礎url.
html:
<div> <a href="contacts">檢視檢視</a> <a href="contacts/list">檢視巢狀檢視1</a> <a href="contacts/detail">檢視巢狀檢視2</a> <ui-view>點選連結後內容會被載入在這裡</ui-view> </div>
js:
var contacts = { abstract:true, name:'parent', url:'/contacts', template:'<ui-view/>' }; var list = { name:'parent.child', url:'/list', templateUrl:'contacts.list.html', parent:contacts }; var detail = { name:'parent.detail', url:'/detail', templateUrl:'contacts.detail.html', parent:contacts }; nest.config(function($locationProvider,$stateProvider){ $locationProvider.html5Mode({enabled:true}).hashPrefix('!'); $stateProvider.state(contacts).state(list).state(detail); });
contacts.list.html:
<p>我是contacts.detail</p>
contacts.detail.html:
<p>我是contacts.detail</p>
當url變為'contacts/list'和'contacts/detail'的時候,會啟用相應的狀態
------------------------------------------------------------------------------------------------------------------------------------------
栗子2: 父狀態插入帶有ui-view(s)的檢視模板,顯示子狀態們通用的內容,而ui-view部分供各個子狀態不同的檢視模板來填充.
html同上.
js:
var contacts = { abstract:true, name:'parent', url:'/contacts', templateUrl:'contacts.html' }; var list = { name:'parent.child', url:'/list', templateUrl:'contacts.list.html', parent:contacts }; var detail = { name:'parent.detail', url:'/detail', templateUrl:'contacts.detail.html', parent:contacts }; nest.config(function($locationProvider,$stateProvider){ $locationProvider.html5Mode({enabled:true}).hashPrefix('!'); $stateProvider.state(contacts).state(list).state(detail); });
contacts.html:
<h4>聯絡人列表:</h4> <ui-view></ui-view>
無論切換哪個子狀態,contacts.html中的<h4>聯絡人列表:</h4>部分都是通用的.
------------------------------------------------------------------------------------------------------------------------------------------
栗子3: 混合使用
html:
<div> <a href="contacts">檢視檢視</a> <a href="contacts/list">檢視巢狀檢視1</a> <ui-view>點選連結後內容會被載入在這裡</ui-view> </div>
js:
var contacts = { abstract:true, name:'parent', url:'/contacts', templateUrl:'contacts.html', controller:function($scope){ $scope.contacts = [{id:0,name:'code_bunny'},{id:1,name:'white_bunny'},{id:2,name:'black_bunny'}] } }; var list = { name:'parent.child', url:'/list', templateUrl:'contacts.list.html', parent:contacts }; var detail = { name:'parent.detail', url:'/detail/:id', templateUrl:'contacts.detail.html', parent:contacts, controller:function($scope,$stateParams){ $scope.id = $stateParams.id } }; nest.config(function($locationProvider,$stateProvider){ $locationProvider.html5Mode({enabled:true}).hashPrefix('!'); $stateProvider.state(contacts).state(list).state(detail); });
contacts.html:
<h4>聯絡人列表:</h4> <ui-view></ui-view>
contacts.list.html:
<ul> <li ng-repeat="contact in contacts"> <a href="contacts/detail/{{contact.id}}">{{contact.name}}</a> </li> </ul>
contacts.detail.html:
<p>{{contacts[id]['name']}}</p>
完整程式碼: https://github.com/OOP-Code-Bunny/angular/tree/master/uiRouter
參考網站: https://github.com/angular-ui/ui-router/wiki/Nested-States-%26-Nested-Views