首先给大家介绍angular-ui-router的基本用法。
如何引用依赖angular-ui-router
1
2
3
4
|
angular.module( 'app' ,[ "ui.router" ]) .config( function ($stateProvider){ $stateProvider.state(stateName, stateCofig); }) |
$stateProvider.state(stateName, stateConfig)
stateName是string类型
stateConfig是object类型
//statConfig可以为空对象
$stateProvider.state("home",{});
//state可以有子父级
$stateProvider.state("home",{});
$stateProvider.state("home.child",{})
//state可以是链式的
$stateProvider.state("home",{}).state("about",{}).state("photos",{});
stateConfig包含的字段:template, templateUrl, templateProvider, controller, controllerProvider, resolve, url, params, views, abstract, onEnter, onExit, reloadOnSearch, data
$urlRouteProvider
$urlRouteProvider.when(whenPath, toPath)
$urlRouterProvider.otherwise(path)
$urlRouteProvider.rule(handler)
$state.go
$state.go(to, [,toParams],[,options])
形参to是string类型,必须,使用"^"或"."表示相对路径;
形参toParams可空,类型是对象;
形参options可空,类型是对象,字段包括:location为bool类型默认true,inherit为bool类型默认true,
relative为对象默认$state.$current,notify为bool类型默认为true, reload为bool类型默认为false
$state.go('photos.detail')
$state.go('^')到上一级,比如从photo.detail到photo
$state.go('^.list')到相邻state,比如从photo.detail到photo.list
$state.go('^.detail.comment')到孙子级state,比如从photo.detail到photo.detial.comment
ui-sref
ui-sref='stateName'
ui-sref='stateName({param:value, param:value})'
ui-view
==没有名称的ui-view
1
2
3
4
|
<div ui-view></div> $stateProvider.state( "home" ,{ template: "<h1>hi</h1>" }) |
或者这样配置:
1
2
3
4
5
6
7
|
$stateProvider.state( "home" { views: { "" : { template: "<h1>hi</h1>" } } }) |
==有名称的ui-view
1
2
3
4
5
6
7
8
|
<div ui-view= "main" ></div> $stateProvider.state( "home" ,{ views: { "main" : { template: "<h1>hi</h1>" } } }) |
==多个ui-view
1
2
3
4
5
6
7
8
|
<div ui-view></div> <div ui-view= "data" ></div> $stateProvider.state( "home" ,{ views: { "" :{template: "<h1>hi</h1>" }, "data" : {template: "<div>data</div>" } } }) |
项目文件结构
node_modules/
partials/
.....about.html
.....home.html
.....photos.html
app.js
index.html
创建state和view
app.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
var photoGallery = angular.module( 'photoGallery' ,[ "ui.router" ]); photoGallery.config( function ($stateProvider, $urlRouterProvider){ $urlRouterProvider.otherwise( '/home' ); $stateProvider .state( 'home' ,{ url: '/home' , templateUrl: 'partials/home.html' }) .state( 'photos' ,{ url: '/photos' , templateUrl: 'partials/photos.html' }) .state( 'about' ,{ url: '/about' , templateUrl: 'partials/about.html' }) }) |
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
<!DOCTYPE html> <html lang= "en" ng-app= "photoGallery" > <head> <meta charset= "UTF-8" > <title></title> <link rel= "stylesheet" href= "node_modules/bootstrap/dist/css/bootstrap.css" /> </head> <body> <h1>Welcome</h1> <div ui-view></div> <script src= "node_modules/jquery/dist/jquery.js" ></script> <script src= "node_modules/angular/angular.js" ></script> <script src= "node_modules/angular-ui-router/release/angular-ui-router.js" ></script> <script src= "node_modules/angular-animate/angular-animate.js" ></script> <script src= "node_modules/bootstrap/dist/js/bootstrap.js" ></script> <script src= "node_modules/angular-bootstrap/ui-bootstrap-tpls.js" ></script> <script src= "app.js" ></script> </body> </html> |
state之间的跳转
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
<nav class= "navbar navbar-inverse" > <div class= "container-fluid" > <div class= "navbar-header" > <button class= "navbar-toggle collapsed" type= "button" data-toggle= "collapse" data-target= "#bs-example-navbar-collapse-1" > <span class= "icon-bar" ></span> <span class= "icon-bar" ></span> <span class= "icon-bar" ></span> </button> <a ui-sref= "home" class= "navbar-brand" >Home</a> </div> <div class= "collapse navbar-collapse" id= "bs-example-navbar-collapse-1" > <ul class= "nav navbar-nav" > <li> <a ui-sref= "photos" >Photos</a> </li> <li> <a ui-sref= "about" >About</a> </li> </ul> </div> </div> </nav> <div ui-view></div> |
以上通过ui-sref属性完成state之间的跳转。
多个view以及state嵌套
有时候,一个页面上可能有多个ui-view,比如:
1
2
|
<div ui-view= "header" ></div> <div ui-view= "body" ></div> |
假设,以上页面属于一个名称为parent的state中。
我们知道在ui-router中,一个state大致是这样设置的:
1
2
|
<div ui-view= "header" ></div> <div ui-view= "body" ></div> |
所有state下views下的所有键值对(类似 "body@content":{templateUrl: 'partials/photos.html'})都被放到一个键值集合中。而ui-view的工作原理就是根据自己的属性值,到这个键值集合中去找匹配 的键,找到就把对应的页面显示出来。
点击header对应的页面链接,可能会跳转到另外的子页面出现在<div ui-view="body"></div>这个位置。这时候页面出现了子父关系,而每个页面都属于某个state,这样state间 就出现了子父关系。这些跳转的子页面,在路由设置中,可能被称为parent.son1, parent.son2...这就是state的嵌套。
在现有的文件结构上增加content.html, header.html,文件结构变为:
node_modules/
partials/
.....about.html
.....home.html
.....photos.html
.....content.html
.....header.html
app.js
index.html
content.html 包含了多各ui-view, 一个ui-view和页头相关,保持不变;令一个ui-view和会根据页头上的点击呈现不同的内容
1
2
|
<div ui-view= "header" ></div> <div ui-view= "body" ></div> |
header.html 把原先indext.html中nav部分放到这里来
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
<nav class= "navbar navbar-inverse" > <div class= "container-fluid" > <div class= "navbar-header" > <button class= "navbar-toggle collapsed" type= "button" data-toggle= "collapse" data-target= "#bs-example-navbar-collapse-1" > <span class= "icon-bar" ></span> <span class= "icon-bar" ></span> <span class= "icon-bar" ></span> </button> <a ui-sref= "content.home" class= "navbar-brand" >Home</a> </div> <div class= "collapse navbar-collapse" id= "bs-example-navbar-collapse-1" > <ul class= "nav navbar-nav" > <li> <a ui-sref= "content.photos" >Photos</a> </li> <li> <a ui-sref= "content.about" >About</a> </li> </ul> </div> </div> </nav> |
index.html 这时变成了这样
<div ui-view></div>
app.js 路由现在这样设置
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
|
var photoGallery = angular.module( 'photoGallery' ,[ "ui.router" ]); photoGallery.config( function ($stateProvider, $urlRouterProvider){ $urlRouterProvider.otherwise( 'home' ); $stateProvider .state( 'content' ,{ url: '/' , views:{ "" :{templateUrl: 'partials/content.html' }, "header@content" :{templateUrl: 'partials/header.html' }, } }) .state( 'content.home' ,{ url: 'home' , views:{ "body@content" :{templateUrl: 'partials/home.html' } } }) .state( 'content.photos' ,{ url: 'photos' , views:{ "body@content" :{templateUrl: 'partials/photos.html' } } }) .state( 'content.about' ,{ url: 'about' , views:{ "body@content" :{templateUrl: 'partials/about.html' } } }) }) |
这时候,页面是这样呈现出来的:
→ 来到home这个路由
1
2
3
4
5
6
|
.state( 'content.home' ,{ url: 'home' , views:{ "body@content" :{templateUrl: 'partials/home.html' } } }) |
以上,告诉我们partials/home.html将会被加载到与"body@content"匹配的ui-view中。暂时对应的ui-view还没有出现,于是等待。
→ 路由看到index.html上的<div ui-view></div>
1
2
3
4
5
6
7
|
.state( 'content' ,{ url: '/' , views:{ "" :{templateUrl: 'partials/content.html' }, "header@content" :{templateUrl: 'partials/header.html' }, } }) |
于是,就找到了content这个state下views下的 "":{templateUrl: 'partials/content.html'}这个键值对,把partials/content.html显示出来。
→ 分别加载partials/content.html页面上的各个部分
看到<div ui-view="header"></div>,就加载如下:
"header@content":{templateUrl: 'partials/header.html'},
看到<div ui-view="body"></div>,先加载 "body@content":{templateUrl: 'partials/home.html'}
→ 点击header上的链接
点击<a ui-sref="content.photos">Photos</a>,来到:
1
2
3
4
5
6
|
.state( 'content.photos' ,{ url: 'photos' , views:{ "body@content" :{templateUrl: 'partials/photos.html' } } }) |
把partials/photos.html显示到<div ui-view="body"></div>中去。
点击<div ui-view="body"></div>,来到:
1
2
3
4
5
6
|
.state( 'content.about' ,{ url: 'about' , views:{ "body@content" :{templateUrl: 'partials/about.html' } } }) |
把partials/about.html显示到<div ui-view="body"></div>中去。
state多级嵌套
以上,在路由设置中,state名称有content, content.photos有了这样的一层嵌套。接下来,要实现state的多级嵌套。
在photos.html页面准备加载一个子页面,叫做photos-list.html;
与photo-list.html页面相邻的还有一个页面,叫做photo-detail.html;
在photo-detail.html页面上加载一个子页面,叫做photos-detail-comment.html;
这样,页面有了嵌套关系,state也相应的会有嵌套关系。
现在,文件结构变成:
node_modules/
partials/
.....about.html
.....home.html
.....photos.html
.....content.html
.....header.html
.....photos-list.html
.....photo-detail.html
.....photos-detail-comment.html
app.js
index.html
photos.html 加一个容纳子页面的ui-view
photos
<div ui-view></div>
如何到达这个子页面呢?修改header中的相关部分如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
<nav class= "navbar navbar-inverse" > <div class= "container-fluid" > <div class= "navbar-header" > <button class= "navbar-toggle collapsed" type= "button" data-toggle= "collapse" data-target= "#bs-example-navbar-collapse-1" > <span class= "icon-bar" ></span> <span class= "icon-bar" ></span> <span class= "icon-bar" ></span> </button> <a ui-sref= "content.home" class= "navbar-brand" >Home</a> </div> <div class= "collapse navbar-collapse" id= "bs-example-navbar-collapse-1" > <ul class= "nav navbar-nav" > <li> <a ui-sref= "content.photos.list" >Photos</a> </li> <li> <a ui-sref= "content.about" >About</a> </li> </ul> </div> </div> |
以上,通过<a ui-sref="content.photos.list">Photos</a>来到photos.html的子页面photos-list.html.
photos-list.html 通过2种途径到相邻页photo-detail.html
1
2
3
4
5
|
<h1>photos-list</h1> <ul> <li><a ui-sref= "^.detail" >我通过相对路径到相邻的state</a></li> <li><a ui-sref= "content.photos.detail" >我通过绝对路径到相邻的state</a></li> </ul> |
photo-detail.html 又提供了来到其子页面photos-detail-comment.html的ui-view
1
2
3
|
<h1>photo-details</h1> <a class= "btn btn-default" ui-sref= ".comment" >通过相对路径去子state</a> <div ui-view></div> |
photos-detail-comment.html 则很简单:
<h1>photos-detail-comment</h1>
app.js state多级嵌套的设置为
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
|
var photoGallery = angular.module( 'photoGallery' ,[ "ui.router" ]); photoGallery.config( function ($stateProvider, $urlRouterProvider){ $urlRouterProvider.otherwise( 'home' ); $stateProvider .state( 'content' ,{ url: '/' , views:{ "" :{templateUrl: 'partials/content.html' }, "header@content" :{templateUrl: 'partials/header.html' }, } }) .state( 'content.home' ,{ url: 'home' , views:{ "body@content" :{templateUrl: 'partials/home.html' } } }) .state( 'content.photos' ,{ url: 'photos' , views:{ "body@content" :{templateUrl: 'partials/photos.html' } } }) .state( 'content.photos.list' ,{ url: '/list' , templateUrl: 'partials/photos-list.html' }) .state( 'content.photos.detail' ,{ url: '/detail' , templateUrl: 'partials/photos-detail.html' }) .state( 'content.photos.detail.comment' ,{ url: '/comment' , templateUrl: 'partials/photos-detail-comment.html' }) .state( 'content.about' ,{ url: 'about' , views:{ "body@content" :{templateUrl: 'partials/about.html' } } }) }) |
抽象state
如果一个state,没有通过链接找到它,那就可以把这个state设置为abstract:true,我们把以上的content和content.photos这2个state设置为抽象。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
.state( 'content' ,{ url: '/' , abstract: true , views:{ "" :{templateUrl: 'partials/content.html' }, "header@content" :{templateUrl: 'partials/header.html' }, } }) ... .state( 'content.photos' ,{ url: 'photos' , abstract: true , views:{ "body@content" :{templateUrl: 'partials/photos.html' } } }) |
那么,当一个state设置为抽象,如果通过ui-sref或路由导航到该state会出现什么结果呢?
--会导航到默认路由上
$urlRouterProvider.otherwise('home');
即
1
2
3
4
5
6
|
.state( 'content.home' ,{ url: 'home' , views:{ "body@content" :{templateUrl: 'partials/home.html' } } }) |
最终把partials/home.html显示出来。
使用控制器
在实际项目中,数据大多从controller中来。
首先在路由中设置state所用到的控制器以及控制器别名。
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
46
47
48
49
50
51
52
53
54
55
56
|
var photoGallery = angular.module( 'photoGallery' ,[ "ui.router" ]); photoGallery.config( function ($stateProvider, $urlRouterProvider){ $urlRouterProvider.otherwise( 'home' ); $stateProvider .state( 'content' ,{ url: '/' , abstract: true , views:{ "" :{templateUrl: 'partials/content.html' }, "header@content" :{templateUrl: 'partials/header.html' }, } }) .state( 'content.home' ,{ url: 'home' , views:{ "body@content" :{ templateUrl: 'partials/home.html' , controller: 'HomeController' , controllerAs: 'ctrHome' } } }) .state( 'content.photos' ,{ url: 'photos' , abstract: true , views:{ "body@content" :{ templateUrl: 'partials/photos.html' , controller: 'PhotoController' , controllerAs: 'ctrPhoto' } } }) .state( 'content.photos.list' ,{ url: '/list' , templateUrl: 'partials/photos-list.html' , controller: "PhotoListController" , controllerAs: 'ctrPhotoList' }) .state( 'content.photos.detail' ,{ url: '/detail' , templateUrl: 'partials/photos-detail.html' , controller: 'PhotoDetailController' , controllerAs: 'ctrPhotoDetail' }) .state( 'content.photos.detail.comment' ,{ url: '/comment' , templateUrl: 'partials/photos-detail-comment.html' }) .state( 'content.about' ,{ url: 'about' , views:{ "body@content" :{templateUrl: 'partials/about.html' } } }) }) |
添加controller.js,该文件用来定义所用到的controller.现在的文件结构为:
asserts/
.....css/
.....images/
..........image1.jpg
..........image2.jpg
..........image3.jpg
..........image4.jpg
node_modules/
partials/
.....about.html
.....home.html
.....photos.html
.....content.html
.....header.html
.....photos-list.html
.....photo-detail.html
.....photos-detail-comment.html
app.js
index.html
controllers.js
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
46
47
48
49
50
|
photoGallery.controller( 'HomeController' ,[ '$scope' , '$state' , function ($scope, $state){ this .message = 'Welcome to the Photo Gallery' ; }]); //别名:ctrPhoto photoGallery.controller( 'PhotoController' ,[ '$scope' , '$state' , function ($scope, $state){ this .photos = [ { id: 0, title: 'Photo 1' , description: 'description for photo 1' , imageName: 'image1.jpg' , comments:[ {name: 'user1' , comment: 'Nice' }, { name: 'User2' , comment: 'Very good' } ]}, { id: 1, title: 'Photo 2' , description: 'description for photo 2' , imageName: 'image2.jpg' , comments:[ { name: 'user2' , comment: 'Nice' }, { name: 'User1' , comment: 'Very good' } ]}, { id: 2, title: 'Photo 3' , description: 'description for photo 3' , imageName: 'image3.jpg' , comments:[ {name: 'user1' , comment: 'Nice' } ]}, { id: 3, title: 'Photo 4' , description: 'description for photo 4' , imageName: 'image4.jpg' , comments:[ {name: 'user1' , comment: 'Nice' }, { name: 'User2' , comment: 'Very good' }, { name: 'User3' , comment: 'So so' } ]} ]; //给子state下controller中的photos赋值 this .pullData = function (){ $scope.$$childTail.ctrPhotoList.photos = this .photos; } }]); //别名:ctrPhotoList photoGallery.controller( 'PhotoListController' ,[ '$scope' , '$state' , function ($scope, $state){ this .reading = false ; this .photos = new Array(); this .init = function (){ this .reading = true ; setTimeout( function (){ $scope.$apply( function (){ $scope.ctrPhotoList.getData(); }); }, 1500); } this .getData = function (){ //调用父state中controller中的方法 $scope.$parent.ctrPhoto.pullData(); /*this.photos = $scope.$parent.ctrPhoto.photos;*/ this .reading = false ; } }]); //别名:ctrPhotoDetail photoGallery.controller( 'PhotoDetailController' ,[ '$scope' , '$state' , function ($scope,$state){ }]); |
以上,通过$scope.$$childTail.ctrPhotoList在父state中的controller中拿到子state中的
controller;通过$scope.$parent.ctrPhoto在子state中的controller中拿到父state中的
controller。
photos-list.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
<h1>photos-list</h1> <div ng-init= "ctrPhotoList.init()" > <div style= "margin:auto; 40px;" ng- if = "ctrPhotoList.reading" > <i class= "fa fa-spinner fa-5x fa-pulse" ></i> </div> <div class= "well well-sm" ng-repeat= "photo in ctrPhotoList.photos" > <div class= "media" > <div class= "media-left" style= "15%;" > <a ui-sref= "content.photos.detail" > <img class= "img-responsive img-rounded" src= "../asserts/images/{{photo.imageName}}" alt= "" > </a> </div> <div class= "media-body" > <h4 class= "media-heading" >{{photo.title}}</h4> {{photo.description}} </div> </div> </div> </div> |
state间如何传路由参数
在content.photos.detail这个state设置接收一个路由参数。
1
2
3
4
5
6
|
.state( 'content.photos.detail' ,{ url: '/detail/:id' , templateUrl: 'partials/photos-detail.html' , controller: 'PhotoDetailController' , controllerAs: 'ctrPhotoDetail' }) |
photos-list.html 送出一个路由参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
<h1>photos-list</h1> <div ng-init= "ctrPhotoList.init()" > <div style= "margin:auto; 40px;" ng- if = "ctrPhotoList.reading" > <i class= "fa fa-spinner fa-5x fa-pulse" ></i> </div> <div class= "well well-sm" ng-repeat= "photo in ctrPhotoList.photos" > <div class= "media" > <div class= "media-left" style= "15%;" > <a ui-sref= "content.photos.detail({id:photo.id})" > <img class= "img-responsive img-rounded" src= "../asserts/images/{{photo.imageName}}" alt= "" > </a> </div> <div class= "media-body" > <h4 class= "media-heading" >{{photo.title}}</h4> {{photo.description}} </div> </div> </div> </div> |
以上,通过<a ui-sref="content.photos.detail({id:photo.id})">把路由参数送出。
controller.js PhotoDetailController控制器通过$stateParams获取路由参数
1
2
3
4
5
6
7
8
9
10
11
12
|
... //别名:ctrPhotoDetail photosGallery.controller( 'PhotoDetailController' , [ '$scope' , '$state' , '$stateParams' , function ($scope, $state, $stateParams){ var id = null ; this .photo = null ; this .init = function (){ id = parseInt($stateParams.id); this .photo = $scope.ctrPhoto.photos[id]; } } ]); |
photos-detail.html 从以上的PhotoDetailController中获取数据。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
<h1>photo-details</h1> <a class= "btn btn-default" ui-sref= ".comment" >通过相对路径去子state</a> <a ui-sref= "content.photos.list" style= "margin-left: 15px;" > <i class= "fa fa-arrow-circle-left fa-2x" ></i> </a> <div ng-init= "ctrPhotoDetail.init()" > <img class= "img-responsive img-rounded" ng-src= "../assets/images/{{ctrPhotoDetail.photo.imageName}}" style= "margin:auto; 60%;" > <div class= "well well-sm" style= "margin:auto; 60%; margin-top: 15px;" > <h4>{{ctrPhotoDetail.photo.title}}</h4> <p>{{ctrPhotoDetail.photo.description}}</p> </div> <div style= "margin:auto; 80%; margin-bottom: 15px;" > <button style= "margin-top: 10px; 100%;" class= "btn btn-default" ui-sref= ".comment" >Comments</button> </div> </div> <div ui-view></div> |
state间如何传字符串参数
在路由中这样设置:
1
2
3
4
5
6
|
.state( 'content.photos.detail.comment' ,{ url: '/comment?skip&limit' , templateUrl: 'partials/photos-detail-comment.html' , controller: 'PhotoCommentController' , controllerAs: 'ctrPhotoComment' }) |
controllers.js 中修改如下
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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
|
photoGallery.controller( 'HomeController' ,[ '$scope' , '$state' , function ($scope, $state){ this .message = 'Welcome to the Photo Gallery' ; }]); //别名:ctrPhoto photoGallery.controller( 'PhotoController' ,[ '$scope' , '$state' , function ($scope, $state){ this .photos = [ { id: 0, title: 'Photo 1' , description: 'description for photo 1' , imageName: 'image1.JPG' , comments:[ { name: 'User1' , comment: 'Nice' , imageName: 'man.png' }, { name: 'User2' , comment: 'Very good' , imageName: 'man.png' }, { name: 'User3' , comment: 'Nice' , imageName: 'woman.png' }, { name: 'User4' , comment: 'Very good' , imageName: 'woman.png' }, { name: 'User5' , comment: 'Very good' , imageName: 'man.png' }, { name: 'User6' , comment: 'Nice' , imageName: 'woman.png' }, { name: 'User7' , comment: 'So so' , imageName: 'man.png' } ]}, { id: 1, title: 'Photo 2' , description: 'description for photo 2' , imageName: 'image2.JPG' , comments:[ { name: 'User1' , comment: 'Nice' , imageName: 'man.png' }, { name: 'User2' , comment: 'Very good' , imageName: 'man.png' }, { name: 'User3' , comment: 'Nice' , imageName: 'woman.png' }, { name: 'User4' , comment: 'Very good' , imageName: 'woman.png' } ]}, { id: 2, title: 'Photo 3' , description: 'description for photo 3' , imageName: 'image3.JPG' , comments:[ { name: 'User1' , comment: 'Nice' , imageName: 'man.png' }, { name: 'User2' , comment: 'Very good' , imageName: 'man.png' }, { name: 'User3' , comment: 'Nice' , imageName: 'woman.png' }, { name: 'User4' , comment: 'Very good' , imageName: 'woman.png' }, { name: 'User5' , comment: 'Very good' , imageName: 'man.png' }, { name: 'User6' , comment: 'Nice' , imageName: 'woman.png' }, { name: 'User7' , comment: 'So so' , imageName: 'man.png' } ]}, { id: 3, title: 'Photo 4' , description: 'description for photo 4' , imageName: 'image4.JPG' , comments:[ { name: 'User6' , comment: 'Nice' , imageName: 'woman.png' }, { name: 'User7' , comment: 'So so' , imageName: 'man.png' } ]} ]; //给子state下controller中的photos赋值 this .pullData = function (){ $scope.$$childTail.ctrPhotoList.photos = this .photos; } }]); //别名:ctrPhotoList photoGallery.controller( 'PhotoListController' ,[ '$scope' , '$state' , function ($scope, $state){ this .reading = false ; this .photos = new Array(); this .init = function (){ this .reading = true ; setTimeout( function (){ $scope.$apply( function (){ $scope.ctrPhotoList.getData(); }); }, 1500); } this .getData = function (){ //调用父state中controller中的方法 $scope.$parent.ctrPhoto.pullData(); /*this.photos = $scope.$parent.ctrPhoto.photos;*/ this .reading = false ; } }]); //别名:ctrPhotoDetail photoGallery.controller( 'PhotoDetailController' , [ '$scope' , '$state' , '$stateParams' , function ($scope, $state, $stateParams){ var id = null ; this .photo = null ; this .init = function (){ id = parseInt($stateParams.id); this .photo = $scope.ctrPhoto.photos[id]; } } ]); photoGallery.controller( 'PhotoCommentController' , [ '$scope' , '$state' , '$stateParams' , function ($scope, $state, $stateParams){ var id, skip, limit = null ; this .comments = new Array(); this .init = function (){ id = parseInt($stateParams.id); var photo = $scope.ctrPhoto.photos[id]; if ($stateParams.skip){ skip = parseInt($stateParams.skip); } else { skip = 0; } if ($stateParams.limit){ limit = parseInt($stateParams.limit); } else { limit = photo.comments.length; } this .comments = photo.comments.slice(skip, limit); } } ]); |
也就是,$stateParams不仅可以接收路由参数,还可以接收查询字符串参数。
photo-detail.html 需要把查询字符串参数传递出去
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
<h1>photo-details</h1> <a class= "btn btn-default" ui-sref= ".comment" >通过相对路径去子state</a> <a ui-sref= "content.photos.list" style= "margin-left: 15px;" > <i class= "fa fa-arrow-circle-left fa-2x" ></i> </a> <div ng-init= "ctrPhotoDetail.init()" > <img class= "img-responsive img-rounded" ng-src= "../assets/images/{{ctrPhotoDetail.photo.imageName}}" style= "margin:auto; 60%;" > <div class= "well well-sm" style= "margin:auto; 60%; margin-top: 15px;" > <h4>{{ctrPhotoDetail.photo.title}}</h4> <p>{{ctrPhotoDetail.photo.description}}</p> </div> <div style= "margin:auto; 80%; margin-bottom: 15px;" > <button style= "margin-top: 10px; 100%;" class= "btn btn-default" ui-sref= ".comment({skip:0, limit:2})" >Comments</button> </div> </div> <div ui-view></div> |
以上,通过ui-sref=".comment({skip:0, limit:2})把查询字符串传递出去。
photos-detail-comment.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
<h1>photos-detail-comment</h1> <div ng-init= "ctrPhotoComment.init()" style= "margin-top:15px;" > <div ng-repeat= "comment in ctrPhotoComment.comments" class= "well well-sm" style= "margin: auto; 60%;" > <div class= "media" > <div class= "media-left media-middle" > <a href= "" > <img class= "img-circle" style= "60px;" src= "../assets/images/{{comment.imageName}}" alt= "" > </a> </div> <div class= "media-body" > <h4 class= "media-heading" >{{comment.name}}</h4> {{comment.comment}} </div> </div> </div> </div> |
state间如何传递对象
通过data属性,把一个对象赋值给它。
1
2
3
4
5
6
7
8
9
10
11
12
|
.state( 'content' ,{ url: '/' , abstract: true , data:{ user: "user" , password: "1234" }, views:{ "" :{templateUrl: 'partials/content.html' }, "header@content" :{templateUrl: 'partials/header.html' }, } }) |
给header.html加上一个对应的控制器,并提供注销方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
$stateProvider .state( 'content' ,{ url: '/' , abstract: true , data:{ user: "user" , password: "1234" }, views:{ "" :{templateUrl: 'partials/content.html' }, "header@content" :{ templateUrl: 'partials/header.html' , controller: function ($scope, $rootScope, $state){ $scope.logoff = function (){ $rootScope.user = null ; } } } } }) |
添加一个有关登录页的state
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
|
.state( 'content.login' ,{ url: 'login' , data:{ loginError: 'User or password incorrect.' }, views:{ "body@content" :{ templateUrl: 'partials/login.html' , controller: function ($scope, $rootScope, $state){ $scope.login = function (user, password, valid){ if (!valid){ return ; } if ($state.current.data.user === user && $state.current.data.password === password){ $rootScope.user = { name: $state.current.data.user } // Or Inherited /*$rootScope.user = { name: $state.$current.parent.data.user };*/ $state.go( 'content.home' ); } else { $scope.message = $state.current.data.loginError; } } } } } }) |
添加login.html文件,现在的文件结构为:
asserts/
.....css/
.....images/
..........image1.jpg
..........image2.jpg
..........image3.jpg
..........image4.jpg
node_modules/
partials/
.....about.html
.....home.html
.....photos.html
.....content.html
.....header.html
.....photos-list.html
.....photo-detail.html
.....photos-detail-comment.html
.....login.html
app.js
index.html
login.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
<form name= "form" ng-submit= "login(user, password, form.$valid)" > <div class= "panel panel-primary" style= "360px; margin: auto;" > <div class= "panel-heading" > <h3 class= "panel-title" >Indentification</h3> </div> <div class= "panel-body" > <input name= "user" type= "text" class= "form-control" ng-model= "user" placeholder= "User ..." required> <span ng-show= "form.user.$error.required && form.user.$dirty" class= "label label-danger" >Enter the user</span> <hr> <input name= "password" type= "password" class= "form-control" ng-model= "password" placeholder= "Password ..." required> <span ng-show= "form.password.$error.required && form.password.$dirty" class= "label label-danger" >Enter the password</span> </div> <div class= "panel-footer" > <button class= "btn btn-default" type= "submit" >Login</button> <button class= "btn btn-default" type= "reset" >Reset</button> <span class= "label label-danger" >{{message}}</span> </div> </div> </form> |
header.html 修改如下
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
|
<nav class= "navbar navbar-inverse" > <div class= "container-fluid" > <div class= "navbar-header" > <button class= "navbar-toggle collapsed" type= "button" data-toggle= "collapse" data-target= "#bs-example-navbar-collapse-1" > <span class= "icon-bar" ></span> <span class= "icon-bar" ></span> <span class= "icon-bar" ></span> </button> <a class= "navbar-brand" ui-sref= "content.home" >Home</a> </div> <div class= "collapse navbar-collapse" id= "bs-example-navbar-collapse-1" > <ul class= "nav navbar-nav" > <li> <a ui-sref= "content.photos.list" >Photos</a> </li> <li> <a ui-sref= "content.about" >About</a> </li> </ul> <ul class= "nav navbar-nav navbar-right" > <li ng- if = "user.name" class= "dropdown" > <a class= "dropdown-toggle" role= "button" aria-expanded= "false" href= "#" data-toggle= "dropdown" >{{user.name}} <span class= "caret" ></span></a> <ul class= "dropdown-menu" role= "menu" > <li><a ui-sref= "content.home" ng-click= "logoff()" >Sing out</a></li> </ul> </li> <li ng- if = "!user.name" > <a ui-sref= "content.login" >Sing In</a> </li> </ul> </div> </div> </nav> |
onEnter和onExit事件
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
|
.state( 'content.photos.detail' ,{ url: '/detail/:id' , templateUrl: 'partials/photos-detail.html' , controller: 'PhotoDetailController' , controllerAs: 'ctrPhotoDetail' , resolve:{ viewing: function ($stateParams){ return { photoId: $stateParams.id } } }, onEnter: function (viewing){ var photo = JSON.parse(sessionStorage.getItem(viewing.photoId)); if (!photo){ photo = { views: 1, viewing: 1 } } else { photo.views = photo.views + 1; photo.viewing = photo.viewing + 1; } sessionStorage.setItem(viewing.photoId, JSON.stringify(photo)); }, onExit: function (viewing){ var photo = JSON.parse(sessionStorage.getItem(viewing.photoId)); photo.viewing = photo.viewing - 1; sessionStorage.setItem(viewing.photoId, JSON.stringify(photo)); } }) |
在PhotoDetailController中:
1
2
3
4
5
6
7
8
9
10
11
12
|
photoGallery.controller( 'PhotoDetailController' , [ '$scope' , '$state' , '$stateParams' , function ($scope, $state, $stateParams){ var id = null ; this .photo = null ; this .viewObj = null ; this .init = function (){ id = parseInt($stateParams.id); this .photo = $scope.ctrPhoto.photos[id]; this .viewObj = JSON.parse(sessionStorage.getItem($stateParams.id)); } } ]); |
photos-detail.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
<h1>photo-details</h1> <a class= "btn btn-default" ui-sref= ".comment" >通过相对路径去子state</a> <a ui-sref= "content.photos.list" style= "margin-left: 15px;" > <i class= "fa fa-arrow-circle-left fa-2x" ></i> </a> <div ng-init= "ctrPhotoDetail.init()" > <img class= "img-responsive img-rounded" ng-src= "../assets/images/{{ctrPhotoDetail.photo.imageName}}" style= "margin:auto; 60%;" > <div class= "well well-sm" style= "margin:auto; 60%; margin-top: 15px;" > <div class= "well well-sm pull-right" style= " 100px;" > <i>Views <span class= "badge" >{{ctrPhotoDetail.viewObj.views}}</span></i> </div> <div class= "well well-sm pull-right" style= " 110px;" > <i>Viewing <span class= "badge" >{{ctrPhotoDetail.viewObj.viewing}}</span></i> </div> <h4>{{ctrPhotoDetail.photo.title}}</h4> <p>{{ctrPhotoDetail.photo.description}}</p> </div> <div style= "margin:auto; 80%; margin-bottom: 15px;" > <button style= "margin-top: 10px; 100%;" class= "btn btn-default" ui-sref= ".comment({skip:0, limit:2})" >Comments</button> </div> </div> <div ui-view></div> |
StateChangeStart事件
controller.js 增加如下
1
2
3
4
5
6
7
8
9
10
11
|
photoGallery.controller( 'RootController' , [ '$scope' , '$state' , '$rootScope' , function ($scope, $state, $rootScope){ $rootScope.$on( '$stateChangeStart' , function (event, toState, toParams, fromState, fromParams){ if (toState.data.required && !$rootScope.user){ event.preventDefault(); $state.go( 'content.login' ); } }); } ]); |
修改content这个state:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
.state( 'content' ,{ url: '/' , abstract: true , data:{ user: "user" , password: "1234" }, views:{ "" :{ templateUrl: 'partials/content.html' , controller: 'RootController' }, "header@content" :{ templateUrl: 'partials/header.html' , controller: function ($scope, $rootScope, $state){ $scope.logoff = function (){ $rootScope.user = null ; } } } } }) |
content.photos.detail这个state
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
|
.state( 'content.photos.detail' ,{ url: '/detail/:id' , templateUrl: 'partials/photos-detail.html' , controller: 'PhotoDetailController' , controllerAs: 'ctrPhotoDetail' , data:{ required: true }, resolve:{ viewing: function ($stateParams){ return { photoId: $stateParams.id } } }, onEnter: function (viewing){ var photo = JSON.parse(sessionStorage.getItem(viewing.photoId)); if (!photo){ photo = { views: 1, viewing: 1 } } else { photo.views = photo.views + 1; photo.viewing = photo.viewing + 1; } sessionStorage.setItem(viewing.photoId, JSON.stringify(photo)); }, onExit: function (viewing){ var photo = JSON.parse(sessionStorage.getItem(viewing.photoId)); photo.viewing = photo.viewing - 1; sessionStorage.setItem(viewing.photoId, JSON.stringify(photo)); } }) |
以上,添加了
1
2
3
|
data:{ required: true } |
同理,content.photos.detail.comment这个state
1
2
3
4
5
6
7
8
9
|
.state( 'content.photos.detail.comment' ,{ url: '/comment?skip&limit' , templateUrl: 'partials/photos-detail-comment.html' , controller: 'PhotoCommentController' , controllerAs: 'ctrPhotoComment' , data:{ required: true } }) |
StateNotFound事件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
photosGallery.controller( 'RootController' , [ '$scope' , '$state' , '$rootScope' , function ($scope, $state, $rootScope){ $rootScope.$on( '$stateChangeStart' , function (event, toState, toParams, fromState, fromParams){ if (toState.data.required && !$rootScope.user){ event.preventDefault(); $state.go( 'content.login' ); return ; } }); $rootScope.$on( '$stateNotFound' , function (event, unfoundState, fromState, fromParams){ event.preventDefault(); $state.go( 'content.notfound' ); }); } ]); |
添加一个state:
1
2
3
4
5
6
|
.state( 'content.notfound' ,{ url: 'notfound' , views: { "body@content" : {templateUrl: 'partials/page-not-found.html' } } }) |
page-not-found.html
1
2
3
|
<div class= "well well-sm" style= "margin: 20px;" > <i class= "fa fa-frown-o fa-4x pull-left" ></i><h3>404 - Sorry! Not found your page.</h3> </div> |
StateChangeSuccess事件
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
|
photosGallery.controller( 'RootController' , [ '$scope' , '$state' , '$rootScope' , function ($scope, $state, $rootScope){ $rootScope.accessLog = new Array(); $rootScope.$on( '$stateChangeStart' , function (event, toState, toParams, fromState, fromParams){ if (toState.data.required && !$rootScope.user){ event.preventDefault(); $state.go( 'content.login' ); return ; } }); $rootScope.$on( '$stateNotFound' , function (event, unfoundState, fromState, fromParams){ event.preventDefault(); $state.go( 'content.notfound' ); }); $rootScope.$on( '$stateChangeSuccess' , function (event, toState, toParams, fromState, fromParams){ $rootScope.accessLog.push({ user: $rootScope.user, from: fromState.name, to: toState.name, date: new Date() }); }); } ]); |
添加一个state
1
2
3
4
5
6
7
8
9
|
.state( 'content.log' ,{ url: 'log' , data:{ required: true }, views: { "body@content" : {templateUrl: 'partials/log.html' } } }) |
log.html
1
2
3
4
5
6
7
8
|
<h1><i class= "fa fa-file-text-o" ></i> Access Log</h1> <div style= "margin:auto; 380px;" > <div class= "well well-sm" ng-repeat= "log in accessLog track by $index" > <i class= "fa fa-pencil fa-2x pull-left" ></i> {{log.user ? log.user.name: 'anonymous' }} in {{log.date | date: 'longDate' }} at {{log.date | date: 'shortTime' }} <p>From: {{log.from}} => to: {{log.to}}</p> </div> </div> |
StateChangeError事件
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
|
photosGallery.controller( 'RootController' , [ '$scope' , '$state' , '$rootScope' , function ($scope, $state, $rootScope){ $rootScope.accessLog = new Array(); $rootScope.$on( '$stateChangeStart' , function (event, toState, toParams, fromState, fromParams){ if (toState.data.required && !$rootScope.user){ event.preventDefault(); $state.go( 'content.login' ); return ; } }); $rootScope.$on( '$stateNotFound' , function (event, unfoundState, fromState, fromParams){ event.preventDefault(); $state.go( 'content.notfound' ); }); $rootScope.$on( '$stateChangeSuccess' , function (event, toState, toParams, fromState, fromParams){ $rootScope.accessLog.push({ user: $rootScope.user, from: fromState.name, to: toState.name, date: new Date() }); }); $rootScope.$on( '$stateChangeError' , function (event, toState, toParams, fromState, fromParams, error){ event.preventDefault(); $state.go( 'content.error' , {error: error}); }); } ]); |
添加2个state:
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
|
.state( 'content.profile' , { url: 'profile' , data:{ required: true }, resolve:{ showError: function (){ throw 'Error in code.' ; } }, views:{ "body@content" : {template: '<div>Error</div>' } } }) .state( 'content.error' ,{ url: 'error/:error' , views:{ "body@content" :{ templateUrl: 'partials/error.html' , controller: function ($scope, $stateParams){ $scope.error = { message: $stateParams.error } } } } }) |
error.html
1
2
3
|
<div class= "well well-sm" style= "margin: 20px;" > <i class= "fa fa-exclamation-circle fa-2x" > Sorry! But this message was displayed: {{error.message}}</i> </div> |