angular的uiRouter服務學習(2)

詩&遠方發表於2014-12-17

本篇接著上一篇 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狀態也會被隱式的啟用.

 

子狀態從父狀態繼承了什麼?

子狀態從父狀態繼承了以下兩項: 

  • 父狀態中通過resolve定義的依賴
  • 父狀態自定義的data屬性

     其餘的,比如控制器,模板,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

 

 

 

相關文章