深刻 AngularUI Router

翻译:深刻 AngularUI Router

原文地址:http://www.ng-newsletter.com/posts/angular-ui-router.htmljavascript

ui-router: https://angular-ui.github.io/ui-router/site/#/api/ui.router html

ui-router 是 AngularUI 库提供的特别有用的一个部分,是一个经过提供状态机机制,而不是简单的 URL 来组织咱们的界面的路由框架。java

这个库提供了针对视图的众多的额外控制,咱们能够建立嵌套的视图,在单个页面使用多个视图,多个视图来控制单个视图,还有更多特性。对于更加精细的控制和更为复杂的应用,ui-router 是很是棒的工具。git

ui-router 从状态着手来管理路由,它将应用视为多个状态的组合,经过状态的切换进行路由。github

  • 一个状态能够对应一个页面地址,经过特定的地址到达应用的特定状态。
  • 经过状态的 controller、template 和 views 来管理特定状态的 UI 和行为
  • 经过嵌套视图来解决页面中重复出现的内容。

 

Installation

安装 ui-router,既能够直接下载发布版本,也能够经过 bower 来获取。正则表达式

$ bower install angular-ui-router --save

 

而后,须要在页面中进入这个库,固然要先加载 angular 库。api

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

 

在 angular 中注入 ui.router.数组

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

 

不像 angular 内置的 ngRoute, ui-router 能够嵌套视图,它是基于状态,而不 URL 的。promise

也不像 ngRoute 使用 ng-view 指令,在 ui-router 中,咱们使用 ui-view 指令。安全

当在 ui-router 中考虑路由和状态的关系时,咱们主要关注应用的什么状态对应应用的什么路由。

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

 

相似 ngRoute, 对于给定的状态,模板中的内容将会填充到 <div ui-view></div> 元素,每一个模板还能够包含本身的 ui-view ,这就是咱们能够支持嵌套路径的缘由。

定义路径的时候,咱们使用 .config 方法,像一般同样,可是使用 $stateProvider 来替换 $routeProvider。

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

 

这样,咱们;定义了名为 start 的状态,传递的对象定义了状态配置信息,或者称为 stateConfig,相似于路由配置对象,咱们经过它配置状态信息。

template, templateUrl, templateProvider

可使用下面三种之一的方式来定义视图使用的模板:

  • template, 字符串方式的模板内容,或者是一个返回 HTML 的函数
  • templateUrl, 模板的路径,或者返回模板路径的函数
  • templateProvider, 返回 HTML 内容的函数

例如

$stateProvider.state('home', {
  template: '<h1>Hello {{ name }}</h1>' });

 

 

Controller

相似于 ngRoute,咱们既能够经过控制器的名字来关联一个预约义的控制器,也能够直接建立一个控制器函数来处理。

若是没有对应的模板定义,控制器对象就不会被建立。

Resolve

使用 resolve 功能,咱们能够准备一组用来注入到控制器中的依赖对象。在 ngRoute 中,resolve 能够在路由实际渲染以前解决掉 promise

resolve 选项提供一个对象,对象中的 key 就是准备注入 controller 的依赖名称,值则是建立对象的工厂。

若是是一个串,就试图用这个串来匹配当前已经注册的服务名称,若是是一个函数,执行这个函数,返回的值就是依赖。若是函数返回一个 promise,在控制器被实例化以前,将会被 resolved,返回的值被注入到 controller 中。

复制代码
$stateProvider.state('home', {
  resolve: {
    // This will return immediately as the // result is not a promise person: function() { return { name: "Ari", email: "ari@fullstack.io" } }, // This function returns a promise, therefore // it will be resolved before the controller // is instantiated currentDetails: function($http) { return $http({ method: 'JSONP', url: '/current_details' }); }, // We can use the resulting promise in another // resolution 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; } })
复制代码

 

 

URL

url 用来设置应用对应的一个特定状态. 也就是说,咱们能够经过 url 来到达某个特定的状态,因此这里的 url 不是简单的 url 地址,而是某个可到达状态的标志。

这个特性相似于 ngRoute 中的 URL, 可是,能够被看做一个重大的升级,后面咱们就会看到。

简单的路由相似下面所示。

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

 

当咱们导航到 /index 的时候,应用将会过渡到 inbox 状态,使用这里提供的内容模板填充 ui-view 的内容。

URL 中能够包含多种内容,使人难以置信的强大,能够像在 ngRoute 中设置简单的参数。

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

这里,咱们建立了 :inboxId 参数来捕获 url 中的第二部分,例如,若是应用访问 /inbox/1,那么,$stateParameter.inboxId 就成为 1, 实际上, $stateParams 的值将为 { inboxId: 1 }

也可使用另一种语法。

url: '/inbox/{inboxId}'

路径必须彻底匹配,不像 ngRoute, 若是用户访问 /inbox/,这个路径配置将会工做,可是,若是访问 /inbox,这个状态就不会被激活。

还可使用正则表达式来表示参数,这样能够经过正则表达式来设置匹配规则,例如。

复制代码
// Match only inbox ids that contain // 6 hexidecimal digits url: '/inbox/{inboxId:[0-9a-fA-F]{6}}', // Or // match every url at the end of `/inbox` // to `inboxId` (a catch-all) url: '/inbox/{inboxId:.*}'
复制代码

 

注意,不能在路由中使用捕获组

甚至能够在路径中使用查询参数。

// will match a route such as // /inbox?sort=ascending url: '/inbox?sort'

 绝对路由

若是你使用绝对 url 方式,须要在 url 字符串的开发加上特殊字符 ^

复制代码
$stateProvider
  .state('contacts', { url: '/contacts', ... }) .state('contacts.list', { url: '^/list', ... });
复制代码
  • 'contacts'状态将匹配"/contacts"
  • 'contacts.list'状态将匹配"/list"。子状态的url没有附在父状态的url以后的,由于使用了^

 

嵌套路由

咱们可使用 url 参数添加到路由中来实现嵌套路由。这样能够提供多个 ui-views 在咱们的页面中,例如,咱们能够在 /inbox 之上,提供嵌套的独立路由。这里使用了子状态。

复制代码
$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>' });
复制代码

 第一个路由与前面同样,如今还有第二个路由,一个匹配 inbox 之下的子路由,语法 (.) 表示这是一个子路由。

/inbox/1 匹配第一个状态,/inbox/1/priority 则匹配第二个状态。使用这种语法,咱们能够在父路由中支持嵌套的 url。在父视图中的 ui-view 指令将会处理 priority。

Params

params 选项是参数名称或者正则的数组。它不能合并 url 选项,当状态激活的时候,应用会使用这些参数填充 $stateParams 服务。

Views

咱们能够在 state 中提供命名的视图。这是强大的特性,在单个视图中,咱们能够定义多个视图,甚至使用单个模板。

若是咱们使用了 views 参数,那么,templateUrl, template 和 templateProvider 就会忽略。若是咱们但愿包含父模板,咱们须要建立一个抽象模板。

假如咱们有以下模板。

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

 主要注意的是,顶级的状态天然对应母版页中的视图,通常这个视图是 noname 的,因此,须要一个 noname 的视图来匹配这个 placeholder。其它的视图须要匹配父状态中的视图 placeholder,这些 placeholder 能够是命名的,也能够是 naname的,天然,noname 的只能有一个,不然没法进行区分,咱们在 provider 中进行配置的时候,就须要描述清楚这些 view 和 placeholder 之间的对应关系。

使用 @ 能够定义绝对命名的视图名称,@ 的前面是 placeholder 的名称,后面是状态的名称。@ 前面为空表示未命名的 ui-view,@ 后面为空表示相对于根模板,一般是 index.html

咱们能够建立命名的视图,而后填充对应的模板。每一个子视图均可以有特有的模板,控制器和数据。

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

在这个例子中,咱们有两个命名的视图嵌套在抽象视图中。

Abstract

咱们永远不能直接激活抽象模板,可是,能够经过派生模板来激活。

抽象模板提供封装命名视图的模板,能够传递 $scope 对象给派生子模板。能够经过它解决依赖问题,或者特定数据处理,或者简单地一样的 url 来嵌套多个路由,例如,全部路由都在 /admin 下面。

复制代码
$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>' });
复制代码

onEnter, onExit

在应用进入或者退出视图的时候,会调用这些回调函数。它们均可以设置回调函数;函数能够访问获取的数据。

这些回调函数能够提供咱们一些能力,在访问新视图,或者改变当前状态的时候。这里是很好的执行 "Are you sure?" 对话框,或者请求用户在进入以前登录的地方。

两个函数都不提供参数,须要的信息须要本身提供。

Data

咱们能够附加任意的数到咱们的状态配置对象 configObject 上,data 属性相似于 resolve 属性,除了不会注入到控制器,也不会 resolve promise。

当须要从父状态向子状态传递数据的时候,附加数据是方便的途径。

Evnets

相似 ngRoute 服务,angular-route 服务在状态生命周期的不一样时间点会触发多种事件。咱们能够经过在 $scope 中监听来处理这些事件。

全部的下面的事件都会在 $rootScope 中触发,因此,咱们能够在任何 $scope 对象中监听这些事件。

State change events

能够以下监听

$scope.$on('$stateChangeStart', 
function(evt, toState, toParams, fromState, fromParams), { // We can prevent this state from completing  evt.preventDefault(); });

 

$stateChangeStart

当从一个状态开始向另一个状态过分的时候触发。

$stateChangeSuccess

当状态过渡完成以后触发。

$stateChangeError

在状态过渡中出现错误。常见的错误例如,不能获取模板,或者 promise 不能成功 resolve 等等

视图加载事件

ui-router 也提供了视图加载阶段的事件。

$viewContentLoading

视图开始加载,可是,在 DOM 渲染以前。

能够以下监听。

$scope.$on('$viewContentLoading', 
function(event, viewConfig){ // Access to all the view config properties. // and one special property 'targetView' // viewConfig.targetView });

 

 

$viewContentLoad

视图已经加载,渲染完成。

$stateParams

在前面的内容中,咱们使用 $stateParams 来从 url 参数中获取 params ,这个服务,与 url 不一样。

例如,若是咱们 inbox 状态的 url 以下。

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

 

用户使用下面的 url 访问

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

 

咱们的 $stateParams 对象将获取以下数据。

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

 

 

$urlRouterProvider

相似 ngRoute, 能够建立当特定的 url 访问时处理的规则。

能够经过不一样的 url 激活不一样的状态,因此在管理激活和加载状态的时候, $urlRouterProvider 并非必须的。在状态管理以外的时候才会须要,好比重定向,或者验证的时候。

when()

when 函数须要两个参数,咱们但愿匹配的路径,另外就是咱们但愿从新定向的目标。也能够是一个函数。

例如,若是但愿任何空的路由到咱们的 /inbox 路由中。

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

 

若是提供一个函数处理,路由匹配的时候,这个函数就会被调用,它能够返回下列三种之一的结果。

  • false,这个回应告诉 $urlRouter 规则并不匹配,应该查找其它匹配的状态,在咱们但愿验证用户是否访问正确地址的时候颇有用。
  • 字符串,$urlRouter 将其做为重定向目标。
  • true 或者 undefined,函数已经处理了这个 url 

otherwise()

与 ngRoute 中的 oterwise() 方法相似,oterwiese() 在没有其它路由匹配的状况下重定向。这是建立默认 url 的好方法。

otherwise() 函数只须要一个参数,一个字符串或者一个函数。

若是提供了一个字符串,就会被看作一个默认地址,在任何错误的或者不能匹配任何路由的时候,就会被重定向到这个地址。

若是是一个函数,在没有其它路由匹配的时候,就会被执行

复制代码
.config(function($urlRouterProvider) { $urlRouterProvider.otherwise('/'); // or  $urlRouterProvider.otherwise( function($injector, $location) { $location.path('/'); }); });
复制代码

 

 

rule()

若是咱们但愿处理任何路由,或者在其它路由以前进行一些处理,可使用 rule() 函数。

咱们必须返回一个验证的路径串

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

 

 激活状态

有三种方式来激活特定的状态

  • 使用 $state.go() 方法
  • 使用 ui-sref 绑定的链接
  • 直接导航到与状态关联的 url

 

建立报名向导

为何不使用一下它呢?

咱们建立一个报名的向导来演练一下 ui-router 的使用。

使用 ui-router ,咱们建立一个简单的报名服务,使用一个控制器来处理报名。

首先,咱们建立应用的视图。

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

 

在这个视图中,咱们定义了报名视图。下一步,在报名向导中,须要三步

  • start,在这一步,咱们获取用户名称,提供欢迎信息
  • email, 这里,咱们获取用户的 email 信息
  • finish, 这里,用户完成报名,咱们简单地显示完成页面

报名处理依赖 wizardapp.controllers 模块,

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

 

咱们的 wizardSignupController 控制器,使用 $scope.user 对象在整个过程当中收集信息。

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

 

如今,向导处理逻辑处理主要工做,配置 config() 

复制代码
angular.module('wizardApp', [
  'ui.router', 'wizardapp.controllers' ]) .config(['$stateProvider', '$urlRouterProvider', 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' }); }]);
复制代码

 

这样,咱们基本的流程就已经有了。如今,若是用户导航到 /step_1,就会看到开始页面,尽管如今地址是 /step_1, 而咱们但愿是 /wizard/step_1

为了这个效果,咱们建立 abstract 状态来寄宿各个步骤。

复制代码
.config(['$stateProvider', '$urlRouterProvider',
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' }); }]);
复制代码

 

这样,它们都安全地嵌套到 /wizard 之下了。

在状态之间进行导航,咱们使用 ui-router 提供的指令 ui-sref 来生成连接,这个指令用来生成导航连接。

例如,step_1.html 以下。

<!-- step_1.html --> <h3>Step 1</h3> <form ng-submit=""> <input type="text" ng-model="user.name" placeholder="Your name" /> <input type="submit" class="button" value="Next" ui-sref="wizard.email"/> </form>

 

咱们还但愿在报名流程完成以后,执行特定的动做,来调用定义在父控制器上的 signup 函数,咱们能够在最后的步骤中添加一个控制器来调用 $scope.signup() 函数。因为整个向导封装在 WizardSignupControoler 中,咱们能够像一般同样访问嵌套的 scope 对象。

复制代码
.state('wizard.finish', {
  url: '/finish', templateUrl: 'partials/wizard/step_3.html', controller: function($scope) { $scope.signup(); } });
复制代码

 

 

总结

在这里,咱们深刻讨论了 ui-router 几乎所有的特性,咱们发现这个库很是有用,但愿也能帮到你。

相关文章
相关标签/搜索