angular的uiRouter服務學習(4)

詩&遠方發表於2015-01-07

本篇接著上一篇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 

相關文章