ui-router使用

去年的时候,在公司做过一个ui-router的分享,今天将它整理成文字,发出来。后面,也会陆陆续续将以前的技术分享发出来。

开篇之前,需要提前说明的是,此分享使用的Angular是1.x版本的。

在Angular中,ngRoute是Angular自带的路由模块,它是基于url来驱动视图
ui-router是AngularUI独立的路由模块,它是基于状态来驱动视图。相比于ngRoute,ui-router具有更强大的功能,主要体现在视图的嵌套方面。

安装

使用npm安装

npm install --save angular-ui-router

简单使用

在模板中定义添加标签,添加ui-sref跳转链接,然后在js中配置好路由(注册状态)。

index.html的代码如下:

                    HelloHome

helloWord.js中的代码如下:

//让app依赖ui.router
var myApp = angular.module('myApp', ['ui.router']);

myApp.config(function ($stateProvider, $urlRouterProvider) {    var helloState = {        name: 'hello',        url: '/hello',        template: '

{{title}}

', controller: function ($scope) { $scope.title = "Hello world"; } }; var aboutState = { name: 'about', url: '/about', templateUrl: 'about.html' }; $stateProvider.state(helloState); $stateProvider.state(aboutState);});

上面,helloWord.js中注册路由时,使用nameurltemplate等状态属性。那么,ui-router中有哪些状态属性呢?

  • name: 状态的名字,例如hello
  • url: 在浏览器里面的url部分,例如当hello状态激活时,浏览器里面将会变成/hello
  • template: 模板,状态的视图
  • templateUrl: 模板的url
  • controller:控制器,它可以管理模板中的内容

在上面的index.html代码中,我们使用ui-viewui-srefui-sref-active三个指令,这也是我们经常使用指令,它们代表意思是:

  • ui-view: 某个状态激活时,会被加载到这个标签内部
  • ui-sref:相当于href,是一个链接,点击会激活相应的状态
  • ui-sref-active:当对应状态激活时,这个指令会给所在元素添加class
注册状态另外一种写

除了上面helloWorld.js中的写法外,还有另外一种,这也是我常用的写法:

myApp.config(['$stateProvider', '$urlRouterProvider', function ($stateProvider, $urlRouterProvider) {    $stateProvider    //对应前面的helloState        .state("hello", {            url: '/hello',            template: '

{{title}}

', controller: function ($scope) { $scope.title = "Hello world"; } }) //对应前面的aboutState .state("about", { url: '/about', templateUrl: 'about.html' });}]);

路由嵌套

路由嵌套包括了状态嵌套和视图嵌套,状态嵌套就是在注册状态时,建立状态树(父子状态关系);视图嵌套,就是在父状态的模板中需要有一个ui-view标签,它用来加载子状态的模板内容。

状态嵌套和视图嵌套是一起使用的,共同构成路由嵌套。如果没有建立状态的父子关系,跳转会报错;如果没有视图的嵌套关系,跳转时url变化,但是页面的内容不会变化,因为子状态的模板视图不知道加载到哪里。

嵌套实现的四种方式

  • 使用.来进行嵌套,例如.state('home.list', {})
  • 使用ui-router.stateHelper去构建嵌套,是第三方实现的,请参考ui-router.stateHelper
  • 使用parent属性,来指定父状态的名称,例如:parent: 'home'
  • 使用parent属性,来指定父状态对象,例如:parent: home
"."嵌套

我最常使用的嵌套方式是使用"."来嵌套,示例如下:

$stateProvider    .state("home", {        url: '/home',        templateUrl: 'home.html'    })    .state("home.list", {        url: '/list',        templateUrl: 'list.html',        controller: function ($scope) {            $scope.dataArray = ['first cell', 'second cell', 'third cell']        }    })

注意: 上面的home.list对应路径是#/home/list,它是home状态的url与子状态home.list的url一起拼接起来的。

使用parent来嵌套

这个,我比较少使用,示例如下:

$stateProvider    .state("home", {        url: '/home',        templateUrl: 'home.html'    })    .state("detail", {        url: '/detail',        parent: 'home',        template: '

这是详情部分

' })

路由嵌套注意的地方

  • 状态注册可以是任何顺序,子状态在父状态之前注册也没可以
  • 父状态必须存在
  • 当子状态激活时,它的父状态也是激活状态,例如home.list激活时,home也会激活
  • 嵌套状态和视图时,子状态只会加载自己的模板到父状态的ui-view中。如果父状态模板中不存在ui-view时,激活子状态,url会发生变化,但是视图不会发生变化,因为没有加载子状态模板的ui-view。

路由嵌套时URL组成

有两种方式组成URL:拼接路径绝对路径

第一种:拼接路径(默认)

当前状态是父状态的url与当前状态的url拼接起来的,例如:

$stateProvider    .state("home", {        url: '/home',        templateUrl: 'home.html'    })    .state("home.list", {        url: '/list',        templateUrl: 'list.html',        controller: function ($scope) {            $scope.dataArray = ['first cell', 'second cell', 'third cell']        }    })
第二种: 绝对路径(^)

使用^符号匹配绝对路径,例如:

$stateProvider    .state('contacts', {        url: '/contacts',        ...}).state('contacts.list', {    url: '^/list',    ...});
  • contacts 状态匹配 /contacts
  • contacts.list 状态匹配 /list

路由跳转

当我们使用路由时,还需要知道路由如何跳转的,而在ui-router中,跳转路由有下面几种方式:

第一种,通过$stage.go()来跳转

//跳转到home状态
$sate.go('home');

第二种,通过设置一个含有ui-sref指令的链接

Home

这种方式,点击Home就会跳转到home状态

第三种,通过url直接导航到对应页面

Hello

或者

window.location = '#/home';

注意: 这里是使用angular 1.5.8,如果是angular1.6以上的,有!符号,如window.location = '#!/home'。

跳转并刷新

除了简单跳转之外,有时,我们想跳转到某个状态下,并且刷新页面。这时,我们可以使用ui-srefui-sref-opts一起来实现,如下:

List

当然,我们也可以在js中,使用$stage.go()来实现,如下:

$state.go("home.list",{}, {reload: true}); 

相对跳转

实现相对跳转很简单,使用.开头就行了。

在html中使用ui-sref方式时,示例如下:

List

在js中,使用$stage.go()也可以实现,示例如下:

$state.go(".list"); 

Resolve

Resolve是一个可选的依赖,当设置它时,它内部的属性就会被注入到控制器中。它的用途是,为状态控制器提供定制化的数据。

示例如下:

.state("detail", {    url: '/detail',    parent: 'home',    template: '

这是详情部分

', resolve: { //resolve是一个 config: function () { return { name: 'homeConfig', content: 'Hell' } }, greeting: function($q, $timeout){ var deferred = $q.defer(); $timeout(function() { deferred.resolve('Hello!'); }, 2000); return deferred.promise; }, page: function ($http) { return $http.get('http://localhost'); } }, controller: function ($scope, config, greeting, page) { console.log(config); console.log(greeting); console.log(page); }})

注意:当Resolve中存在promise时,它会等待每个promise relove之后才实例化控制器,然后触发$stateChangeSuccess事件。还有,Resolve中的数据,在子状态也可以获取。

参数

路由传递参数有两种,一种将参数嵌入到url当中;另外一种,没有嵌入到url当中。

参数嵌入方式

第一种:参数嵌入在URL中

嵌入在URL中的参数也分成两种,一种是嵌入在URL路径中;另外一种是放在query部分(也就是?后面)

嵌入在URL路径中时

示例如下:

.state("about.detail", {    url: '/detail/:name',    template: '

这是about的详情页面, 关于{{name}}

', controller: function ($scope, $stateParams) { $scope.name = $stateParams.name; }})

上面代码也可以使用括号,效果完全一样,如下:

.state("about.detail", {    url: ‘/detail/{name}’,    template: '

这是about的详情页面, 关于{{name}}

', controller: function ($scope, $stateParams) { $scope.name = $stateParams.name; }})

注意:放在URL路径里面时,还可以使用正则,例如: url:'/detail/*'

参数放在query部分

在?后面指定参数,例如:url: "/contacts?myParam",它将匹配: "/contacts?myParam=value"
多个参数时,用'&'连接:url: "/contacts?myParam1&myParam2",它将匹配: "contacts?myParams1=value1&myParam2=value2"

第二种:参数不嵌入URL中

注册路由时,设置状态属性params

.state("home.test", {    url: '/test',    params: {        name: null    },    template: '

这是测试页面

', controller: function ($scope, $stateParams) { console.log($stateParams); }})

传递参数

$state.go('about.detail', {name: 'default about'});

获取参数

.state("about.detail", {    url: '/detail/:name',    template: '

这是about的详情页面, 关于{{name}}

', controller: function ($scope, $stateParams) { $scope.name = $stateParams.name; }});

$urlRouterProvider使用

$urlRouterProvider可以用来做重定向处理无效状态定制化url

我们经常使用的功能是重定向和处理无效请求,它分别通过when()otherwise()来实现,而定制化url,则是通过rule()方法实现。

重定向

重定向使用的是when(),它可以传递字符串:

例如,我们项目中,当跳转到project状态时,会默认重定向到"所有项目",如下:

$urlRouterProvider.when("/project", "/project/list/所有");

它也可以传递参数:

例如,我们项目中,曾经就有重定向的情况:

$urlRouterProvider.when('/project/detail/:id/evaluate', ['$match', '$stateParams', function ($match, $stateParams) {    return '/project/detail/' + $urlRouterProvider.$stateParams.id + '/evaluate/analyst'}]);

无效状态处理

无效状态处理,使用的是otherwise(),它传递一个url或函数参数。

例如,在我们的一个项目中,跳转到无效的路由时,它默认会跳转到个人信息页面(”/account”)

$urlRouterProvider.otherwise("/account");

url定制化

url定制化,使用的是rule(),它需要传递一个函数来处理url。

例如,将所有url都转换成小写:

app.config(function ($urlRouterProvider) {    $urlRouterProvider.rule(function ($injector, $location) {        var path = $location.path(), normalized = path.toLowerCase();        if (path != normalized) {            $location.replace().path(normalized);        }    });})

onEnter和onExit

状态还有两个可选的属性,那就是onEnter和onExit回调函数,当状态激活时,它会回调onEnter中的方法(在controller初始化之前);当状态从激活变成非激活时,它会回调onExit方法。

示例:

onEnter: function () {    console.log('进入home页面');},onExit: function () {    console.log('退出home页面')}

注意:这个回调函数,可以访问所有Resolved中的依赖,也就是说它也会等待所有Resolved中的promise处理完之后才执行。

状态变化事件

注意:状态变化事件在1.0之后被放弃了,被 transition hooks代替了。

  • $stateChangeStart 状态变化开始
  • $stateNotFound 未被发现
  • $stateChangeSuccess 状态改变成功
  • $stateChangeError 状态改变失败

使用,如下:

$rootScope.$on("$stateChangeSuccess", function () {   console.log('状态改变成功');});

使用transition hooks代替时,如下:

myApp.run(function($rootScope) {    $rootScope.$on('$stateChangeStart', function(evt, toState, toParams, fromState, fromParams) {        console.log("$stateChangeStart " + fromState.name + JSON.stringify(fromParams) + " -> " + toState.name + JSON.stringify(toParams));    });    $rootScope.$on('$stateChangeSuccess', function() {        console.log("$stateChangeSuccess " + fromState.name + JSON.stringify(fromParams) + " -> " + toState.name + JSON.stringify(toParams));    });    $rootScope.$on('$stateChangeError', function() {        console.log("$stateChangeError " + fromState.name + JSON.stringify(fromParams) + " -> " + toState.name + JSON.stringify(toParams));    });});

视图加载事件

  • $viewContentLoading 当视图开始加载时触发一次,在DOM渲染之前
  • $viewContentLoaded 当视图加载完触发一次,在DOM渲染之后

示例如下:

$scope.$on("$viewContentLoading", function (event, viewConfig) {});

$scope.$on("$viewContentLoaded", function (event, viewConfig) {});

多个命名视图

一个模板中,还可以放多个命名的视图,如下:



  

多个视图可以显示各自的模板:

$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 ... }
      }
    }
  })

你可能感兴趣的:(ui-router使用)