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-view></ui-view>标签,添加ui-sref跳转链接,然后在js中配置好路由(注册状态)。

index.html的代码如下:

<!DOCTYPE html><html><head>    <meta charset="utf-8"/>    <script src="../node_modules/angular/angular.min.js"></script>    <script src="../node_modules/angular-ui-router/release/angular-ui-router.min.js"></script>    <script src="helloWorld.js"></script>    <style>        .active {            color: red;            font-weight: bold;        }    </style></head><body ng-app="myApp"><a ui-sref="hello" ui-sref-active="active">Hello</a><a ui-sref="home" ui-sref-active="active">Home</a><ui-view></ui-view></body></html>

helloWord.js中的代码如下:

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

myApp.config(function ($stateProvider, $urlRouterProvider) {    var helloState = {        name: 'hello',        url: '/hello',        template: '<h3>{{title}}</h3>',        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: '<h3>{{title}}</h3>',            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: '<p>这是详情部分</p>'    })

路由嵌套注意的地方

  • 状态注册可以是任何顺序,子状态在父状态之前注册也没可以
  • 父状态必须存在
  • 当子状态激活时,它的父状态也是激活状态,例如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指令的链接

<a ui-sref="home" ui-sref-active=“active”>Home</a>

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

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

<a href="#/home" ui-sref-active="active">Hello</a>

或者

window.location = '#/home';

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

跳转并刷新

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

<div ui-sref="home.list" ui-sref-opts="{reload: true}" ui-sref-active="active">List</div>

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

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

相对跳转

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

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

<div ui-sref=".list" ui-sref-opts="{reload: true}" 
ui-sref-active=“active">List</div>

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

$state.go(".list"); 

Resolve

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

示例如下:

.state("detail", {    url: '/detail',    parent: 'home',    template: '<div><p>这是详情部分</p></div>',    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: '<p>这是about的详情页面, 关于{{name}}</p>',    controller: function ($scope, $stateParams) {        $scope.name = $stateParams.name;    }})

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

.state("about.detail", {    url: ‘/detail/{name}’,    template: '<p>这是about的详情页面, 关于{{name}}</p>',    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: '<p>这是测试页面</p>',    controller: function ($scope, $stateParams) {        console.log($stateParams);    }})

传递参数

<div ui-sref="about.detail({name: 'default about’})”>Detail</div>

或者使用$state.go()的方式:

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

获取参数

.state("about.detail", {    url: '/detail/:name',    template: '<p>这是about的详情页面, 关于{{name}}</p>',    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) {});

多个命名视图

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

<!-- index.html -->
<body>
  <div ui-view="filters"></div>
  <div ui-view="tabledata"></div>
  <div ui-view="graph"></div>
</body>

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

$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 ... }
      }
    }
  })
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,837评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,551评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,417评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,448评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,524评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,554评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,569评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,316评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,766评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,077评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,240评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,912评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,560评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,176评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,425评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,114评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,114评论 2 352

推荐阅读更多精彩内容

  • 【JS-6】 如何使用ui-router? 小课堂【武汉分院第137期】 分享人:徐恒 目录 1.背景介绍 2.知...
    爱上Shu的小刺猬阅读 1,975评论 2 5
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,647评论 18 139
  • 大家好,我是IT修真院上海分院第01期学员,一枚正直善良的web程序员。 今天给大家分享一下,修真院官网 CSS任...
    爱猫先森阅读 582评论 0 0
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,988评论 25 707
  • 用户的浏览器登录新版时,COOKIE 中的 PHPSESSID 被 改成了 _PHPSESSID ,造成 SESS...
    金星show阅读 10,532评论 0 2