本篇接著上一篇angular的uiRouter服務學習(3)繼續講解uiRouter的用法
本篇主要講解uiRouter的url路由
大多數情況下,狀態是和url相關聯的: 當url改變,啟用對應的狀態.當狀態發生改變,同步url. 所以,在設定狀態的一開始,就應該把url路由的設計考慮進去,同時保持路由和狀態的分離.
其實在之前幾篇的栗子裡,已經多次用到了url路由,比如:
$stateProvider .state('contacts', { url: "/contacts", templateUrl: 'contacts.html' })
當url被導航到baseUrl/contacts,contacts狀態就會被啟用,然後ui-view元素就會被contacts.html填充.另外,如果我們使用transitionTo('contacts')來啟用contacts狀態,url也會被更新為 baseUrl/contacts
URL 引數
基本引數
通常,url會有動態的部分,這些部分就是url引數.有幾種方法可以定義url引數.下面是一個最基本的栗子:
$stateProvider.state('contacts.detail', { url: "/contacts/:contactId", templateUrl: 'contacts.detail.html', controller: function ($stateParams) { $scope.contactId = $stateParams.contactId; } })
也可以使用花括號:
url: "/contacts/{contactId}"
下面舉幾個常用的栗子:
<p> hello/路由:<a href="hello/">'hello/'</a> </p> <p> hello/1路由: 匹配'/hello/'無效 <a href="hello/1">'hello/1 無效'</a> </p> <p> hello路由: 匹配'/hello/'無效 <a href="hello">'hello 無效'</a> </p> <p> user/1路由: 匹配'user/{id}'或'user/:id' <a href="user/1">'user/:id'</a> </p> <p> user/1/路由: 匹配'user/:id'無效 <a href="user/1/">'user/:id/ 無效'</a> </p> <p> user/路由: <a href="user/">'user/'</a> </p> <p> user/anotheruser路由: 匹配'user/{id:int}'無效 <a href="user/anotheruser">'user/{id:int} 下無效'</a> </p>
var hello = { name:'hello', /*注意,只能匹配到'/hello/',最後一個/不能少,最後一個/後面也不能再有其他內容*/ url:'/hello/', template:'<h3>/hello/</h3>' }; var user = { name:'user', /*注意,可以匹配到/user/,也就是說,引數為空也能被匹配到,使用花括號和使用:完全一致*/ /*url:'/user/:id',*/ /*url:'/user/{id}',*/ /*使用了:int,user/後面必須是一個整數,否則不匹配*/ url:'/user/{id:int}', templateProvider: function($stateParams){ return '<h1>user/'+$stateParams.id+'</h1>' } };
'/hello/': 只能匹配到'/hello/',最後一個/不能少,最後一個/後面也不能再有其他內容, 'hello/1'無效, 'hello'也無效
'/user/:id' 或 'user/{id}': 兩種寫法效果一樣. 可以匹配 'user/', 可以匹配'user/...', 不能匹配'user/.../' user後面內容會被作為$stateParams的id屬性值
'/user/{id:int}': id必須為整數. 不能匹配到'user/anotheruser'這樣id不是整數的url
正則引數
使用花括號來定義引數,可以自定義正則來匹配url:
var user = { name:'user', /*id的長度在1-8位之間*/ url:'/user/{id:[0-9a-fA-F]{1,8}}', templateProvider: function($stateParams){ return '<h1>user/'+$stateParams.id+'</h1>' } };
下面舉幾個正則的栗子:
<p> contacts/0123456789路由: 匹配'contacts/{contactId:[0-9]{1-8}}'無效 <a href="contacts/0123456789">'contacts/{contactId:[0-9]{1-8}}'</a> </p>
var user = { name:'user', /*相當於{id}*/ /*url:'/user/{id:[^/]*}',*/ /*id的長度在1-8位之間*/ url:'/user/{id:[0-9a-fA-F]{1,8}}', templateProvider: function($stateParams){ return '<h1>user/'+$stateParams.id+'</h1>' } };
var contacts = { name:'contacts', /*匹配0-9數字,長度在1-8之間*/ url:'/contacts/{contactId:[0-9]{1,8}}', templateProvider:function($stateParams){ return '<h1>contacts/'+$stateParams.contactId+$stateParams.myParam+$stateParams.myName+'</h1>' } };
'/user/{id:[^/]*}': 相當於'/user/:id' 和 '/user/{id}'
'contacts/{contactId:[0-9]{1-8}}': contactsId是長度為1-8位的數字, 比如上面超過8位的url 'contacts/0123456789' ,就不會被匹配到.
'/user/{id:[0-9a-fA-F]{1,8}}': id是長度為1-8的任意字元.
匹配包含'/'的全部內容作為引數:
前面說到的匹配路徑,都是把 / 後面的內容捕獲為引數的,但如果 / 後面還有 /,那麼不僅不會被捕獲,而且url不會被匹配到.如果需要捕獲 / 後面的全部內容作為引數,可以這樣寫:
<p> files/paths/path路由: files後面整體作為一個引數<a href="files/paths/path">'/files/*path'</a> </p>
var files = { name:'files', /*匹配/files/開頭的所有url,後面有多少個'/'都可以,'/'部分也會被作為path這個整體*/ /*url:'/files/{path:.*}',*/ /*同上,獲取包括'/'的全部作為引數的一種簡寫*/ url:'/files/*path', templateProvider:function($stateParams){ return '<h1>files/'+$stateParams.path+'</h1>' } };
url引數的引數:
還可以把url的引數也可以被作為引數捕獲到$stateParams裡.
<p> contacts/0123?myParam路由: <a href="contacts/0123?myParam">'/contacts/{contactId:[0-9]{1-8}}?myParam'</a> </p> <p> contacts/0123?myParam=param&myName=name路由: <a href="contacts/0123?myParam=param&myName=name">'/contacts/{contactId:[0-9]{1-8}}?myParam&myName'</a> </p>
var contacts = { name:'contacts', /*匹配0-9數字,長度在1-8之間*/ /*url:'/contacts/{contactId:[0-9]{1,8}}',*/ /*在上面的基礎上,必須要有myParam引數*/ /*url:'/contacts/{contactId:[0-9]{1,8}}?myParam',*/ /*在上面的基礎上,必須要有myParam和myName兩個引數*/ url:'/contacts/{contactId:[0-9]{1,8}}?myParam&myName', templateProvider:function($stateParams){ return '<h1>contacts/'+$stateParams.contactId+$stateParams.myParam+$stateParams.myName+'</h1>' } };
在url後面加上?myParam,則url的myParam引數值會被捕獲,成為$stateParams的myParam屬性值.
注意,即使url不帶有myParam引數,它一樣可以被匹配,$stateParams的myParam屬性值就是undefined.
如果要捕獲多個引數,可以用 & 符號連線多個引數
巢狀狀態的url路由:
追加url(預設方式):
巢狀狀態路由匹配時,子狀態的url前會加上父狀態的url,然後匹配和在一起的那個url:
<p> contacts/0123?myParam=param&myName=name路由: <a href="contacts/0123?myParam=param&myName=name">'/contacts/{contactId:[0-9]{1-8}}?myParam&myName'</a> </p> <p> contacts/list/0123?myParam路由: '/contacts'路由的子路由'/list': <a href="contacts/list/0123?myParam">'/contacts/list'</a> </p> <p> list/0123?myParam路由: '/contacts'路由的子路由'^/list'<a href="list/0123?myParam">'/list'</a> </p>
var contacts = { name:'contacts', url:'/contacts', template:'<div ui-view></div>' }; var list = { name:'contacts.list', url:'/list/{contactId:[0-9]{1,8}}?myParam', parent:'contacts', templateProvider:function($stateParams){ return '<h1>contacts/'+$stateParams.contactId+$stateParams.myParam+$stateParams.myName+'</h1>' } };
list是contacts的子狀態,所以list狀態匹配的url應該是父狀態的url+子狀態的url: '/contacts/list/{contactId:[0-9]{1,8}}?myParam'
絕對url:
在子狀態的url前加上 ^ 符號,則子狀態的url前不會拼合父狀態的url:
同樣上面的這個栗子:
url:'^/list/{contactId:[0-9]{1,8}}?myParam'
則子狀態匹配的url就是'/list/{contactId:[0-9]{1,8}}?myParam'
$stateParams 服務:
關於$stateParams這個服務,之前的栗子裡已經用到過很多次了. 可以知道,它是一個物件,用於存放url裡的引數,這裡的引數有兩種,一種是狀態的url屬性裡定義的引數,另外一個就是url?後面的引數.無論哪一種,每一個引數都會被放在$stateParams物件裡.使用$stateParams可以讓我們使用url中的任意部分.
注意: $stateParams服務只能用在狀態裡.不能注入到其它地方.
下面來看一個$stateParams的栗子:
<p> users/123/detail//0路由: <a href="users/123/details//0">'/users/:id/details/{type}/{repeat:[0-9]+}?from&to'</a> </p> <p> users/123/detail/default/0?from=there&to=here路由: <a href="users/123/details/default/0?from=there&to=here">'/users/:id/details/{type}/{repeat:[0-9]+}?from&to'</a> </p>
var users = { name:'users', url:'/users/:id/details/{type}/{repeat:[0-9]+}?from&to', templateProvider:function($stateParams){ console.log($stateParams); return '<h1>'+$stateParams.type+' '+$stateParams.repeat+' '+$stateParams.from+' '+$stateParams.to+'</h1>' } };
這裡url裡一共定義了5個引數: id type repeat from to
當導航到匹配的路徑時,狀態被啟用,栗子中的兩個url所對應的$stateParams如下:
$stateParams 服務的兩個陷阱:
*以下兩個問題只存在於老的版本中,最新版本的測試發現,這兩個問題都已經沒有了.
1.每個狀態所對應的$stateParams只包含該狀態url裡定義的引數,不包含其它狀態裡定義的引數,即使是父狀態,子狀態,也不包含.
2.$stateParams和$routeParams一樣,是等到狀態被啟用完成,resolve都解析後才生成的,所以是不能用在resolve函式裡的,只能用$state.current.params來替代.
但是新版本的已經解決了這個問題.現在可以在resolve函式裡使用了.
$urlRouterProvider
$urlRouterProvider服務用來監測$location. 當$location發生變化時,它會根據你定義的規則一一匹配,直到找到所匹配的. 所有的url都會被編譯到一個urlMatcher物件裡(詳情檢視下面的$urlMatcherFactory)
$urlRouterProvider有下面幾個方法.可以使用這些方法在模型中進行配置:
app.config(function($urlRouterProvider){ $urlRouterProvider.when().otherwise().rule() });
.when(what,handler) 方法重定向url:
what: 字串/正則/urlMatcher物件,表示需要重定向的url
handler: 字串/函式, 表示要重定向到的url
如果handler是一個字串,它就是要重定向到的地址.
handler是字串的栗子:
<p> 空 <a href="/">''</a> </p>
urlRouting.config(function($urlRouterProvider){ $urlRouterProvider.when('/','/hello/') });
當點選這個空連結的時候,他會重定向到/hello/,啟用對應的狀態.
如果handler是函式,那麼它可以被注入服務.可以注入$match服務: (這一部分沒有搞懂)
.otherwise() 方法重定向url:
傳入一個引數,字串或者函式, 字串表示你想要重定向到的url地址. 函式返回想要重定向到的地址. 函式可以被注入兩個依賴: $injector和$location
urlRouting.config(function($urlRouterProvider){ /*沒有匹配到任何狀態或者.when()重定向時,重定向到'users/123/details//0'*/ $urlRouterProvider.otherwise('users/123/details//0') });
開啟頁面直接重定向到 'users/123/details//0'
.rule() 方法自定義url處理方式:
.rule()方法接受一個函式作為引數,函式可以注入$injector和$location服務.返回一個字串格式的有效路由.
舉個栗子:
<p> HELLO/ <a href="HELLO/">'HELLO/'</a> </p>
$urlRouterProvider.rule(function($injector,$location){ var path = $location.path(),normalized = path.toLowerCase(); if(path!==normalized){ return normalized } })
通過rule函式,HELLO/會被轉換成hello/,啟用對應的狀態
$urlMatcherFactory和urlMatchers
在前面介紹$urlRouterProvider的.when()方法時,說到.when()的第一個引數what格式可以是字串,函式,urlMatcher物件.那麼什麼是urlMatcher物件呢,也就是通過$urlMatcherFactory定義的url規則.
使用$urlMatcherFactory.compile('path')傳入一個路由規則.path的定義規則和定義狀態時的url定義的規則是一致的:
如果要使用$urlMatcherFactory,需要鏈入ui.router.util依賴.如果不引入依賴,可以使用ui.router自帶的$urlMatcherFactoryProvider
看如下栗子:
urlRouting.config(function($locationProvider,$stateProvider,$urlRouterProvider,$urlMatcherFactoryProvider){ var urlMatcher = $urlMatcherFactoryProvider.compile('/home/:id?param1'); $stateProvider.state('mystate',{ url:urlMatcher, templateProvider:function($stateParams){ return '<h1>mystate'+ $stateParams.id +'</h1>' } }); /*注意如果一個urlMatcher既匹配了狀態裡的url,又匹配了.when,以狀態為準,不會重定向*/ $urlRouterProvider.when(urlMatcher,'hello/'); });
可以看到,.compile裡傳入了一個路由規則,這個規則可以直接作為狀態的url屬性值.也可以作為$urlRouterProvider.when()的第一個引數.
參考原文: https://github.com/angular-ui/ui-router/wiki/URL-Routing