Tout d'abord, je vais vous présenter l'utilisation de base d'angular-ui-router.
Comment référencer la dépendance angulaire-ui-router
angular.module('app',["ui.router"]) .config(function($stateProvider){ $stateProvider.state(stateName, stateCofig); })
$stateProvider.state(stateName, stateConfig)
stateName est de type chaîne
stateConfig est un type d'objet
//statConfig peut être un objet vide
$stateProvider.state("home",{});
//l'état peut avoir des enfants et des parents
$stateProvider.state("home",{});
$stateProvider.state("home.child",{})
//l'état peut être chaîné
$stateProvider.state("home",{}).state("about",{}).state("photos",{});
StateConfig contient des champs : modèle, templateUrl, templateProvider, contrôleur, contrôleurProvider, résolution, url, params, vues, résumé, onEnter, onExit, reloadOnSearch, data
$urlRouteProvider
$urlRouteProvider.when(whenPath, toPath)
$urlRouterProvider.otherwise(chemin)
$urlRouteProvider.rule(handler)
$state.go
$state.go(to, [,toParams],[,options])
Le paramètre formel to est de type chaîne, ce qui est obligatoire. Utilisez "^" ou "."
Le paramètre formel toParams est nullable et le type est object
;
Les options du paramètre formel peuvent être nulles et le type est un objet. Les champs incluent : location est un type booléen et la valeur par défaut est true, héritage est un type booléen et la valeur par défaut est true, relatif est un objet et la valeur par défaut est $. state.$current, notify est un type bool et la valeur par défaut est true, et reload est un type bool et la valeur par défaut est false
.
$state.go('^') au niveau précédent, par exemple de photo.detail à photo
$state.go('^.list') vers l'état adjacent, par exemple de photo.detail à photo.list
$state.go('^.detail.comment') vers l'état du petit-enfant, par exemple de photo.detail à photo.detial.comment
ui-sref
ui-sref='stateName'
ui-sref='stateName({param:value, param:value})'
ui-view
== ui-view sans nom
<div ui-view></div> $stateProvider.state("home",{ template: "<h1>hi</h1>" })
$stateProvider.state("home"{ views: { "": { template: "<h1>hi</h1>" } } })
<div ui-view="main"></div> $stateProvider.state("home",{ views: { "main" : { template: "<h1>hi</h1>" } } })
<div ui-view></div> <div ui-view="data"></div> $stateProvider.state("home",{ views: { "":{template: "<h1>hi</h1>"}, "data": {template: "<div>data</div>"} } })
Structure du fichier projet
node_modules/
partiels/
.....à propos.html
.....home.html
.....photos.html
app.js
index.html
Créer un état et une vue
app.js
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
<!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>
Sauter entre les états
<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>
Vues multiples et imbrication d'état
Parfois, il peut y avoir plusieurs vues d'interface utilisateur sur une page, telles que :
<div ui-view="header"></div> <div ui-view="body"></div>
Nous savons que dans ui-router, un état est défini à peu près comme ceci :
<div ui-view="header"></div> <div ui-view="body"></div>
Cliquez sur le lien de la page correspondant à l'en-tête, qui peut accéder à une autre sous-page apparaissant à la position . À ce stade, une relation enfant-parent apparaît sur la page, et chaque page appartient à un certain état, donc une relation enfant-parent apparaît entre les états. Ces sous-pages redirigées peuvent être appelées parent.son1, parent.son2... dans les paramètres de routage. Il s'agit de l'imbrication d'état.
Ajoutez content.html et header.html à la structure de fichiers existante, et la structure de fichiers devient :
node_modules/
partiels/
.....à propos.html
.....home.html
.....photos.html
.....content.html
.....header.html
app.js
index.html
.
<div ui-view="header"></div> <div ui-view="body"></div>
<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>
Le routage app.js est désormais configuré comme ceci
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'} } }) })
→ Accédez à l'itinéraire d'origine
.state('content.home',{ url: 'home', views:{ "body@content":{templateUrl: 'partials/home.html'} } })
以上,告诉我们partials/home.html将会被加载到与"body@content"匹配的ui-view中。暂时对应的ui-view还没有出现,于是等待。
→ 路由看到index.html上的
.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页面上的各个部分
看到
,就加载如下:"header@content":{templateUrl: 'partials/header.html'},
看到
,先加载 "body@content":{templateUrl: 'partials/home.html'}→ 点击header上的链接
点击Photos,来到:
.state('content.photos',{ url: 'photos', views:{ "body@content":{templateUrl: 'partials/photos.html'} } })
把partials/photos.html显示到
中去。点击
,来到:.state('content.about',{ url:'about', views:{ "body@content":{templateUrl: 'partials/about.html'} } })
把partials/about.html显示到
中去。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
如何到达这个子页面呢?修改header中的相关部分如下:
<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>
以上,通过Photos来到photos.html的子页面photos-list.html.
photos-list.html 通过2种途径到相邻页photo-detail.html
<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
<h1>photo-details</h1> <a class="btn btn-default" ui-sref=".comment">通过相对路径去子state</a> <div ui-view></div>
photos-detail-comment.html 则很简单:
app.js state多级嵌套的设置为
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设置为抽象。
.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');
即
.state('content.home',{ url: 'home', views:{ "body@content":{templateUrl: 'partials/home.html'} } })
最终把partials/home.html显示出来。
使用控制器
在实际项目中,数据大多从controller中来。
首先在路由中设置state所用到的控制器以及控制器别名。
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
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
<h1>photos-list</h1> <div ng-init="ctrPhotoList.init()"> <div style="margin:auto; width: 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="width: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设置接收一个路由参数。
.state('content.photos.detail',{ url: '/detail/:id', templateUrl: 'partials/photos-detail.html', controller: 'PhotoDetailController', controllerAs: 'ctrPhotoDetail' })
photos-list.html 送出一个路由参数
<h1>photos-list</h1> <div ng-init="ctrPhotoList.init()"> <div style="margin:auto; width: 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="width: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>
以上,通过把路由参数送出。
controller.js PhotoDetailController控制器通过$stateParams获取路由参数
... //别名: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中获取数据。
<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; width: 60%;"> <div class="well well-sm" style="margin:auto; width: 60%; margin-top: 15px;"> <h4>{{ctrPhotoDetail.photo.title}}</h4> <p>{{ctrPhotoDetail.photo.description}}</p> </div> <div style="margin:auto; width: 80%; margin-bottom: 15px;"> <button style="margin-top: 10px; width:100%;" class="btn btn-default" ui-sref=".comment">Comments</button> </div> </div> <div ui-view></div>
state间如何传字符串参数
在路由中这样设置:
.state('content.photos.detail.comment',{ url:'/comment?skip&limit', templateUrl: 'partials/photos-detail-comment.html', controller: 'PhotoCommentController', controllerAs: 'ctrPhotoComment' })
controllers.js 中修改如下
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 需要把查询字符串参数传递出去
<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; width: 60%;"> <div class="well well-sm" style="margin:auto; width: 60%; margin-top: 15px;"> <h4>{{ctrPhotoDetail.photo.title}}</h4> <p>{{ctrPhotoDetail.photo.description}}</p> </div> <div style="margin:auto; width: 80%; margin-bottom: 15px;"> <button style="margin-top: 10px; width: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
<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; width: 60%;"> <div class="media"> <div class="media-left media-middle"> <a href=""> <img class="img-circle" style="width: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属性,把一个对象赋值给它。
.state('content',{ url: '/', abstract: true, data:{ user: "user", password: "1234" }, views:{ "":{templateUrl: 'partials/content.html'}, "header@content":{templateUrl: 'partials/header.html'}, } })
给header.html加上一个对应的控制器,并提供注销方法。
$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
.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
<form name="form" ng-submit="login(user, password, form.$valid)"> <div class="panel panel-primary" style="width: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 修改如下
<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事件
.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中:
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
<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; width: 60%;"> <div class="well well-sm" style="margin:auto; width: 60%; margin-top: 15px;"> <div class="well well-sm pull-right" style="width: 100px;"> <i>Views <span class="badge">{{ctrPhotoDetail.viewObj.views}}</span></i> </div> <div class="well well-sm pull-right" style="width: 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; width: 80%; margin-bottom: 15px;"> <button style="margin-top: 10px; width:100%;" class="btn btn-default" ui-sref=".comment({skip:0, limit:2})">Comments</button> </div> </div> <div ui-view></div>
StateChangeStart事件
controller.js 增加如下
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:
.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
.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)); } })
以上,添加了
data:{ required: true }
同理,content.photos.detail.comment这个state
.state('content.photos.detail.comment',{ url:'/comment?skip&limit', templateUrl: 'partials/photos-detail-comment.html', controller: 'PhotoCommentController', controllerAs: 'ctrPhotoComment', data:{ required: true } })
StateNotFound事件
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:
.state('content.notfound',{ url:'notfound', views: { "body@content": {templateUrl: 'partials/page-not-found.html'} } })
page-not-found.html
<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事件
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
.state('content.log',{ url:'log', data:{ required: true }, views: { "body@content": {templateUrl: 'partials/log.html'} } })
log.html
<h1><i class="fa fa-file-text-o"></i> Access Log</h1> <div style="margin:auto; width: 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事件
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:
.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
<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>