Post

angular之第二章_路由嵌套

目录:

第二章【路由嵌套】

路由嵌套(视图和状态嵌套)

状态是可以相互嵌套的,通常使用以下几种方式:

  • 在状态名称中使用点标记来嵌套,如:.state('contacts.list',{}). 这种方法是最常用的,ui-router会自动根据你使用的点标记语法来推断出状态的层级关系。如:
    1
    2
    3
    4
    
        //这里的 contacts 状态下面有一个子状态
        $stateProvider
        .state('contacts', {})
        .state('contacts.list', {});
    
  • 在状态的parent属性中指定父级状态,parent属性的值可以为string或者object,如:
    1
    2
    3
    4
    5
    6
    7
    
        //parent使用string时,应该指定为状态名称
        //这里 list 状态中并没有使用点标记来标明层级关系,不过在 list 状态中有一个parent属性指定了其父级状态的名称
        $stateProvider
        .state('contacts', {})
        .state('list', {
          parent: 'contacts'
        });
    

    ```javascript //parent使用object时,应该指定为父级状态的对象 //这里 list 状态中也没有使用点标记来标明层级关系,不过在 list 状态中有一个parent属性指定了其父级状态对象

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    
    //这里定义一个状态名为contacts
    var contacts = {
          name: 'contacts',
          templateUrl: 'contacts.html'
    }
    //这里定义一个状态名为contactsList
    var contactsList = {
        name: 'list',
        parent: contacts,  //指定它的父状态为contacts
        templateUrl: 'contacts.list.html'
    }
    
    $stateProvider
      .state(contacts)
      .state(contactsList)
    ``` ##### 状态定义顺序 通常来说,定义的顺序对代码没什么影响,你可以任意的定义。比如:父状态定义在子状态之后,跨模块定义等。因为最终他们终将会排成队列初始化的,像树一样,从上倒下初始化。 不过如果你定义了一个子状态,那么它们的父状态却是一定要定义的。不然代码会报错^_^。还有,在同一个父状态定义多个子状态时,名称应该是唯一的。
    
状态嵌套与视图

一个子状态激活后,与之相关的父状态也会隐式激活。每个状态的视图将会嵌入到父状态模版的ui-view标签中。 子状态将会继承到父状态定义的解决器(resolve)和自定义数据(data)

抽象状态

抽象状态是不能激活的(但它可以有子状态)也就是说它只能激活下面的子状态。一般在下面情况可能会用到:

  • 为子状态分类添加url
  • 多个ui-view下子状态插入节点控制
  • 为子状态提供解决器和自定义数据
  • 为子状态提供统一的onEnteronExit事件 请记住抽象状态至少需要一个ui-view提供给子状态使用,所以抽象状态都应该有template属性。

一些例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$stateProvider
	//这里为子状态提供统一的层级URL
    .state('contacts', {
        abstract: true,
        url: '/contacts',

        // 这里至少应该为子状态的模版提供一个填充位
        template: '<ui-view/>'
    })
    .state('contacts.list', {
        // url 应是 '/contacts/list'
        url: '/list'
        //...more
    })
    .state('contacts.detail', {
        // url 应是 '/contacts/detail'
        url: '/detail',
        //...more
    })

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$stateProvider
	//组织多个子视图
    .state('contacts', {
        abstract: true,
        templateUrl: 'contacts.html'
    })
    .state('contacts.list', {
        //填充到父模版中
        templateUrl: 'contacts.list.html'
    })
    .state('contacts.detail', {
        //填充到父模版中
        templateUrl: 'contacts.detail.html'
    })

当然你也可以组合起来使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$stateProvider
	//提供统一的URL/父级controller/和自定义数据contacts
	$stateProvider
    .state('contacts', {
        abstract: true,
        url: '/contacts',
        templateUrl: 'contacts.html',
        controller: function($scope){
            $scope.contacts = [{ id:0, name: "Alice" }, { id:1, name: "Bob" }];
        }
    })
    .state('contacts.list', {
        url: '/list',
        templateUrl: 'contacts.list.html'
    })
    .state('contacts.detail', {
        url: '/:id',
        templateUrl: 'contacts.detail.html',
        controller: function($scope, $stateParams){
          $scope.person = $scope.contacts[$stateParams.id];
        }
    })

第三章【多视图的使用】

到现在你至少应该明白ui-view标签就是为子状态预留模版插入位置的标记,那么在多视图或复杂的视图中,多个ui-view和视图又如何对应呢,主要有以下几种访视。 实现如下图的视图: 示例图片 这里可以将页面分为三部分:过滤条件/列表/图表。

这样我们就可以拆成三个模版,分别定义如下: 第一种,这里使用的是名称匹配,也是最简单的一种

1
2
3
4
5
6
<!-- index.html -->
<body>
  <div ui-view="filters"></div>
  <div ui-view="tabledata"></div>
  <div ui-view="graph"></div>
</body>
1
2
3
4
5
6
7
8
9
$stateProvider
  .state('report', {
  //这里的三个属性分别与上面ui-view对应,请注意:当设置views后,状态的templateUrl/template/templateProvider属性将会被忽略
    views: {
      'filters': { ... templates and/or controllers ... },
      'tabledata': {},
      'graph': {},
    }
  })

第二种,视图的相对名称和绝对名称 视图和状态后台进行绑定的模型是viewname@statename而statename就是定义状态时的名称,所以它可以不写。这样上面的例子也可以这样写:

1
2
3
4
5
6
7
 .state('report',{
    views: {
      'filters@': { },
      'tabledata@': { },
      'graph@': { }
    }
  })

相对名称

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
33
34
35
36
37
38
39
40
41
42
43
44
45
 $stateProvider
  .state('contacts', {
    //它的父级应该是index.html
    templateUrl: 'contacts.html'
  })
  .state('contacts.detail', {
    views: {
        ////////////////////////////////////
        // 相对名称                         //
        // 相对于改状态的父状态 ui-view's     //
        ////////////////////////////////////

        //将模版嵌入到'contacts'状态中的ui-view='detail'标签中
        // <div ui-view='detail'/> within contacts.html
        "detail" : { },

        //将模版嵌入到contacts状态中的无名的ui-view标签中
        // <div ui-view/> within contacts.html
        "" : { },

        /////////////////////////////////////////////////////////////////
        // 绝对名称请使用 '@'符号                                         //
        // 这里可以使用任意的视图和状态名称,请记住viewname@statename这个模型 //
        ///////////////////////////////////////////////////////////////

        //将模版嵌入contacts.detail状态里的ui-view='info'标签的位置
        // <div ui-view='info'/> within contacts.detail.html
        "info@contacts.detail" : { }

        //将模版嵌入到contacts状态里的ui-view='detail'标签的位置
        // <div ui-view='detail'/> within contacts.html
        "detail@contacts" : { }

        //省略了视图名称,也就是将模版嵌入到ui-view=''的标签位置
        // <div ui-view/> within contacts.html
        "@contacts" : { }

        //省略了状态,那么就应该是嵌入到index.html里的ui-view='status'标签位置
        // <div ui-view='status'/> within index.html
        "status@" : { }

        //省略了视图名和状态名。应该嵌入到index.html里的ui-view=''标签位置
        // <div ui-view/> within index.html
        "@" : { }
  });

第四章【URL路由】

通过URL可以访问到你定义的多个状态,比如你这样设置路由:

1
2
3
4
5
6
7
$stateProvider
  $stateProvider
    .state('contacts', {
        url: "/contacts",
        templateUrl: 'contacts.html'
    })
  })

这样你就可以index.html/contacts来激活contacts状态,contacts.html将会替换到父级的ui-view标签中而且相应的地址也会变为index.html/contacts

URL传参

通常你可以在激活一个状态时给其传递参数,比如:

1
2
3
4
5
6
7
8
9
$stateProvider
    .state('contacts.detail', {
        url: "/contacts/:contactId",
        templateUrl: 'contacts.detail.html',
        controller: function ($stateParams) {
            // If we got here from a url of /contacts/42
            expect($stateParams).toBe({contactId: "42"});
        }
    })

这样我们定义了一个可以传参的状态(请注意url属性的配置),其中上面的url属性也可以这样写:

1
url: "/contacts/{contactId}"

这里的意思是,在URL路径中可以在contacts后面跟一个任意的字符串,我们把这个字符串变量名命名为contactId.其中还有更多用法:

  • /hello/ -这个只是匹配/hello/这种url路径
  • /hello/:id -这个可以在hello后面跟任意字符串做参数
  • /hello/{id} -这只是上面的另一种写法
  • `/hello/{id:int} -这个说明了变量id的数据类型为int
  • /hello/{id:[0-9]{3}} -这个使用了一个正则表达式来确定参数id的可传入类型 注意这里的变量名和编程中的变量名取名规则一致,也就是说只能使用数字、字母、下划线,且第一个不能为数字

很好,现在我们已经定义了一个可以传入参数的状态。那么我们应该怎么传入参数呢?

  • 在URL路径中直接携带参数,如/hello/123这里的id的值就是123了
  • 在链接中使用
    1
    
     	<a ui-sref="contacts.detail({contactId: id})">View Contact</a>
    

前面的参数传递类似于http的get请求,传递参数时会在url中显示。对于这种情况ui-router还提供了另一种方式可以隐藏传参: 定义方式如下:

1
2
3
4
5
6
7
 	.state('contacts', {
        url: "/contacts",
        params: {
            param1: null
        },
        templateUrl: 'contacts.html'
    })

那么它又是怎么传入:

  • 通过链接传入
    1
    
     	<a ui-sref="contacts({param1: value1})">View Contacts</a>
    
  • 通过函数传入
    1
    
     	$state.go('contacts', {param1: value1})
    

传参和参数定义已经介绍完了,那么我们怎么在我们的controller中接收这些参数呢,这就要使用到$stateParams服务了。其实上面的参数都会在stateparams服务中将变量名注册为key并注入相应的值,其用法如下:

1
2
3
4
5
6
7
8
9
10
11
12
 	$stateProvider.state('contacts.detail', {
       url: '/contacts/:contactId',
       controller: function($stateParams){
          $stateParams.contactId  //*** 可以访问未定义的属性,不过值为undefined ***//
       }
    }).state('contacts.detail.subitem', {
       url: '/item/:itemId',
       controller: function($stateParams){//注入服务
          $stateParams.contactId //*** 这里可以直接使用 ***//
          $stateParams.itemId //*** Exists! ***//
       }
    })
路由事件处理($urlRouterProvider)

$urlRouterProvider这个服务可以为URL跳转做一些事件操作,它的基本原型如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 	app.config(function($urlRouterProvider){
    //跳往空链接则跳往主页
    $urlRouterProvider.when('', '/index');

    //跳往/aspx/i时转向主页
    $urlRouterProvider.when('/aspx/i', '/index');

    //如果你把参数作为函数的话,angular会为这个函数启用注入器的
    $urlRouterProvider.when('/aspx/i', function($match, $stateParams) {

    });

    //如果访问URL的没有定义,则跳往主页
    $urlRouterProvider.otherwise('/index');
})

组件介绍

  • $state/$stateProvider: 状态定义管理器
  • ui-sref 指令: 功效和HTML中的A标签差不多
  • ui-view 指令: 标记子状态模版替换位置
  • $urlRouter/$urlRouterProvider: 管理url和状态之间的变化,它的等级最低
  • $urlMatcherFactory: 编译URL路径
  • $templateFactory: http加载状态的视图模版
This post is licensed under CC BY 4.0 by the author.