URL参数
基本参数
通常,url动态部分被称为参数,有几个选项用于指定参数。基本参数如下:
1 2 3 4 5 6 7 8 9 10 | $stateProvider .state('contacts.detail', { // 这里设置了url参数 url: "/contacts/:contactId", templateUrl: 'contacts.detail.html', controller: function ($stateParams) { // If we got here from a url of /contacts/42 expect($stateParams).toBe({contactId: 42}); } }) |
或者,你也可以使用花括号的方式来指定参数:
1 2 | // 与前面的设置方法等效 url: "/contacts/{contactId}" |
示例:
'/hello/'
- 只匹配'/hello/'
路径,没有对斜杠进行特殊处理,这种模式将匹配整个路径,而不仅仅是一个前缀。'/user/:id'
- 匹配'/user/bob'
、'/user/1234!!!'
,甚至还匹配'/user/'
,但是不匹配'/user'
和'/user/bob/details'
。第二个路径段将被捕获作为参数"id"
。'/user/{id}'
- 与前面的示例相同,但使用花括号语法。
含正则表达式的参数
使用花括号的方式可以设置一个正则表达式规则的参数:
1 2 | // 只会匹配 contactId 为1到8位的数字 url: "/contacts/{contactId:[0-9]{1,8}}" |
示例:
'/user/{id:[^/]*}'
- 与'/user/{id}'
相同'/user/{id:[0-9a-fA-F]{1,8}}'
- 与前面的示例相似,但只匹配1到8为的数字和字符'/files/{path:.*}'
- 匹配任何以'/files/'
开始的URL路径,并且捕获剩余路径到参数'path'
中。'/files/*path'
- 与前面相同,捕获所有特殊的语法。
警告:不要把捕获圆括号写进正则表达式,ui-router 的 UrlMatcher 将为整个正则表达式添加捕获。
Query Parameters
可以通过?
来指定参数作为查询参数
1 2 | url: "/contacts?myParam" // 匹配 "/contacts?myParam=value" |
如果你需要不止一个查询参数,请用&
分隔:
1 2 | url: "/contacts?myParam1&myParam2" // 匹配 "/contacts?myParam1=value1&myParam2=wowcool" |
绝对路由(^)
如果你使用绝对 url 匹配的方式,那么你需要给你的url字符串加上特殊符号"^"
。
1 2 3 4 5 6 7 8 9 | $stateProvider .state('contacts', { url: '/contacts', ... }) .state('contacts.list', { url: '^/list', ... }); |
路由将成为:
'contacts'
状态将匹配"/contacts"
'contacts.list'
状态将匹配"/list"
。子状态的url没有附在父状态的url之后的,因为使用了^
。
$stateParams 服务
之前看到的$stateParams
服务是一个对象,包含 url 中每个参数的键/值。$stateParams
可以为控制器或者服务提供 url 的各个部分。
注意:$stateParams
服务必须与一个控制器相关,并且$stateParams
中的“键/值”也必须事先在那个控制器的url
属性中有定义。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | // 如果状态中 url 属性是: url: '/users/:id/details/{type}/{repeat:[0-9]+}?from&to' // 当浏览 '/users/123/details//0' // $stateParams 对象将是 { id:'123', type:'', repeat:'0' } // 当浏览 '/users/123/details/default/0?from=there&to=here' // $stateParams 对象将是 { id:'123', type:'default', repeat:'0', from:'there', to:'here' } |
使用$stateParams
的两个陷阱
- 只有当状态被激活并且状态的所有依赖项都被注入时,
$stateParams
对象才存在。这代表你不能再状态的resolve
函数中使用$stateParams
对象,可以使用$state.current.params
来代替。
1 2 3 4 5 6 7 8 9 10 | $stateProvider.state('contacts.detail', { resolve: { someResolve: function($state){ //*** 不能在这里使用 $stateParams , the service is not ready ***// //*** 使用 $state.current.params 来代替 ***// return $state.current.params.contactId + "!" }; }, // ... }) |
- 在状态控制器中,
$stateParams
对象只包含那些在状态中定义的参数,因此你不能访问在其他状态或者祖先状态中定义的参数。
1 2 3 4 5 6 7 8 9 10 11 12 | $stateProvider.state('contacts.detail', { url: '/contacts/:contactId', controller: function($stateParams){ $stateParams.contactId //*** Exists! ***// } }).state('contacts.detail.subitem', { url: '/item/:itemId', controller: function($stateParams){ $stateParams.contactId //*** 注意! DOESN'T EXIST!! ***// $stateParams.itemId //*** Exists! ***// } }) |
$stateProvider .state(‘blog.index', { templateUrl: ’templates/blog_index.html', data: { current_page: 1, page_size: 20 } })
上面 data 对象就是自定义数据,
里面定义了2页面的当前页和显示内容条数
在视图对应的 controller 中我们就可以通过下面的方法来获取自定义数据.
console.log($state.current.data.current_page); // 1 console.log($state.current.data.page_size); // 20自定义数据也可以被附加到状态控制对象state configObject.该数据和预载入数据resolve属性相似,但是该数据不会被注入到控制器中,promise也不会被预载入,它的用途是从父状态传递数据到子状态。
onEnter,onExit 回调函数
当应用进入或者离开当前状态的视图时会调用这两个函数。这两个函数可以访问预载入的数据。这两个回调函数使开发者可以根据状态改变来采取某些动作,例如在用户要离开时可以弹出对话框‘你确定吗?’以及防止意外操作等。
state事件
$rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams){ ... })
$rootScope.$on('$stateNotFound', function(event, unfoundState, fromState, fromParams){ ... })
$rootScope.$on('$stateChangeSuccess', function(event, toState, toParams, fromState, fromParams){ ... })
$rootScope.$on('$stateChangeError', function(event, toState, toParams, fromState, fromParams, error){ ... })
view事件
View被加载但是DOM树构建之前时:
$scope.$on('$viewContentLoading', function(event, viewConfig){ ... });
View被加载而且DOM树构建完成时:
$scope.$on('$viewContentLoaded', function(event){ ... });
<a href="#/blog/1234”>博客详情</a>
<a ui-sref=“blog.detail({blogID:blogID})”>博客详情</a>
$state.go(‘blog.detail', {blogID:blogID});
when()
该函数需要两个参数:1.当前的路径,2.需要重定向到的路径(或者是需要在路径被访问是运行的函数)。设置重定向前需要为$urlRouterProvider设置when函数来接受一个字符串。例如,当希望重定向一个空的路由到/inbox:
.config(function($urlRouterProvider) {
$urlRouterProvider.when('', '/inbox');});
如果传递的是函数,在路径被匹配时该函数会被执行,处理器返回如下3个值中的一个: - falsy,该回应告诉$urlRouter没有匹配到当前url规则,应该尝试匹配新的路径,这样能保证用户访问了正常的路径。 - 字符串,$urlRouter将该字符串当做重定向的路径。 - TRUE 或者 undefined,该回应告诉$urlRouter,url已被处理
otherwise()
和ngRoute的otherwise()函数相似,在用户提交的路径没有被定义的时候它将重定向到指定的页面。这是个创建’默认‘路径的好方法。 otherwise()只接受一个参数,要么函数要么字符串,字符串必须为合法的url路由地址,函数则会在没有任何路径被匹配的时候被运行。
.config(function($urlRouterProvider) {
$urlRouterProvider.otherwise('/');
// or $urlRouterProvider.otherwise(
function($injector, $location) {
$location.path('/');
});});
rule()
如果想越过任何URL的匹配或者在其他路由前做路由修改,则可以使用rule()函数。在使用它的时候必须返回一个合法的代表路径的字符串。
app.config(function($urlRouterProvider){
$urlRouterProvider.rule(
function($injector, $location) {
return '/index';
});})
最后貼两行代码
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="bootstrap.min.css">
<script src="jquery.min.js"></script>
<script src="angular.js"></script>
<script src="angular-ui-router.js"></script>
<script src="bootstrap.min.js"></script>
<script type="text/javascript">
var app = angular.module('myapp', ['ui.router']);
app.config(['$stateProvider', function($stateProvider) {
$stateProvider.state('index', {
url: '/index',
views : {
'main1' : {
template: 'hello world1'
},
'main2' : {
template: 'hello world2'
}
}
});
}]);
</script>
</head>
<body ng-app="myapp">
<a href="#" ui-sref="index">click</a>
<div ui-view="main1"></div>
<div ui-view="main2"></div>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="bootstrap.min.css">
<script src="jquery.min.js"></script>
<script src="angular.js"></script>
<script src="angular-ui-router.js"></script>
<script src="bootstrap.min.js"></script>
<script type="text/javascript">
var app = angular.module('myapp', ['ui.router']);
app.config(['$stateProvider', function($stateProvider) {
$stateProvider
.state('index', {
url: '/index',
template: 'parent<div ui-view="main1"></div>{{content}}<div ui-view="main2"></div>parent',
controller: ['$scope', 'parent', function($scope, parent) {
console.log(parent)
console.log('after 5s..');
$scope.content = 'hello world';
}],
resolve: {
parent : ['$q', '$timeout', function ($q, $timeout) {
var defer = $q.defer();
$timeout(function () {
defer.resolve('return value');
}, 2000);
console.log('before 5s..')
console.log(defer.promise)
return defer.promise;
}]
}
})
.state('index.about', {
url : '/about', // final path is /index/about, use ^ can be just /about
views : {
'main1' : { //'main1@'
template: 'main1',
// resolve:{}
controller : ['parent', 'child', function(parent, child) {
//调用自身的解决项,以及父路由的解决项
console.log(child, parent);
}]
},
'main2' : { //'main2@'
template: 'main2'
}
},
resolve: {
child: ['parent', function (parent) { // 调用父路由的解决项
return parent + ' and child';
}]
},
});
}]);
</script>
</head>
<body ng-app="myapp">
<a href="#" ui-sref="index.about">click</a>
<div ui-view></div>
<div ui-view="main1"></div><!-- main1@ -->
<div ui-view="main2"></div><!-- main1@ -->
</body>
</html>
关于ui-view如何匹配模板
$stateProvider
.state('contacts.detail', {
url: '/{contactId:[0-9]{1,4}}',
views: {
'' : {
templateUrl: 'app/contacts/contacts.detail.html',
},
'hint@': {
template: 'This is contacts.detail populating the "hint" ui-view'
},
'menuTip': {
templateProvider: ['$stateParams', function($stateParams) {
return '<hr><small class="muted">Contact ID: ' + $stateParams.contactId + '</small>';
}]
}
}
});
首先注意这是一个嵌套模板,父state是contacts,它肯定也有template, 而且一定包含<ui-view>
看views下的三个key(也就是视图名):‘’,‘hint@’, ‘menuTip’
menuTip比较好理解,就是ui-view="menuTip"所在的页面使用下面对应的模板,那么这个页面在哪,默认是在当前state的父亲state的页面,这里contacts.detail的父亲是contacts,所以模板放在contacts的template中的ui-view="menuTip",而contacts的template是放在主页面index.html中的ui-view中的,换句话说这个主页面又算是contacts的父亲.
menuTip的解释也正说明一般子路由的模板是填充父路由中的ui-view.
对于'hint@', 和上面的menuTip比较其实多了一个@,那意思就不一样的了。上面menuTip等同于menuTip@contacts,这边的话因为自己加了@,就变成了hint@' ', ‘ ’表示最顶层的state,也就是index.html中的ui-view。所以最终hint@对应的模板填充到了index.html中的ui-view中,而不是contacts的ui-view中.
对于'',没有@,那么父state是contacts,最后变成' '@contacts,也就是说填充到父state的ui-view="" 中.
这个概念比较绕,不懂还是要多看几遍.