angularJS路由框架ui-router

简介

AngularUI Router是AngularJS的路由框架,和默认的$route不同,它将所有路由包装成可划分层级的状态机状态,路由路径在ui-router中不是必须的。由于ui-router的路由状态机是分层级的,所以使用ui-router可以非常方便地创建包含多个嵌入的子模板。

Demo

直接使用ui-router的方式可以参考其github文档,这里以yeoman为例简单展示下ui-router的使用。

bower.json中加入ui-router依赖包,然后bower install执行安装

bower.json
1
2
3
4
5
6
7
8
9
{
  "name": "learn-angular",
  "version": "0.0.0",
  "dependencies": {
    /*省略其他*/
    "angular-ui-router": "^0.2.13"
  },
 /*省略其他*/
}

index.html中添加引用:

app/index.html
1
<script src="bower_components/angular-ui-router/release/angular-ui-router.js"></script>

在app.coffee中设置路由:

app/scripts/app.coffee
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
'use strict'

angular
  .module('learnAngularApp', [
    'ui.router'    # 添加模块依赖
  ])
  .run ['$rootScope', '$state', '$stateParams', ($rootScope,   $state,   $stateParams) ->
    $rootScope.$state = $state
    $rootScope.$stateParams = $stateParams
    ]
  .config ($stateProvider,$urlRouterProvider) ->
    $urlRouterProvider.otherwise "/"
    $stateProvider
      .state 'home',
        url: '/'                      # 可见默认路由状态是home
        templateUrl: 'views/home.html'
      .state 'state1',
        url: '/state1'
        templateUrl: 'views/state1.html'
      .state 'state1.list',
        url: '/list'
        templateUrl: 'views/state1.list.html'
        controller: ($scope) ->
          $scope.items = ["A", "List", "Of", "Items"]
      .state 'state2',
        url: '/state2'
        templateUrl: 'views/state2.html'
      .state 'state2.list',
        url: '/list'
        templateUrl: 'views/state2.list.html'
        controller: ($scope) ->
          $scope.things = ["A", "Set", "Of", "Things"]

ui-router最重要的三个服务$state,$stateProvider,$urlRouterProvider。在$stateProvider上配置所有的状态,state方法的第一个参数是状态名,第二个参数是一个hash对象,里面可以配置url,templateUrl,controller等。

注意其中类似state1.liststate1的状态,state1.liststate1的子状态,所以触发state1.list的url是父子状态的联合,即/state1+/list => /state1/list,所以当浏览器导航到/state1/list(或手动触发$state.go())时,state1.list.html才被插入父模板渲染。

下图非常清晰反映了ui-router的渲染逻辑:

  • 绿色 = 初始状态
  • 黄色 = 即时渲染
  • 蓝色 = 最终状态

渲染逻辑

主模板index.html:

app/index.html
1
2
3
4
5
6
7
8
<div>
    <ul>
      <li ui-sref-active="active"><a ui-sref="home">home</a></li>
      <li ui-sref-active="active" ><a ui-sref="state1">state1</a></li>
      <li ui-sref-active="active"><a ui-sref="state2">state2</a></li>
    </ul>
</div>
<div ui-view></div>

这里出现了两个ui-router的指令,ui-sref类似于angularjs的ng-href,只不过后面指定的是路由状态。

另一个指令就是ui-view,ui-router根据激活的状态向该指令中插入子模板。ui-router插入模板的规则是:ui-router会将激活状态的模板插入父状态模板的ui-view处。home状态是根状态,所以app/index.htmlui-view中插入的就是home状态的模板片段app/views/home.html

其他模板:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!-- app/views/state1.html -->
<h1>State 1</h1>
<hr/>
<a ui-sref="state1.list">Show List</a>
<div ui-view></div>
<!-- app/views/state1.list.html -->
<h3>List of State 1 Items</h3>
<ul>
  <li ng-repeat="item in items"></li>
</ul>
<!-- app/views/state2.html -->
<h1>State 2</h1>
<hr/>
<a ui-sref="state2.list">Show List</a>
<div ui-view></div>
<!-- app/views/state2.list.html -->
<h3>List of State 2 Things</h3>
<ul>
  <li ng-repeat="thing in things"></li>
</ul>

由上面的规则可以同理推出,当url为/state1/list时,会渲染主模板state1.html,并且会将子模板state1.list.html嵌入state1.htmlui-view中一起渲染出来。cool,路由渲染的灵活性非常高!

另外还有一个常用指令ui-sref-active="classname",它通常和ui-sref一起使用,含义是当前状态被激活则会在所属html标签上class属性添加classname,如果不再是激活状态则去除classname。

另外,ui-router还支持多模板:

app/views/index.html
1
2
3
4
5
6
7
<body>
    <div ui-view="viewA"></div>
    <div ui-view="viewB"></div>
    <!-- Also a way to navigate -->
    <a ui-sref="route1">Route 1</a>
    <a ui-sref="route2">Route 2</a>
</body>
app/scripts/app.coffee
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
myApp.config ($stateProvider) ->
  $stateProvider.state("index",
    url: ""
    views:
      viewA:
        template: "index.viewA"
      viewB:
        template: "index.viewB"
  ).state("route1",
    url: "/route1"
    views:
      viewA:
        template: "route1.viewA"
      viewB:
        template: "route1.viewB"
  ).state "route2",
    url: "/route2"
    views:
      viewA:
        template: "route2.viewA"
      viewB:
        template: "route2.viewB"

Comments