ui-router 是 angular-ui
旗下一員大將,其 github README 介紹的主要特色如下:
- 使用
state
取代 angular 原生的route
- 可巢狀的
state
與<ui-view>
(view template directive) - 支援多個
<ui-view>
,可以給定名稱作為 reference 依據
特別點出來的原因在於今天要講的都不是這些,而是我打從一開始看到 ui-router
就想要使用它的真正原因
既不是巢狀 view ,也不是它的
state
,而是state
衍生出的 UI as route 能力。
所以是?
改變 URL 之後,能夠在不變動(也不重載) view 的內容的情況下吃到新的參數而產生不一樣的
state
。
用講的很抽象,這邊來看一個例子。
建議使用 popout 模式觀看,比較能夠看出網址列的變化。
從例子中可以發現這是應該是一個製作 webapp 常常會使用到的功能,接下來就來看看如何利用 ui-router
實作出來。
用 ui-router
實作
.config
設定
首先在 .config
內的 configuration 內容一般狀態可能如下:
$stateProvider
.state('home', {
url: '/home',
templateUrl: 'home.html',
controller: 'HomeCtrl'
});
這時候需要利用 ui-router
的巢狀特性,讓子 state
改變的時候不會去 reload 當前(母 state
)的 <ui-view>
內容。
由於 home.child
只是做為我們更新 $stateParams
的手段,它根本不需要 <ui-view>
directive,這邊就不將 template / templateUrl
及 controller
選項加入它的 state
configuration 中(controller
在沒有 template
的情況下還是會 initalize,不過由於已經沒有 on-screen 的 attach 目標,這邊索性不加)。
$stateProvider
.state('home', {
url: '/home',
templateUrl: 'home.html',
controller: 'HomeCtrl'
})
.state('home.child', {
url: '/:pos'
});
感知變動並更新 view
一般標準的 $stateChangeSuccess
之後會觸發 controller
去 attach 新的 scope
到目標的 <ui-view>
上,因此我們會把每次 state change 之後執行的內容寫在綁定的 controller
裡面;然而在上面的設定之下,我們已經沒有會跟著 initialize 的 controller
,因此需要一些寫法來幫助我們執行在 UI as route
的 state change 之後需要進行的動作。
直接綁定 $stateParams
可以選擇綁在任一上級 $scope
或是 $rootScope
上,綁定的 view model 名稱可以隨意更改(這邊用的是 $stateParams
),以不要與 view 裡面有用到的混淆為準則。
/**
* 綁在 $rootScope
*/
.run(function ($rootScope, $stateParams) {
$rootScope.$stateParams = $stateParams;
})
/**
* 綁在任一上級 $scope
*/
.controller('MainCtrl', function ($scope, $stateParams) {
$scope.$stateParams = $stateParams;
})
在 view 裡面使用(假定 pos 輸入為數字):
<div class="index-number" ng-class="['a', 'b', 'c', 'd'][$stateParams.pos]">
目前的 pos: {{ $stateParams.pos }}
</div>
使用 $scope.$on
用 $scope.$on
的好處是可以擺在特定想要應對的位置,就結構上來講比較沒有用到 scope inheritance,或許對整個 app 的架構有正面的幫助。
.controller('SomeCtrl', function ($scope, $stateParams) {
$scope.$on('$stateChangeSuccess', function () {
$scope.params = angular.copy($stateParams);
});
})
在 view 裡面使用(假定 pos 輸入為數字):
<div class="index-number" ng-class="['a', 'b', 'c', 'd'][params.pos]">
目前的 pos: {{ params.pos }}
</div>
使用 $scope.$watch
$scope.$watch
的用法類似 $scope.$on
。
.controller('SomeCtrl', function ($scope, $stateParams) {
$scope.$watch(function (scope) {
return $stateParams;
}, function (newParams, oldParams) {
$scope.params = newParams;
});
});
在 view 裡面使用(假定 pos 輸入為數字):
<div class="index-number" ng-class="['a', 'b', 'c', 'd'][params.pos]">
目前的 pos: {{ params.pos }}
</div>
不同的 UI as route
最後,做為觸類旁通的這個範例展示兩種不同的 UI as route
作法:
- 使用
state
的 parameter -#/?theater=1
- 使用
state
-#/theater
一樣建議使用 popout 模式觀看。
後記
ui-router
這 module 真的讓 angular app 有了更多的可能性,也期待它繼續發展下去。
之後也會有更多關於 ui-router
的文章喔!