首先给大家介绍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
$stateprovider.state(home,{template: hi})
或者这样配置:
$stateprovider.state(home{views: {: {template: hi}}})
==有名称的ui-view
$stateprovider.state(home,{views: {main : {template: hi}}})
==多个ui-view
$stateprovider.state(home,{views: {:{template: hi},data: {template: data
}}})
项目文件结构
node_modules/
partials/
.....about.html
.....home.html
.....photos.html
app.js
index.html
创建state和view
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
welcome
state之间的跳转
index.html
home
photosabout
以上通过ui-sref属性完成state之间的跳转。
多个view以及state嵌套
有时候,一个页面上可能有多个ui-view,比如:
假设,以上页面属于一个名称为parent的state中。
我们知道在ui-router中,一个state大致是这样设置的:
所有state下views下的所有键值对(类似 body@content:{templateurl: 'partials/photos.html'})都被放到一个键值集合中。而ui-view的工作原理就是根据自己的属性值,到这个键值集合中去找匹配的键,找到就把对应的页面显示出来。
点击header对应的页面链接,可能会跳转到另外的子页面出现在
这个位置。这时候页面出现了子父关系,而每个页面都属于某个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和会根据页头上的点击呈现不同的内容
header.html 把原先indext.html中nav部分放到这里来
home
photosabout
index.html 这时变成了这样
app.js 路由现在这样设置
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这个路由
.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中的相关部分如下:
home
photosabout
以上,通过photos来到photos.html的子页面photos-list.html.
photos-list.html 通过2种途径到相邻页photo-detail.html
photos-list我通过相对路径到相邻的state我通过绝对路径到相邻的state
photo-detail.html 又提供了来到其子页面photos-detail-comment.html的ui-view
photo-details通过相对路径去子state
photos-detail-comment.html 则很简单:
photos-detail-comment
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';}]);//别名:ctrphotophotogallery.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;}}]);//别名:ctrphotolistphotogallery.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;}}]);//别名:ctrphotodetailphotogallery.controller('photodetailcontroller',['$scope', '$state', function($scope,$state){}]);
以上,通过$scope.$$childtail.ctrphotolist在父state中的controller中拿到子state中的controller;通过$scope.$parent.ctrphoto在子state中的controller中拿到父state中的controller。
photos-list.html
photos-list
{{photo.title}}{{photo.description}}
state间如何传路由参数
在content.photos.detail这个state设置接收一个路由参数。
.state('content.photos.detail',{url: '/detail/:id',templateurl: 'partials/photos-detail.html',controller: 'photodetailcontroller',controlleras: 'ctrphotodetail'})
photos-list.html 送出一个路由参数
photos-list
{{photo.title}}{{photo.description}}
以上,通过把路由参数送出。
controller.js photodetailcontroller控制器通过$stateparams获取路由参数
...//别名:ctrphotodetailphotosgallery.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中获取数据。
photo-details通过相对路径去子state{{ctrphotodetail.photo.title}}{{ctrphotodetail.photo.description}}
comments
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';}]);//别名:ctrphotophotogallery.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;}}]);//别名:ctrphotolistphotogallery.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;}}]);//别名:ctrphotodetailphotogallery.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 需要把查询字符串参数传递出去
photo-details通过相对路径去子state{{ctrphotodetail.photo.title}}{{ctrphotodetail.photo.description}}
comments
以上,通过ui-sref=.comment({skip:0, limit:2})把查询字符串传递出去。
photos-detail-comment.html
photos-detail-comment
{{comment.name}}{{comment.comment}}
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
indentification
enter the userenter the password
loginreset{{message}}
header.html 修改如下
home
photos about {{user.name}} sing out sing in
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
photo-details通过相对路径去子stateviews {{ctrphotodetail.viewobj.views}}
viewing {{ctrphotodetail.viewobj.viewing}}
{{ctrphotodetail.photo.title}}{{ctrphotodetail.photo.description}}
comments
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
404 - sorry! not found your page.
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
access log{{log.user ? log.user.name: 'anonymous'}} in {{log.date | date: 'longdate'}} at {{log.date | date: 'shorttime'}}from: {{log.from}} => to: {{log.to}}
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: 'error
'}} }).state('content.error',{url:'error/:error',views:{body@content:{templateurl: 'partials/error.html',controller: function($scope, $stateparams){$scope.error = {message: $stateparams.error}}}}})
error.html
sorry! but this message was displayed: {{error.message}}
