State Manager.
$stateProvider 和angular的router功能相似,但它只关心状态(state)的变化
一个最简单的state形式如下所示:
<!-- in index.html --> <body ng-controller="MainCtrl"> <section ui-view></section> </body>
// in app-states.js (or whatever you want to name it)
$stateProvider.state(\'contacts\', {
template: \'<h1>My Contacts</h1>\'
})
temlate 如何插入
如果state 被激活了,该 state 的template 就会被自动地插入到它的parent template的 ui-view 中。如果该state处于top-level(即没有父级的state).则它的parent template 就是 index.html
激活一个state
有三种激活state的方式
-
1. 调用 $state.go(), 还有一个类似的方法 $state.transitionTo,官方文档说是low level 的方法,大部分情况都用 go(),所以...
$state.go(to, [, toParams], [, options])
$state.go() 提供一个转换到 new state 的方便快捷的方式,它会在内部调用 $state.transitionTo 方法,并且自动设置options 参数为
{ location: true, inherit: true, relative: $state.$current, notify: true }to
$state.go()可以传入一个state name 或者 一个相对路径作为第一个参数, 如果一个路径以 ^ 或者 . 开头,则改路径是相对的,否则是绝对的。
几个例子:
-
$state.go(\'contact.detail\')会转换到 \'contact.detail\' state -
$state.go(\'^\')转换到 parent state. -
$state.go(\'^.sibling\')转换到 sibling state. -
$state.go(\'.child.grandchild\')转换到 grandchild state.
toParams
toParams 以一个对象直接量的形式填充到 $stateParams
如果参数为指定将会从当前已经定义的参数中继承, sibilings state 可以共享继承自parent state的参数,child state 可以继承当前state的参数
options
各种配置参数
-
-
2. 点击包含ui-sref指令的链接
用法:
ui-sref=\'stateName\' --- 转换到一个state,无附加参数
ui-sref=\'stateName({param: value, param: value})\'; --- 转换到一个state,并且传递参数 -
3. 链接跳转到 state 对应的 url
提一下url传递参数的方法,
url: \'/contacts/:contactId\' ----- 当访问 /contact/42时, $stateParams 为 {contactId: 42}
url: \'/contacts/{contactId}\' ----- 和上面一样
例子:
\' /hello/ \' -- 只匹配 url 为 /hello/
\' /user/:id \' -- 匹配 url 为 \'/user/bob\' 或者 \'/user/1234\', 也能匹配 \'/user/\'. 但是不匹配 \'/user\' , \'user/bob/detail\'
\' /user/{id} \' -- 与前面一个相同
\' /user/{id: int} \' -- 参数正则匹配, : 后面可以写一个正则,但是不能使用分组
resolve
resolve属性是一个可选的对象,应当被作为依赖注入到controller中,当 $stateChangeSuccess 事件触发时,resolve会在controller实例化之前被解析。
resolve是一对键值对,其中
- key - {string}:a name of a dependency to be injected into the controller.
- factory - {string | function}:
如果是字符串,则该字符串为service的别名
如果是函数,则该函数被注入,其返回值被当作依赖。如果返回值是一个promise, 它会在controller实例化之前被解析,并将返回值注入到controller中
例子:$stateProvider.state(\'myState\', { resolve:{ // 直接返回一个值,由于不是promise,会被立即解析 simpleObj: function(){ return {value: \'simple!\'}; }, // 返回要给promise // 这是一种典型的用法 // 你需要在function中注入你用到的所有服务,比如这里的 $http服务 promiseObj: function($http){ // $http returns a promise for the url data return $http({method: \'GET\', url: \'/someUrl\'}); }, // 另外一种常见用法,如果你需要对返回值进行操作,可以使用链式的 .then() 方法 promiseObj2: function($http){ return $http({method: \'GET\', url: \'/someUrl\'}) .then (function (data) { return doSomeStuffFirst(data); }); }, // 直接使用已经定义的service的名称, // 这里会在当前的module中寻找名为 translations 的service,并将其返回 // 注意: 这里的service也可以返回一个promise,然后像上面那样操作 translations: "translations", // 直接将 service 注入到 resolve 函数的例子 // service 返回一个promise // 小建议: 注入 $stateParams 来访问 url 参数 translations2: function(translations, $stateParams){ // 我们假定 getLang 是一个service 的方法,使用$http服务从url为 "/:lang/home" 来获取数据 return translations.getLang($stateParams.lang); }, // 返回自定义promise的例子 greeting: function($q, $timeout){ var deferred = $q.defer(); $timeout(function() { deferred.resolve(\'Hello!\'); }, 1000); return deferred.promise; } }, // controller 会等上面所有的item完全解析之后才进行实例化。 // 在下面的例子中,所有的promise对象在controller实例化之前都已经全部初始化并且注入到controller中等待被使用 controller: function($scope, simpleObj, promiseObj, promiseObj2, translations, translations2, greeting){ $scope.simple = simpleObj.value; // You can be sure that promiseObj is ready to use! $scope.items = promiseObj.data.items; $scope.items = promiseObj2.items; $scope.title = translations.getLang("english").title; $scope.title = translations2.title; $scope.greeting = greeting; } })
嵌套state
几种嵌套state的方法
1. 使用 . 号,例如 .state(\' contacts.list \', {})
2. 使用 ui-router.stateHelper 从一个state树创建state
3. 使用 parent 属性和string类型的 parent state. 例如, parent: \'contacts\'
4. 使用 parent 属性和object类型的 parent state. 例如, parent: contacts (这里 contacts 是一个stateObject)
Child State 从 Parent State继承了啥
- 解析后的resolve依赖(原文是 resolved dependencies via resolve, 我翻译的什么玩意儿啊)
- 自定义的data属性
abstract states 的 child state 会继承它的url prefix。除此之外没有其他的继承
Inherited Resolved Dependencies(0.2.0新增)
child state 会从它的祖先state(s)继承 resolved dependencies, 并且可以覆盖他们。你可以在 child states 的 resolve function 中注入 resolved dependencies。
直接看例子吧,翻译成中文说不明白
$stateProvider.state(\'parent\', { resolve:{ resA: function(){ return {\'value\': \'A\'}; } }, controller: function($scope, resA){ $scope.resA = resA.value; } }) .state(\'parent.child\', { resolve:{ // 这里的 resA 继承自parent state, 可以将其注入到 resolve function,和 controller 中 resB: function(resA){ return {\'value\': resA.value + \'B\'}; } }, controller: function($scope, resA, resB){ $scope.resA2 = resA.value; $scope.resB = resB.value; }
注意:resolve 的key必须被注入到 child states 中,如果你想在 children state 实例化之前解析 promise 的话
抽象state (Abstract States)
抽象state 可以拥有child state,但是不能使其自身得到激活. 一个抽象state就是不能被转换到的状态,当它的任何一个后代state被激活时它会被隐式的激活。
你可能会用到抽象state的一些场景
1,在所有 child state的url之前插入一个url prefix
2,通过它自身的 ui-view(s) 插入一个 template,用来装载 child states
- 指定一个可选的controller 给template,controller与template必须匹配(匹配啥呢?)
- 另外,children states 可以继承$scope对象,但是要明确一点,$scope是通过 view 的嵌套来继承的,而不是state,state的嵌套与$scope的继承没有任何关系
3,提供 resolved dependencies 给 child states 使用
4,提供 自定义data给child states 或者 事件监听器使用
5,执行 onEnter 或 onExit 函数
记住:抽象state 仍然需要它自己的 template 来装载它的孩子们。所以,如果你正在使用抽象state做上面的事情,你需要额外的设置 template: \'<ui-view/>\';
抽象state使用示例:
为后代插入url prefix 的例子
$stateProvider .state(\'contacts\', { abstract: true, url: \'/contacts\', // Note: abstract still needs a ui-view for its children to populate. // You can simply add it inline here. template: \'<ui-view/>\' }) .state(\'contacts.list\', { // url will become \'/contacts/list\' url: \'/list\' //...more }) .state(\'contacts.detail\', { // url will become \'/contacts/detail\' url: \'/detail\', //...more })
插入template 来装载后代的例子
$stateProvider .state(\'contacts\', { abstract: true, templateUrl: \'contacts.html\' }) .state(\'contacts.list\', { // contacts.list.html的html片段会被插入到 contacts.html中 templateUrl: \'contacts.list.html\' }) .state(\'contacts.detail\', { // 同样contacts.detail.html的html片段也会被插入到 contacts.html中 templateUrl: \'contacts.detail.html\' })
<!-- contacts.html --> <h1>Contacts Page</h1> <div ui-view></div>
综合应用
$stateProvider
.state(\'contacts\', {
abstract: true,
url: \'/contacts\',
templateUrl: \'contacts.html\',
controller: function($scope){
$scope.contacts = [{ id:0, name: "Alice" }, { id:1, name: "Bob" }];
}
})
.state(\'contacts.list\', {
url: \'/list\',
templateUrl: \'contacts.list.html\'
})
.state(\'contacts.detail\', {
url: \'/:id\', // url 会变为 \'/contacts/:id\'
templateUrl: \'contacts.detail.html\',
// 继承了contacts state 的$scope
controller: function($scope, $stateParams){
$scope.person = $scope.contacts[$stateParams.id];
}
})
<!-- contacts.html --> <h1>Contacts Page</h1> <div ui-view></div>
<!-- contacts.list.html --> <ul> <li ng-repeat="person in contacts"> <a ng-href="#/contacts/{{person.id}}">{{person.name}}</a> </li> </ul>
<!-- contacts.detail.html --> <h2>{{ person.name }}</h2>
多个命名的views
当你需要设置多个views时,要用到state的views属性,states是要给对象
views 会覆盖 state 的template 属性
当你设置了 views 对象时,state 的 templateUrl, template, templateProvider属性会被忽略,在这种情况下,你需要一个parent view 来安置这些 views, 你可以定义一个包含一个template的抽象state,一个在该抽象state template 内部的child state来容纳这个 views 对象。
views 对象中的key 名应该与 ui-view中的名称匹配
<!-- index.html --> <body> <div ui-view="filters"></div> <div ui-view="tabledata"></div> <div ui-view="graph"></div> </body>
$stateProvider .state(\'report\', { views: { \'filters\': { ... templates and/or controllers ... }, \'tabledata\': {}, \'graph\': {}, } })
每个在views中的view可以单独设置它自己的template,controller和resolve
$stateProvider .state(\'report\',{ views: { \'filters\': { templateUrl: \'report-filters.html\', controller: function($scope){ ... controller stuff just for filters view ... } }, \'tabledata\': { templateUrl: \'report-table.html\', controller: function($scope){ ... controller stuff just for tabledata view ... } }, \'graph\': { templateUrl: \'report-graph.html\', controller: function($scope){ ... controller stuff just for graph view ... } } } })
view names 相对vs绝对
每个view会遵从 viewname@statename 的机制在后台分配一个绝对名称,这里 viewname 是写在view 指令中那个,statename 是 view指令所在的 那个state 的绝对名称。
你可以根绝绝对命名语法写自己的view names
上面要给例子可以用绝对命名改写成以下形式:
.state(\'report\',{ views: { \'filters@\': { }, \'tabledata@\': { }, \'graph@\': { } } })
这里 view names 全部采用绝对命名, 分别指向位于未命名state的 template 中的三个 views ( "filters" , "tabledata", "graph").由于root state未命名,所以这里@后面什么也没有。未命名的 template 就是 index.html
绝对命名法可以让我们做很多强大的view 定位,看栗子
<!-- index.html (root unnamed template) --> <body ng-app> <div ui-view></div> <!-- contacts.html plugs in here --> <div ui-view="status"></div> </body> <!-- contacts.html --> <h1>My Contacts</h1> <div ui-view></div> <div ui-view="detail"></div> <!-- contacts.detail.html --> <h1>Contacts Details</h1> <div ui-view="info"></div>
$stateProvider .state(\'contacts\', { // 这个template会被插入到为命名的ui-view中,因为他是top level 的 //state,它的父级state 的template就是index.html templateUrl: \'contacts.html\' }) .state(\'contacts.detail\', { views: { //////////////////////////////////// // Relative Targeting // // Targets parent state ui-view\'s // //////////////////////////////////// // 相对地命中父级state-contacts中的 detail view // <div ui-view=\'detail\'/> within contacts.html "detail" : { }, // 相对地命中父级state-contacts中的未命名view // <div ui-view/> within contacts.html "" : { }, /////////////////////////////////////////////////////// // 绝对 Targeting using \'@\' // //命中当前state或者其祖先state的任何view // /////////////////////////////////////////////////////// //绝对命中当前state, \'contacts.detail\'. // <div ui-view=\'info\'/> within contacts.detail.html "info@contacts.detail" : { } // 绝对命中 \'contacts\' state 中的detail view // <div ui-view=\'detail\'/> within contacts.html "detail@contacts" : { } // 绝对命中父级state-- \'contacts\' 中的未命名view. // <div ui-view/> within contacts.html "@contacts" : { } //绝对命中未命名根state中的status view. // <div ui-view=\'status\'/> within index.html "status@" : { } // 绝对命中未命名根state中的未命名view // <div ui-view/> within index.html "@" : { } });
搞得很烦,到此为止!!