Angular學(xué)習(xí)筆記(17)—ui-router

安裝

$ bower install angular-ui-router --save

<script type="text/javascript"
 src="app/bower_components/angular-ui-router/release/angular-ui-router.js"></script>

angular.module('myApp', ['ui.router']);

現(xiàn)在,不同于內(nèi)置的ngRoute服務(wù),由于ui-router基于狀態(tài)工作,而不是簡單的url,因此可以將它嵌套在視圖中。
在處理ngRoute服務(wù)時(shí)我們不再使用ng-view,而改為使用ui-view指令。
ui-router內(nèi)處理路由和狀態(tài)時(shí),我們主要關(guān)心的是應(yīng)用程序處在哪個(gè)狀態(tài)以及Web應(yīng)用當(dāng)前處在哪個(gè)路由位置。

<div ng-controller="DemoController"> 
    <div ui-view></div>
</div>

定義在任意給定狀態(tài)內(nèi)的模板都處在<div ui-view></div>元素內(nèi)。此外,每個(gè)模板都可以包含自己的ui-view。 這事實(shí)上就允許你在路由中嵌套視圖。定義路由:

.config(function($stateProvider,$urlRouterProvider) {
    $stateProvider.state('start', {
            url: '/start',
            templateUrl: 'partials/start.html'
        })
});

這一步給狀態(tài)配置對象分配了一個(gè)名為start的狀態(tài)。這個(gè)狀態(tài)配置對象的參數(shù)如下:

1.template、templateUrl、templateProvider

在每個(gè)視圖上設(shè)置模板的方式有三種。

  • template:一個(gè)HTML內(nèi)容字符串或者返回HTML的函數(shù)
  • templateUrl:一個(gè)模板URL路徑字符串或者是返回URL路徑字符串的函數(shù)
  • templateProvider:一個(gè)返回HTML內(nèi)容字符串的函數(shù)

2.controller

可以給已經(jīng)注冊好的控制器關(guān)聯(lián)一個(gè)URL(使用字符串),也可以創(chuàng)建一個(gè)控制器函數(shù)作為狀態(tài)控制器。如果沒有定義模板,就不會(huì)創(chuàng)建這個(gè)控制器。

3.resolve

我們還可以使用resolve功能解析要注入到控制器中的依賴列表。這個(gè)resolve選項(xiàng)就是一個(gè)對象,其中鍵就是要注入到控制器中的依賴名稱,而其值就是待解析的factories。
如果傳入一個(gè)字符串,angular-route會(huì)嘗試匹配一個(gè)現(xiàn)有的已注冊的服務(wù)。如果傳入一個(gè)函數(shù),則注入這個(gè)函數(shù),而函數(shù)的返回值就是依賴。如果這個(gè)函數(shù)返回一個(gè)promise,它會(huì)在控制器被實(shí)例化之前解析,同時(shí)其值(就像ngRoute)會(huì)注入到控制器中。

$stateProvider.state('home', {
    resolve: {
        // 當(dāng)結(jié)果不是promise時(shí)立即返回
        person: function() {
            return {
                name: "Ari",
                email: "ari@fullstack.io"
            }
        },
        // 這個(gè)函數(shù)返回一個(gè)promise,它會(huì)在控制器實(shí)例化之前解析
        currentDetails: function($http) {
            return $http({
                method: 'JSONP',
                url: '/current_details'
            });
        },
        // 還可以在另一個(gè)解析中使用上面返回的promise
        facebookId: function($http, currentDetails) {
            $http({
                method: 'GET',
                url: 'http://facebook.com/api/current_user',
                params: {
                    email: currentDetails.data.emails[0]
                }
            });
        }
    },
    controller: function($scope. person, currentDetails, facebookId) {
        $scope.person = person;
    }
});

4.url

url選項(xiàng)可以給應(yīng)用程序的狀態(tài)分配一個(gè)唯一的URL。這個(gè)url選項(xiàng)提供了與深度鏈接同樣的功能,它通過狀態(tài)導(dǎo)航應(yīng)用,而不是簡單地通過URL導(dǎo)航。
基本路由可以像這樣指定:

$stateProvider
    .state('inbox', {
        url: '/inbox',
        template: '<h1>Welcome to your inbox</h1>'
    });

當(dāng)用戶導(dǎo)航到/inbox時(shí),應(yīng)用會(huì)轉(zhuǎn)換到inbox狀態(tài),然后使用模板內(nèi)容填充主要的ui-view指令。
URL可以接受一系列不同的選項(xiàng),它還可以在url中設(shè)置基本的參數(shù)。

$stateProvider
    .state('inbox', {
        url: '/inbox/:inboxId',
        template: '<h1>Welcome to your inbox</h1>',
        controller: function($scope, $stateParams) {
            $scope.inboxId = $stateParams.inboxId;
        }
    });

應(yīng)用會(huì)捕獲作為URL第二個(gè)組成部分的:inboxId。例如,如果用戶轉(zhuǎn)換到/inbox/1,$stateParams.inboxId就會(huì)變成1(因?yàn)?code>$stateParams為{inboxId: 1})。
還可以使用不同的語法:

url: '/inbox/{inboxId}'

這里路徑必須與URL精確匹配。和ngRoute不同,如果用戶導(dǎo)航到/inbox/,上面的路徑能夠正常工作。但是,當(dāng)導(dǎo)航導(dǎo)到/inbox時(shí),上述示例配置中的狀態(tài)不會(huì)被激活。
此外,你還可以在路徑參數(shù)內(nèi)使用正則表達(dá)式,因此你可以設(shè)置一個(gè)匹配路由的規(guī)則。

// 只匹配包含6個(gè)十六進(jìn)制數(shù)字的inbox ID
url: '/inbox/{inboxId: [0-9a-fA-f]{6}}',
// 或者匹配每個(gè)URL中`/inbox`后面的`inboxId`(全部捕獲)
url: '/inbox/{inboxId:.*} '

注意,不能在路由內(nèi)使用正則捕獲組,因?yàn)槁酚山馕銎鲗o法解析這個(gè)路由。
甚至還可以在路由中指定查詢參數(shù):

// 匹配諸如/inbox?sort=ascending形式的路由
url: '/inbox?sort'

5.嵌套路由

你可以使用url參數(shù)以插入路由的方式提供嵌套路由。這讓你可以在頁面或者模板內(nèi)有多個(gè)ui-views。

$stateProvider.state('inbox', {
    url: '/inbox/:inboxId',
    template: '<div><h1>Welcome to your inbox</h1>\
        <a ui-sref="inbox.priority">Show priority</a>\
        <div ui-view></div></div>'
    controller: function($scope, $stateParams) {
        $scope.inboxId = $stateParams.inboxId;
    }
})
.state('inbox.priority', {
    url: '/priority',
    template: '<h2>Your priority inbox</h2>'
});

第一個(gè)路由會(huì)按預(yù)期匹配。現(xiàn)在這里有了第二個(gè)路由,也就是一個(gè)匹配父路由inbox之下的子路由。

  • /inbox/1匹配第一個(gè)狀態(tài)。
  • /inbox/1/priority匹配第二個(gè)狀態(tài)。

使用這種語法,你可以在父路由內(nèi)嵌套URL。父視圖中的ui-view會(huì)解析priority收件箱。

6.params

params選項(xiàng)是一個(gè)參數(shù)名數(shù)組或者是一個(gè)正則表達(dá)式數(shù)組。不能將這個(gè)選項(xiàng)與url選項(xiàng)聯(lián)合使用。當(dāng)狀態(tài)被激活時(shí),這些參數(shù)會(huì)被填充到$stateParams服務(wù)中。

7.views

ui-router的一個(gè)強(qiáng)大的特性就是可以在一個(gè)狀態(tài)內(nèi)設(shè)置多個(gè)命名視圖。在獨(dú)立的視圖內(nèi),你可以在獨(dú)立模板中定義多個(gè)要引用的視圖。
如果設(shè)置了views參數(shù),那么狀態(tài)的templateUrl、templatetemplateProvider就會(huì)被忽略。如果你想在路由中包含父模板,就需要?jiǎng)?chuàng)建一個(gè)包含模板的抽象狀態(tài)。
比方說我們有一個(gè)視圖看起來像這樣:

<div>
    <div ui-view="filters"></div>
    <div ui-view="mailbox"></div>
    <div ui-view="priority"></div>
</div>

現(xiàn)在,你可以創(chuàng)建命名視圖來填充每個(gè)獨(dú)立的模板。每個(gè)子視圖都可以包含它自己的模板、控制器和使用resolve關(guān)鍵字解析的數(shù)據(jù)。

$stateProvider.state('inbox', {
    views: {
        'filters': {
            template: '<h4>Filter inbox</h4>',
            controller: function($scope) {}
        },
        'mailbox': {template: 'partials/mailbox.html'},
        'priority': {
            template: '<h4>Priority inbox</h4>',
            resolve: {
                facebook: function() {return FB.messages(); }
            }
        }
    }
});

8.abstract

抽象模板永遠(yuǎn)不能直接激活,但是可以設(shè)置被激活的子節(jié)點(diǎn)。
你可以使用抽象模板提供一個(gè)模板包裝器來包裹多個(gè)命名視圖,或者傳遞$scope對象給子節(jié)點(diǎn)。你還可以使用它們來傳遞解析后的依賴或者自定義數(shù)據(jù),或者在同一url下嵌套多個(gè)路由(比如,所有的路由都在/adminURL之下)。
設(shè)置抽象模板與設(shè)置常規(guī)狀態(tài)一樣,區(qū)別只在于設(shè)置abstract屬性:

$stateProvider
  .state('admin', {
      abstract: true,
      url: '/admin',
      template: ;<div ui-view></div>' 
  })
  .state('admin.index', {
      url: '/index',
      template: '<h3>Admin index</h3>' 
  })
  .state('admin.users', {
      url: '/users',
      template: '<ul>...</ul>'
  });

9.onEnter、onExit

Angular會(huì)在用戶(分別)進(jìn)入或者離開視圖時(shí)調(diào)用這些回調(diào)函數(shù)。對于這兩個(gè)選項(xiàng),你可以設(shè)置希望調(diào)用的函數(shù)。這些函數(shù)可以訪問被解析的數(shù)據(jù)。
這些回調(diào)函數(shù)讓你可以在新視圖上或者進(jìn)入另一個(gè)狀態(tài)時(shí)觸發(fā)某個(gè)行為。使用它們可以很好地實(shí)現(xiàn)一個(gè)“你確定嗎?”形式的模態(tài)視圖,或者在用戶進(jìn)入這個(gè)狀態(tài)之前要求用戶登錄。

10.data

你可以附加任意數(shù)據(jù)給你的狀態(tài)配置對象configObject。這個(gè)選項(xiàng)跟resolve屬性很像,但是它的數(shù)據(jù)不會(huì)被注入到控制器中,promise也不會(huì)被解析。
當(dāng)需要從父狀態(tài)給子狀態(tài)傳遞數(shù)據(jù)時(shí),這個(gè)選項(xiàng)特別有用。

事件

angular-route服務(wù)會(huì)在狀態(tài)生命周期的不同階段觸發(fā)不同的事件。
在應(yīng)用程序內(nèi)可以通過監(jiān)聽$scope對象的方式附加函數(shù)給這些事件。以下所有事件都會(huì)觸發(fā)在$ootScope上,因此可以在任意$scope對象上監(jiān)聽這些事件。

1.狀態(tài)改變事件

可以使用如下方式監(jiān)聽這個(gè)事件:

$scope.$on('$stateChangeStart',
  function(evt, toState, roParams, fromState, fromParams) { 
      // 可以阻止這一狀態(tài)完成
      evt.preventDefault();
  });

這個(gè)事件可能會(huì)以如下方式觸發(fā)。
$stateChangeStart從一個(gè)狀態(tài)開始過渡到另一個(gè)狀態(tài)時(shí)觸發(fā)這個(gè)事件。 $stateChangeSuccess從一個(gè)狀態(tài)過渡到下一個(gè)狀態(tài)完成時(shí)觸發(fā)這個(gè)事件。 $stateChangeError當(dāng)過渡期間發(fā)生錯(cuò)誤時(shí)觸發(fā)這個(gè)事件。通常,模板不能被解析或者解析promise失敗時(shí)會(huì)引發(fā)錯(cuò)誤。

2.視圖加載事件

ui-router還在視圖加載階段提供了事件。
$viewContentLoading視圖開始加載時(shí),DOM被渲染之前,觸發(fā)這個(gè)事件。
你可以像這樣監(jiān)聽這個(gè)事件:

$scope.$on('$viewContentLoading', 
  function(event, viewConfig) {
      // 在這里可以訪問所有視圖配置屬性 
      // 以及一個(gè)特殊的“targetView”屬性 
      // viewConfig.targetView
  });

$viewContentLoaded在視圖加載完成以及DOM渲染之后觸發(fā)這個(gè)事件。

$stateParams

$stateParams服務(wù)展示了如何根據(jù)URL的不同組成部分處理數(shù)據(jù)。
例如,如果在inbox狀態(tài)中有個(gè)看起來像這樣的URL:

url: 'inbox/:inboxId/messages/{sorted}}?from&to' 

然后用戶到達(dá)這個(gè)路由:

/inbox/123/messages/ascending?from=10&to=20 

那么$stateParams對象的結(jié)果就是:

{inboxId: '123', sorted: 'ascending', from: 10, to: 20}

$urlRouterProvider

你可以使用路由提供程序構(gòu)建規(guī)則,規(guī)定當(dāng)特定的URL被激活時(shí)會(huì)發(fā)生什么。 創(chuàng)建的這些狀態(tài)負(fù)責(zé)在不同的URL中激活自身,因此不一定需要$urlRouterProvider來管理激活和加載狀態(tài)。當(dāng)你想要管理發(fā)生在狀態(tài)作用域之外的行為時(shí),它就可以派上用場了,比如重定向或者身份驗(yàn)證。
你可以在模塊配置函數(shù)中使用$urlRouterProvider。

when()

when()接受兩個(gè)參數(shù):想要匹配的入口路徑和用于重定向的路徑(或者是在路徑匹配時(shí)調(diào)用的函數(shù))。
為了設(shè)置重定向,需要給when方法設(shè)置一個(gè)字符串參數(shù)。
例如,如果想將一個(gè)空路由重定向到/inbox:

.config(function($urlRouterProvider) { 
    $urlRouterProvider.when('', '/inbox'); 
});

如果傳入一個(gè)函數(shù),它會(huì)在路徑匹配時(shí)調(diào)用。這個(gè)處理程序可能返回以下三個(gè)值中的一個(gè)。

  • falsy:這個(gè)值告訴$urlRouter該規(guī)則不匹配,同時(shí)它應(yīng)該嘗試找到一個(gè)不同的狀態(tài)來匹配。如果想要確保用戶可以正確地訪問一個(gè)URL,它將很有幫助。
  • 字符串:$urlRouter會(huì)把這個(gè)字符串值當(dāng)作重定向的URL。
  • truthy or undefined:這個(gè)值讓$urlRouter知道已經(jīng)處理了URL。

otherwise()

otherwise()方法在沒有其他路由匹配時(shí)發(fā)起重定向。這個(gè)方法是創(chuàng)建默認(rèn)URL的一種很好的方式。
otherwise()方法接受一個(gè)參數(shù):一個(gè)字符串或者函數(shù)。
如果傳入一個(gè)字符串,任何無效或者不匹配的路由都會(huì)重定向到字符串指定的URL。
如果傳入一個(gè)函數(shù),它會(huì)在沒有其他路由匹配時(shí)被調(diào)用,同時(shí)負(fù)責(zé)處理返回結(jié)果。

.config(function() {
    $urlRouterProvider.otherwise('/');
    // 或者
    $urlRouterProvider.otherwise(function($injector, $location) { 
        $location.path('/');
    });
});

rule()

如果想要繞過所有的URL匹配,或者想要在操作其他路由之前對路由做一些操作, 可以使用rule()函數(shù)。
使用rule()函數(shù)時(shí)必須返回一個(gè)有效路徑字符串。

.config(function($urlRouterProvider) { 
    $rulRouterProvider.rule(function($injector, $location) { 
        return '/index';
    });
});

創(chuàng)建一個(gè)導(dǎo)航程序

當(dāng)我們想要為用戶創(chuàng)建一個(gè)注冊向?qū)У臅r(shí)候,就需要使用ui-router了,這是一個(gè)非常合適的應(yīng)用場景。
我們將使用ui-router創(chuàng)建一個(gè)快速注冊服務(wù),它包含一個(gè)控制器,用于處理注冊任務(wù)。首先,需要?jiǎng)?chuàng)建應(yīng)用視圖:

<div ng-controller="WizardSignupController"> 
    <h2>Signup wizard</h2>
    <div ui-view></div>
</div>

接下來,在這個(gè)注冊向?qū)е羞€需要有三個(gè)階段。

  • start:在這個(gè)階段,我們獲取用戶名并向其介紹注冊向?qū)А?/li>
  • email:在這里,接受用戶的郵件。
  • finish:此時(shí),用戶完成注冊過程,我們要向其展示一個(gè)完整的頁面。

在真實(shí)的應(yīng)用中,finish階段應(yīng)該將注冊資料發(fā)送給服務(wù)器,同時(shí)進(jìn)行真實(shí)的注冊 操作。在這里,由于沒有后端,因此暫時(shí)只顯示這個(gè)視圖。
這個(gè)注冊程序依賴于wizardapp.controllers模塊,我們將在其中編寫包含控制器:WizardSignupController。

angular.module('wizardApp', [ 
    'ui.router', 
    'wizardapp.controllers'
]);

WizardSignupController簡單地提供了$scope.user對象,在注冊過程以及注冊行為中,我 們都會(huì)使用這個(gè)對象。

angular.module('wizardapp.controllers', []) 
  .controller('WizardSignupController', 
      function($scope, $state) { 
        $scope.user = {}; 
        $scope.signup = function() {}
  });

向?qū)С绦蜻壿嫺采w了大部分工作。你可以將這些邏輯設(shè)置到應(yīng)用的config()函數(shù)中:

angular.module('wizardApp', [
    'ui.router', 'wizardapp.controllers'
])
.config(function($stateProvider, $urlRouterProvider) { 
    $stateProvider
        .state('start', {
            url: '/step_1',
            /templateUrl: 'partials/wizard/step_1.html' 
        })
        .state('email', {
            url: '/step_2',
            templateUrl: 'partials/wizard/step_2.html'
        })
        .state('finish', {
            url: '/finish',
            templateUrl: 'partials/wizard/step_3.html' 
        });
});

設(shè)置這些選項(xiàng)之后,基本流程就全部完成了。現(xiàn)在,如果用戶導(dǎo)航到路由/step_1,他們將被定向到流程的起點(diǎn)。盡管整個(gè)流程也可以都發(fā)生在根URL上(即/step_1),但你可能更希望將 它們放在子路由中(如/wizard/step_1)。
為此,只需要設(shè)置一個(gè)包裝其他步驟的abstract狀態(tài)就可以了。

.config(function($stateProvider, $urlRouterProvider) { 
    $stateProvider
        .state('wizard', {
            abstract: true,
            url: '/wizard',
            template: '<div><div ui-view></div></div>' 
        })
        .state('wizard.start', {
            url: '/step_1',
            templateUrl: 'partials/wizard/step_1.html' 
        })
        .state('wizard.email', {
            url: '/step_2',
            templateUrl: 'partials/wizard/step_2.html' 
        })
        .state('wizard.finish', {
            url: '/finish',
            templateUrl: 'partials/wizard/step_3.html' 
        });
});

現(xiàn)在,這些路由不再定義在頂級路由中了,你可以將它們(子路由)安全地嵌套在/wizardURL內(nèi)。
此外,我們還想在注冊程序的尾部附加一個(gè)功能:在父控制器WizardSignupController上調(diào)用signup函數(shù)。我們只需在向?qū)С绦虻奈膊吭O(shè)置一個(gè)控制器來調(diào)用$scope上的函數(shù)就行了。 由于整個(gè)向?qū)С绦蚨挤庋b在WizardSignupController中,這就表示可以正常使用作用域嵌套作用域?qū)傩浴?/p>

.state('wizard.finish',{
    url: '/finish',
    templateUrl: 'partials/wizard/step_3.html', 
    controller: function($scope) { 
        $scope.signup();
    }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容