Angularjs中UI Router全攻略
首先给大家介绍angular-ui-router的基本用法。
如何引用依赖angular-ui-router
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
<divui-view></div>
$stateProvider.state("home",{
template:"<h1>hi</h1>"
})
或者这样配置:
$stateProvider.state("home"{
views:{
"":{
template:"<h1>hi</h1>"
}
}
})
==有名称的ui-view
<divui-view="main"></div>
$stateProvider.state("home",{
views:{
"main":{
template:"<h1>hi</h1>"
}
}
})
==多个ui-view
<divui-view></div>
<divui-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
varphotoGallery=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
<!DOCTYPEhtml> <htmllang="en"ng-app="photoGallery"> <head> <metacharset="UTF-8"> <title></title> <linkrel="stylesheet"href="node_modules/bootstrap/dist/css/bootstrap.css"/> </head> <body> <h1>Welcome</h1> <divui-view></div> <scriptsrc="node_modules/jquery/dist/jquery.js"></script> <scriptsrc="node_modules/angular/angular.js"></script> <scriptsrc="node_modules/angular-ui-router/release/angular-ui-router.js"></script> <scriptsrc="node_modules/angular-animate/angular-animate.js"></script> <scriptsrc="node_modules/bootstrap/dist/js/bootstrap.js"></script> <scriptsrc="node_modules/angular-bootstrap/ui-bootstrap-tpls.js"></script> <scriptsrc="app.js"></script> </body> </html>
state之间的跳转
index.html
<navclass="navbarnavbar-inverse"> <divclass="container-fluid"> <divclass="navbar-header"> <buttonclass="navbar-togglecollapsed"type="button"data-toggle="collapse"data-target="#bs-example-navbar-collapse-1"> <spanclass="icon-bar"></span> <spanclass="icon-bar"></span> <spanclass="icon-bar"></span> </button> <aui-sref="home"class="navbar-brand">Home</a> </div> <divclass="collapsenavbar-collapse"id="bs-example-navbar-collapse-1"> <ulclass="navnavbar-nav"> <li> <aui-sref="photos">Photos</a> </li> <li> <aui-sref="about">About</a> </li> </ul> </div> </div> </nav> <divui-view></div>
以上通过ui-sref属性完成state之间的跳转。
多个view以及state嵌套
有时候,一个页面上可能有多个ui-view,比如:
<divui-view="header"></div> <divui-view="body"></div>
假设,以上页面属于一个名称为parent的state中。
我们知道在ui-router中,一个state大致是这样设置的:
<divui-view="header"></div> <divui-view="body"></div>
所有state下views下的所有键值对(类似"body@content":{templateUrl:'partials/photos.html'})都被放到一个键值集合中。而ui-view的工作原理就是根据自己的属性值,到这个键值集合中去找匹配的键,找到就把对应的页面显示出来。
点击header对应的页面链接,可能会跳转到另外的子页面出现在<divui-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和会根据页头上的点击呈现不同的内容
<divui-view="header"></div> <divui-view="body"></div>
header.html把原先indext.html中nav部分放到这里来
<navclass="navbarnavbar-inverse"> <divclass="container-fluid"> <divclass="navbar-header"> <buttonclass="navbar-togglecollapsed"type="button"data-toggle="collapse"data-target="#bs-example-navbar-collapse-1"> <spanclass="icon-bar"></span> <spanclass="icon-bar"></span> <spanclass="icon-bar"></span> </button> <aui-sref="content.home"class="navbar-brand">Home</a> </div> <divclass="collapsenavbar-collapse"id="bs-example-navbar-collapse-1"> <ulclass="navnavbar-nav"> <li> <aui-sref="content.photos">Photos</a> </li> <li> <aui-sref="content.about">About</a> </li> </ul> </div> </div> </nav>
index.html这时变成了这样
<divui-view></div>
app.js路由现在这样设置
varphotoGallery=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这个路由
.state('content.home',{
url:'home',
views:{
"body@content":{templateUrl:'partials/home.html'}
}
})
以上,告诉我们partials/home.html将会被加载到与"body@content"匹配的ui-view中。暂时对应的ui-view还没有出现,于是等待。
→路由看到index.html上的<divui-view></div>
.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页面上的各个部分
看到<divui-view="header"></div>,就加载如下:
"header@content":{templateUrl:'partials/header.html'},
看到<divui-view="body"></div>,先加载"body@content":{templateUrl:'partials/home.html'}
→点击header上的链接
点击<aui-sref="content.photos">Photos</a>,来到:
.state('content.photos',{
url:'photos',
views:{
"body@content":{templateUrl:'partials/photos.html'}
}
})
把partials/photos.html显示到<divui-view="body"></div>中去。
点击<divui-view="body"></div>,来到:
.state('content.about',{
url:'about',
views:{
"body@content":{templateUrl:'partials/about.html'}
}
})
把partials/about.html显示到<divui-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
<divui-view></div>
如何到达这个子页面呢?修改header中的相关部分如下:
<navclass="navbarnavbar-inverse"> <divclass="container-fluid"> <divclass="navbar-header"> <buttonclass="navbar-togglecollapsed"type="button"data-toggle="collapse"data-target="#bs-example-navbar-collapse-1"> <spanclass="icon-bar"></span> <spanclass="icon-bar"></span> <spanclass="icon-bar"></span> </button> <aui-sref="content.home"class="navbar-brand">Home</a> </div> <divclass="collapsenavbar-collapse"id="bs-example-navbar-collapse-1"> <ulclass="navnavbar-nav"> <li> <aui-sref="content.photos.list">Photos</a> </li> <li> <aui-sref="content.about">About</a> </li> </ul> </div> </div>
以上,通过<aui-sref="content.photos.list">Photos</a>来到photos.html的子页面photos-list.html.
photos-list.html通过2种途径到相邻页photo-detail.html
<h1>photos-list</h1> <ul> <li><aui-sref="^.detail">我通过相对路径到相邻的state</a></li> <li><aui-sref="content.photos.detail">我通过绝对路径到相邻的state</a></li> </ul>
photo-detail.html又提供了来到其子页面photos-detail-comment.html的ui-view
<h1>photo-details</h1> <aclass="btnbtn-default"ui-sref=".comment">通过相对路径去子state</a> <divui-view></div>
photos-detail-comment.html则很简单:
<h1>photos-detail-comment</h1>
app.jsstate多级嵌套的设置为
varphotoGallery=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所用到的控制器以及控制器别名。
varphotoGallery=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='WelcometothePhotoGallery';
}]);
//别名:ctrPhoto
photoGallery.controller('PhotoController',['$scope','$state',function($scope,$state){
this.photos=[
{id:0,title:'Photo1',description:'descriptionforphoto1',imageName:'image1.jpg',comments:[
{name:'user1',comment:'Nice'},
{name:'User2',comment:'Verygood'}
]},
{id:1,title:'Photo2',description:'descriptionforphoto2',imageName:'image2.jpg',comments:[
{name:'user2',comment:'Nice'},
{name:'User1',comment:'Verygood'}
]},
{id:2,title:'Photo3',description:'descriptionforphoto3',imageName:'image3.jpg',comments:[
{name:'user1',comment:'Nice'}
]},
{id:3,title:'Photo4',description:'descriptionforphoto4',imageName:'image4.jpg',comments:[
{name:'user1',comment:'Nice'},
{name:'User2',comment:'Verygood'},
{name:'User3',comment:'Soso'}
]}
];
//给子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=newArray();
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>
<divng-init="ctrPhotoList.init()">
<divstyle="margin:auto;width:40px;"ng-if="ctrPhotoList.reading">
<iclass="fafa-spinnerfa-5xfa-pulse"></i>
</div>
<divclass="wellwell-sm"ng-repeat="photoinctrPhotoList.photos">
<divclass="media">
<divclass="media-left"style="width:15%;">
<aui-sref="content.photos.detail">
<imgclass="img-responsiveimg-rounded"src="../asserts/images/{{photo.imageName}}"alt="">
</a>
</div>
<divclass="media-body">
<h4class="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>
<divng-init="ctrPhotoList.init()">
<divstyle="margin:auto;width:40px;"ng-if="ctrPhotoList.reading">
<iclass="fafa-spinnerfa-5xfa-pulse"></i>
</div>
<divclass="wellwell-sm"ng-repeat="photoinctrPhotoList.photos">
<divclass="media">
<divclass="media-left"style="width:15%;">
<aui-sref="content.photos.detail({id:photo.id})">
<imgclass="img-responsiveimg-rounded"src="../asserts/images/{{photo.imageName}}"alt="">
</a>
</div>
<divclass="media-body">
<h4class="media-heading">{{photo.title}}</h4>
{{photo.description}}
</div>
</div>
</div>
</div>
以上,通过<aui-sref="content.photos.detail({id:photo.id})">把路由参数送出。
controller.jsPhotoDetailController控制器通过$stateParams获取路由参数
...
//别名:ctrPhotoDetail
photosGallery.controller('PhotoDetailController',['$scope','$state','$stateParams',
function($scope,$state,$stateParams){
varid=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>
<aclass="btnbtn-default"ui-sref=".comment">通过相对路径去子state</a>
<aui-sref="content.photos.list"style="margin-left:15px;">
<iclass="fafa-arrow-circle-leftfa-2x"></i>
</a>
<divng-init="ctrPhotoDetail.init()">
<imgclass="img-responsiveimg-rounded"ng-src="../assets/images/{{ctrPhotoDetail.photo.imageName}}"
style="margin:auto;width:60%;">
<divclass="wellwell-sm"style="margin:auto;width:60%;margin-top:15px;">
<h4>{{ctrPhotoDetail.photo.title}}</h4>
<p>{{ctrPhotoDetail.photo.description}}</p>
</div>
<divstyle="margin:auto;width:80%;margin-bottom:15px;">
<buttonstyle="margin-top:10px;width:100%;"
class="btnbtn-default"ui-sref=".comment">Comments</button>
</div>
</div>
<divui-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='WelcometothePhotoGallery';
}]);
//别名:ctrPhoto
photoGallery.controller('PhotoController',['$scope','$state',function($scope,$state){
this.photos=[
{id:0,title:'Photo1',description:'descriptionforphoto1',imageName:'image1.JPG',comments:[
{name:'User1',comment:'Nice',imageName:'man.png'},
{name:'User2',comment:'Verygood',imageName:'man.png'},
{name:'User3',comment:'Nice',imageName:'woman.png'},
{name:'User4',comment:'Verygood',imageName:'woman.png'},
{name:'User5',comment:'Verygood',imageName:'man.png'},
{name:'User6',comment:'Nice',imageName:'woman.png'},
{name:'User7',comment:'Soso',imageName:'man.png'}
]},
{id:1,title:'Photo2',description:'descriptionforphoto2',imageName:'image2.JPG',comments:[
{name:'User1',comment:'Nice',imageName:'man.png'},
{name:'User2',comment:'Verygood',imageName:'man.png'},
{name:'User3',comment:'Nice',imageName:'woman.png'},
{name:'User4',comment:'Verygood',imageName:'woman.png'}
]},
{id:2,title:'Photo3',description:'descriptionforphoto3',imageName:'image3.JPG',comments:[
{name:'User1',comment:'Nice',imageName:'man.png'},
{name:'User2',comment:'Verygood',imageName:'man.png'},
{name:'User3',comment:'Nice',imageName:'woman.png'},
{name:'User4',comment:'Verygood',imageName:'woman.png'},
{name:'User5',comment:'Verygood',imageName:'man.png'},
{name:'User6',comment:'Nice',imageName:'woman.png'},
{name:'User7',comment:'Soso',imageName:'man.png'}
]},
{id:3,title:'Photo4',description:'descriptionforphoto4',imageName:'image4.JPG',comments:[
{name:'User6',comment:'Nice',imageName:'woman.png'},
{name:'User7',comment:'Soso',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=newArray();
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){
varid=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){
varid,skip,limit=null;
this.comments=newArray();
this.init=function(){
id=parseInt($stateParams.id);
varphoto=$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>
<aclass="btnbtn-default"ui-sref=".comment">通过相对路径去子state</a>
<aui-sref="content.photos.list"style="margin-left:15px;">
<iclass="fafa-arrow-circle-leftfa-2x"></i>
</a>
<divng-init="ctrPhotoDetail.init()">
<imgclass="img-responsiveimg-rounded"ng-src="../assets/images/{{ctrPhotoDetail.photo.imageName}}"
style="margin:auto;width:60%;">
<divclass="wellwell-sm"style="margin:auto;width:60%;margin-top:15px;">
<h4>{{ctrPhotoDetail.photo.title}}</h4>
<p>{{ctrPhotoDetail.photo.description}}</p>
</div>
<divstyle="margin:auto;width:80%;margin-bottom:15px;">
<buttonstyle="margin-top:10px;width:100%;"
class="btnbtn-default"ui-sref=".comment({skip:0,limit:2})">Comments</button>
</div>
</div>
<divui-view></div>
以上,通过ui-sref=".comment({skip:0,limit:2})把查询字符串传递出去。
photos-detail-comment.html
<h1>photos-detail-comment</h1>
<divng-init="ctrPhotoComment.init()"style="margin-top:15px;">
<divng-repeat="commentinctrPhotoComment.comments"class="wellwell-sm"style="margin:auto;width:60%;">
<divclass="media">
<divclass="media-leftmedia-middle">
<ahref="">
<imgclass="img-circle"style="width:60px;"src="../assets/images/{{comment.imageName}}"alt="">
</a>
</div>
<divclass="media-body">
<h4class="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:'Userorpasswordincorrect.'
},
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
}
//OrInherited
/*$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
<formname="form"ng-submit="login(user,password,form.$valid)">
<divclass="panelpanel-primary"style="width:360px;margin:auto;">
<divclass="panel-heading">
<h3class="panel-title">Indentification</h3>
</div>
<divclass="panel-body">
<inputname="user"type="text"class="form-control"ng-model="user"placeholder="User..."required>
<spanng-show="form.user.$error.required&&form.user.$dirty"class="labellabel-danger">Entertheuser</span>
<hr>
<inputname="password"type="password"class="form-control"ng-model="password"placeholder="Password..."required>
<spanng-show="form.password.$error.required&&form.password.$dirty"class="labellabel-danger">Enterthepassword</span>
</div>
<divclass="panel-footer">
<buttonclass="btnbtn-default"type="submit">Login</button>
<buttonclass="btnbtn-default"type="reset">Reset</button>
<spanclass="labellabel-danger">{{message}}</span>
</div>
</div>
</form>
header.html修改如下
<navclass="navbarnavbar-inverse">
<divclass="container-fluid">
<divclass="navbar-header">
<buttonclass="navbar-togglecollapsed"type="button"data-toggle="collapse"data-target="#bs-example-navbar-collapse-1">
<spanclass="icon-bar"></span>
<spanclass="icon-bar"></span>
<spanclass="icon-bar"></span>
</button>
<aclass="navbar-brand"ui-sref="content.home">Home</a>
</div>
<divclass="collapsenavbar-collapse"id="bs-example-navbar-collapse-1">
<ulclass="navnavbar-nav">
<li>
<aui-sref="content.photos.list">Photos</a>
</li>
<li>
<aui-sref="content.about">About</a>
</li>
</ul>
<ulclass="navnavbar-navnavbar-right">
<ling-if="user.name"class="dropdown">
<aclass="dropdown-toggle"role="button"aria-expanded="false"href="#"data-toggle="dropdown">{{user.name}}<spanclass="caret"></span></a>
<ulclass="dropdown-menu"role="menu">
<li><aui-sref="content.home"ng-click="logoff()">Singout</a></li>
</ul>
</li>
<ling-if="!user.name">
<aui-sref="content.login">SingIn</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){
varphoto=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){
varphoto=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){
varid=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>
<aclass="btnbtn-default"ui-sref=".comment">通过相对路径去子state</a>
<aui-sref="content.photos.list"style="margin-left:15px;">
<iclass="fafa-arrow-circle-leftfa-2x"></i>
</a>
<divng-init="ctrPhotoDetail.init()">
<imgclass="img-responsiveimg-rounded"ng-src="../assets/images/{{ctrPhotoDetail.photo.imageName}}"
style="margin:auto;width:60%;">
<divclass="wellwell-sm"style="margin:auto;width:60%;margin-top:15px;">
<divclass="wellwell-smpull-right"style="width:100px;">
<i>Views<spanclass="badge">{{ctrPhotoDetail.viewObj.views}}</span></i>
</div>
<divclass="wellwell-smpull-right"style="width:110px;">
<i>Viewing<spanclass="badge">{{ctrPhotoDetail.viewObj.viewing}}</span></i>
</div>
<h4>{{ctrPhotoDetail.photo.title}}</h4>
<p>{{ctrPhotoDetail.photo.description}}</p>
</div>
<divstyle="margin:auto;width:80%;margin-bottom:15px;">
<buttonstyle="margin-top:10px;width:100%;"
class="btnbtn-default"ui-sref=".comment({skip:0,limit:2})">Comments</button>
</div>
</div>
<divui-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){
varphoto=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){
varphoto=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
<divclass="wellwell-sm"style="margin:20px;"> <iclass="fafa-frown-ofa-4xpull-left"></i><h3>404-Sorry!Notfoundyourpage.</h3> </div>
StateChangeSuccess事件
photosGallery.controller('RootController',['$scope','$state','$rootScope',
function($scope,$state,$rootScope){
$rootScope.accessLog=newArray();
$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:newDate()
});
});
}
]);
添加一个state
.state('content.log',{
url:'log',
data:{
required:true
},
views:{
"body@content":{templateUrl:'partials/log.html'}
}
})
log.html
<h1><iclass="fafa-file-text-o"></i>AccessLog</h1>
<divstyle="margin:auto;width:380px;">
<divclass="wellwell-sm"ng-repeat="loginaccessLogtrackby$index">
<iclass="fafa-pencilfa-2xpull-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=newArray();
$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:newDate()
});
});
$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'Errorincode.';
}
},
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
<divclass="wellwell-sm"style="margin:20px;">
<iclass="fafa-exclamation-circlefa-2x">Sorry!Butthismessagewasdisplayed:{{error.message}}</i>
</div>