angular的uiRouter服务学习(1)

angular有内置的路由服务$route:angular -- $route API翻译

使用$route可以帮助实现路由的切换,视图的改变,但是这个内置的$route只包含了基本的功能,在很多场合下是不够用的.所以,需要学习使用uiRouter.

首先,在页面中链入‘angular-ui-router.min.js‘,然后在模块中写入依赖:

var myapp = angular.module(‘myApp‘,[‘ui.router‘]);

然后就可以使用一个叫做$state的服务,使用$stateProvider来配置这个服务.

$stateProvider和angualr内置的$routeProvider的用法类似,但是它是通过‘状态‘来管理路由的.

  • 在整个应用的界面和导航中,状态对应了页面中的一个位置(也就是ui-view)
  • 状态通过controller,template,view等属性,描述了它对应位置的视图展示和行为.
  • 状态之间通常有一些共同点,把这些共同点从模型中分解出来的最好办法就是通过状态继承. 比如父/子状态(又名状态嵌套)

 下面例举一个最简单的状态:

<body ng-controller="MainCtrl">
  <section ui-view></section>
</body>
$stateProvider.state(‘contacts‘, {
  template: ‘<h1>My Contacts</h1>‘
})

名为‘contacts‘的状态,对应了ui中的‘ui-view‘指令元素.当contacts状态被激活,ui-view元素就会被template填充.

 模板的插入位置:

当一个状态被激活,它的模板会被自动填充到父状态模板里的ui-view元素里.如果这个状态是个顶层状态-比如上面例子中的‘contacts‘状态,它没有父状态.那么,它的父状态模板就是整个html.

另外,ui-view元素可以拥有原始内容,用于当状态还没有被激活时展示,当状态被激活后,原始内容会被替换掉:

<body>
    <ui-view>
        <i>Some content will load here!</i>
    </ui-view>
</body>

现在,‘contacts‘状态不会被激活,下面来看看如何激活它:

 激活一个状态:

  • 调用 $state.go().具体用法以后再讲.
  • 点击一个带有ui-sref属性的a链接.ui-sref属性值就是状态值.具体用法以后再讲.
  • 状态里定义对应url,当页面的url改变成对应状态的url时,就激活这个状态.具体用法以后再讲.

 状态的模板:

有几种方法可以定义状态对应的视图模板:

1.定义template属性,属性值为字符串html:

$stateProvider.state(‘contacts‘, {
  template: ‘<h1>My Contacts</h1>‘
})

2.定义templateUrl属性,属性值一个函数,函数返回值为模板文件对应的url路径:

函数中可以注入$stateParams.$stateParams是url参数组成的键值对对象. 比如这里url里的name就是一个参数,那么,$stateParams就是{name:‘‘}

$stateProvider.state(‘contacts‘,{
      url:‘/contacts/:name‘,
      templateUrl: function($stateParams){
            return ‘partials/contacts.‘ + $stateParams.name + ‘.html‘
      }
})

3.定义templateProvider属性.属性值是一个函数,函数的返回值为字符串html:

函数中同样可以注入$stateParams

$stateProvider.state(‘contacts‘,{
      url:‘/contacts/:name‘,
      templateProvider: function($stateParams){
            return ‘<h1>‘+$stateParams.name+‘</h1>‘
      }
})

 状态的控制器:

可以为每个视图模板分配一个控制器. 注意:如果模板没有被定义,那么控制器不会被实例化.

有以下几种方式可以定义控制器:

1.定义controller属性,属性值为模块下定义好的控制器,

在myapp模块下定义了‘contact‘模块,然后controller属性就可以直接定义属性值为‘contact‘

myapp.config(function($stateProvider){
    $stateProvider.state(‘contacts‘,{
        url:‘/contacts/:name‘,
        controller:‘contact‘,
        templateProvider: function($stateParams){
            return ‘<h1>‘+‘{{text}},‘+$stateParams.name+‘</h1>‘
        }
    })
});
myapp.controller(‘contact‘,function($scope){
    $scope.text=‘hi‘
});

2.定义controller属性,属性值就是控制器函数

myapp.config(function($stateProvider){
    $stateProvider.state(‘contacts‘,{
        url:‘/contacts/:name‘,
        controller:function($scope){
            $scope.text=‘hi‘
        },
        templateProvider: function($stateParams){
            return ‘<h1>‘+‘{{text}},‘+$stateParams.name+‘</h1>‘
        }
    })
});

3.定义controller属性,属性值为模块下定义好的控制器+‘as con‘,并且定义controllerAs属性为‘con‘,然后在作用域中使用con

这里的con只是我随便取的名字...不是定死的...

myapp.config(function($stateProvider){
    $stateProvider.state(‘contacts‘,{
        url:‘/contacts/:name‘,
        controller:‘contact as con‘,
        templateProvider: function($stateParams){
            return ‘<h1>‘+‘{{con.text}},‘+$stateParams.name+‘</h1>‘
        },
        controllerAs:‘con‘
    })
});
myapp.controller(‘contact‘,function(){
    this.text=‘hi‘
});

4.定义controller属性,属性值就是控制器函数,并且定义controllerAs属性为‘con‘,然后在作用域中使用con

这里的con只是我随便取的名字...不是定死的...

myapp.config(function($stateProvider){
    $stateProvider.state(‘contacts‘,{
        url:‘/contacts/:name‘,
        controller:function(){
            this.text=‘hi‘
        },
        templateProvider: function($stateParams){
            return ‘<h1>‘+‘{{con.text}},‘+$stateParams.name+‘</h1>‘
        },
        controllerAs:‘con‘
    })
});

控制器会根据需要,在对应的作用域被创建的时候实例化. 比如: 当url被改变到和状态匹配的url时,$stateProvider会把对应的模板加载到视图里,然后把控制器绑定到模板的作用域下.

 状态的resolve:

resolve属性非常重要,它是一个map对象(也就是json对象),它为状态的控制器提供了所需的依赖.这些依赖可以给状态对应的控制器提供所需要的内容或数据.如果resolve属性值中有promise对象,那么它会在控制器被实例化、$stateChangeSuccess事件触发之前被解析并且转换成值.

resolve的属性和属性值应该是这样的:

  • 属性名: 这个名字将会被作为依赖,注入到controller里.
  • 属性值: 字符串|函数     
    • 字符串: 当前模型下既有的服务名       
    • 函数: 函数的返回值会作为依赖,可以被注入到控制器中.如果函数的返回值是一个promise对象,那么它会在控制器被实例化、$stateChangeSuccess事件触发之前被解析并且转换成值.这个值会被作为依赖注入.  

下面这段代码介绍了常见的resolve的六种类型:

/*resolve*/
myapp.config(function($stateProvider){
    $stateProvider.state(‘contacts‘,{
        url:‘/contacts/:name‘,
        resolve:{
            //字符串格式:使用一个既有的服务
            first:‘aService‘,
            //函数:函数的返回值就是将被注入的服务
            second:function(){
                return {data:‘second的data‘}
            },
            //函数:在函数中注入既有的服务
            third:function(anotherService,$stateParams){
                var data = anotherService.getName($stateParams.name);
                return {data:data}
            },
            //函数:返回一个promise对象,最终得到的将是resolve里的内容
            fourth:function($q,$timeout){
                var defer = $q.defer();
                $timeout(function(){
                    defer.resolve({data:‘我是fourth的data‘});
                    //注意,如果一个state的resolve里的某个promise被拒绝了,那这个state直接无法继续下去了.
                    //defer.reject({data:‘我是fourth的data‘})
                },2000);
                return defer.promise;
            },
            //函数:返回$http返回的promise返回的promise,最终得到的是.then里面return的内容
            fifth:function($http){
                return $http({
                    method:‘GET‘,
                    url:‘/contacts/name‘
                }).then(function(res){
                    return {data:res.data}
                },function(){

                })
            },
            //函数:返回$http返回的promise,最终得到的就是后台返回值.
            sixth:function($http){
                return $http({
                    method:‘GET‘,
                    url:‘/contacts/name‘
                })
            }
        },
        templateUrl:function($stateParams){
            return ‘partials/contacts.‘ + $stateParams.name + ‘.html‘
        },
        controller:‘ctrl‘
    })
});
myapp.factory(‘aService‘,function(){
    return {
        getName:function(){
            alert(‘我是aService服务的getName方法‘)
        },
        data:‘first的data‘
    }
});
myapp.factory(‘anotherService‘,function(){
    return {
        getName:function(data){
            return data.toUpperCase()
        }
    }
});
myapp.controller(‘ctrl‘,function($scope,first,second,third,fourth,fifth,sixth){
    first.getName();
    $scope.data1 = first.data;
    $scope.data2 = second.data;
    $scope.data3 = third.data;
    $scope.data4 = fourth.data;
    $scope.data5 = fifth.data;
    $scope.data6 = sixth.data;
});

1.first: 一个既有的服务名.

  注入‘first‘依赖就相当于注入了‘aService‘服务. 

{
        getName:function(){
            alert(‘我是aService服务的getName方法‘)
        },
        data:‘first的data‘
}

2.second: 一个函数

  注入‘second‘依赖,得到的是这个函数的返回值

{data:‘second的data‘}

3.third: 一个函数,函数中可以注入既有的服务

  其实这种情况和2一样,只是说,函数里可以注入依赖

{data:‘BUNNY‘}

4.fourth: 一个promise对象

  控制器会等到promise被解析以后再实例化,而注入的依赖,不是promise本身,而是promise被解析的值,需要注入的是,如果promise不是被resolve,而是被reject,那么js会被中断,控制器不会被实例化.状态切换也失败了.

{data:‘我是fourth的data‘}

5.fifth: 一个promise对象返回的promise对象

  其实这种情况和4一样,promise.then返回的promise对象,会被.then()函数里的返回值解析.这适用于对返回值做一些处理后再返回.

  (这里后台返回 [‘bunny‘,‘cat‘,‘dog‘] )

{data:[‘bunny‘,‘cat‘,‘dog‘]}

6.sixth: 返回一个$http返回的promise对象

  其实这种情况也和4一样.这个promise会被返回值解析.所以最后得到的就是返回值了.

{data:[‘bunny‘,‘cat‘,‘dog‘]}

 给状态对象添加自定义的数据:

$stateProvider.state(‘name‘,{})中的.state第二个参数对象可以添加自定义的属性和值,为了避免冲突,一般使用data属性来为它添加自定义属性.

自定义的属性可以通过$state.current.data来访问到.

myapp.config(function($stateProvider){
    $stateProvider.state(‘contacts‘,{
        url:‘/contacts/:name‘,
        templateUrl:function($stateParams){
            return ‘partials/contacts.‘+$stateParams.name+‘.html‘
        },
        data:{
            stateData1:111,
            stateData2:222
        },
        controller:function($scope,$state){
            $scope.data7 = $state.current.data.stateData1 + $state.current.data.stateData2
        }
    })
});

 状态的onEnter和onExit回调:

状态的onEnter属性和onExit属性可以用来定义进入状态和退出状态所执行的回调:

注意,回调函数中可以注入resolve里定义的依赖,比如下面的‘title‘:

  $stateProvider.state(‘contacts‘,{
        url:‘/contacts/:name‘,
        templateUrl:function($stateParams){
            return ‘partials/contacts.‘+$stateParams.name+‘.html‘
        },
        resolve:{
            title:function(){
                return ‘contacts‘
            }
        },
        onEnter: function(title){
            console.log(‘进入‘+title+‘状态啦‘)
        },
        onExit: function(title){
            console.log(‘退出‘+title+‘状态啦‘)
        }
 })

状态改变事件:

这些事件都是在$rootScope上触发的.

  • $stateChangeStart: 当状态开始改变时触发, 接受5个参数:

    • event: 事件对象,使用event.preventDefault()可以阻止状态发生改变. 
    • toState: toState是定义.state时传入的第二个参数对象  
    • toParams: toParams就是$stateParams
    • fromState: fromState是上一个状态(就是离开的状态).state时传入的第二个参数对象
    • fromParams: fromParams是上一个状态(就是离开的状态)的$stateParams    
  • $stateNotFound: 当状态没有找到时触发,接受4个参数: $stateChangeSuccess: 当状态改变结束时触发,可以接受5个参数,5个参数同‘$stateChangeStart‘的5个参数
    • event: 事件对象,使用event.preventDefault()可以阻止js继续执行,否则会报错,卡住.
    • unfoundState: 一个对象,这个对象有三个属性:
      • to: 前往的状态名(也就是没有找到的这个状态名)
      • toParams: 前往的状态的参数(在使用ui-sref或者$state.go()的时候可以传入)
      • options: 使用$state.go()的时候传入的第三个参数
    • fromState: 同上
    • fromParams: 同上
  • $stateChangeError: 当状态改变失败时触发,需要注意,如果在状态的resolve过程中遇到了问题(比如js错误,服务找不到,请求得不到响应等),这些错误不会像传统的那样被抛出,而是会在$stateChangeError里被捕获.它可以接受6个参数
    • event: 事件对象
    • toState: 同上
    • toParams: 同上
    • fromState: 同上
    • fromParams: 同上
    • error: 一个包含了错误信息的对象 

 

<div>
  <a href="contacts/bunny">查看视图</a>
  <a href="contacts/exit">离开</a>
  <a ui-sref="lalala({a:1,b:2})">无</a>
  <section ui-view loading>点击链接后内容会被加载在这里</section>
</div>
myapp.directive(‘loading‘,function($rootScope){
    return {
        restrict:‘EA‘,
        link:function(scope,iEle,iAttrs,ctrl){
            console.log(scope===$rootScope);
            scope.$on(‘$stateChangeStart‘,function(event,toState,toParams,fromState,fromParams){
                console.log(‘状态开始改变‘);
                /*toState是定义.state时传入的第二个参数对象*/
                //console.log(toState);
                /*toParams就是$stateParams*/
                //console.log(toParams);
                /*fromState是上一个状态.state时传入的第二个参数对象*/
                //console.log(fromState);
                /*fromParams是上一个状态的$stateParams*/
                //console.log(fromParams);
            });
            scope.$on(‘$stateChangeSuccess‘,function(event,toState,toParams,fromState,fromParams){
                console.log(‘状态改变结束‘);
                /*参数全部同上*/
            });
            scope.$on(‘$stateNotFound‘,function(event,unfoundState,fromState,fromParams){
                console.log(‘没有找到对应的状态‘);
                /*unfoundState包含了三个属性:*/
                /*1.to:前往的状态名(也就是没有找到的这个状态名)
                * 2.toParams:前往的状态的参数(在使用ui-sref或者$state.go()的时候可以传入,这个例子里就是{a:1,b:2})
                * 3.options:使用$state.go()的时候传入的第三个参数.
                * */
                /*最后两个参数同上*/
                 console.log(unfoundState);
                //如果不写这句,那么接下来就会报错,卡住js进程了.
                event.preventDefault()
            });
            scope.$on(‘$stateChangeError‘,function(event, toState, toParams, fromState, fromParams, error){
                console.log(‘切换状态出错‘);
                /*error是一个包含了错误信息的对象*/
                console.log(error);
            });
            scope.$on(‘$viewContentLoading‘,function(event,viewConfig){
                console.log(‘视图开始加载‘);
            });
            scope.$on(‘$viewContentLoaded‘,function(event){
                console.log(‘视图渲染完毕‘)
            })
        }
    }
});

*注意,官网里是说事件都在$rootScope上触发,但是这里直接在指令元素的scope上也能触发.

 视图加载事件:

  • $viewContentLoading: 当视图开始渲染的时候,$rootScope传播这个事件. 它接受2个参数

    • event: 事件对象
    • viewConfig: 包含一个属性:targetView
  • $viewContentLoaded: 当视图渲染完毕的时候,视图所在的scope传播这个事件.
scope.$on(‘$viewContentLoading‘,function(event,viewConfig){
   console.log(‘视图开始加载‘);
});
scope.$on(‘$viewContentLoaded‘,function(event){
   console.log(‘视图渲染完毕‘)
})

事件触发执行顺序:

下面来理一下一个状态被激活的过程是怎样的:

1. 触发$stateChangeStart事件,如果使用event.preventDefault(),会阻止状态改变.

如果没有找到对应状态,会触发$stateNotFound事件,然后中断.

2. 触发$viewContentLoading事件.

3. 如果在切换状态的过程中出错(比如resolve出错),触发$stateChangeError事件,无出错跳过此步.

4. 触发上一个状态(若有)的onExit回调事件

5. 触发当前状态的onEnter回调事件

6. 触发$viewContentLoaded事件

7. 触发$stateChangeSuccess事件.

完整代码: https://github.com/OOP-Code-Bunny/angular/tree/master/uiRouter

参考网站: https://github.com/angular-ui/ui-router/wiki

时间: 2024-10-03 23:07:19

angular的uiRouter服务学习(1)的相关文章

angular的uiRouter服务学习(3)

本篇接着上一篇 angular的uiRouter服务学习(2) 继续讲解uiRouter的用法 本篇主要讲解uiRouter的多个命名的视图 我们可以给ui-view元素添加ui-view的值来给它命名,这样,一个视图模板里就可以有多个ui-view标签. 比如下面这个应用,它需要动态的填充一个图表,图表里有一些表格数据,筛选项,等: 给视图命名,需要在状态里定义views属性. views的属性值为一个对象. views属性会覆盖template属性: 如果给状态定义了views属性,那么状态

angular的uiRouter服务学习(5) --- $state.includes()方法

$state.includes方法用于判断当前激活状态是否是指定的状态或者是指定状态的子状态. $state.includes(stateOrName,params,options) $state.includes方法接受三个参数,其中第二和第三个都不知道是干啥的...估计也不太用得到,就暂时不管了... stateOrName:字符串(必填). 是一个状态的名字. 比如当前的激活状态是 "contacts.details.item" 如下调用: $state.includes(&qu

成为优秀Angular开发者所需要学习的19件事

一款to-do app基本等同于前端开发的"Hello world".虽然涵盖了创建应用程序的CRUD方面,但它通常只涉及那些框架或库也能做到的皮毛而已. Angular看起来似乎总是在改变和更新 - 但实际上,还是有一些事情仍然保持不变.以下是关于Angular所需要学习的核心概念的概述,以便大家可以正确地利用JavaScript框架. 说到Angular,我们需要学习很多东西,很多人被困在初学者的圈子里,仅仅是因为不知道去哪里搜索或者应该搜索什么关键词.下面我们会说到的这个指南(也

c# windows 服务学习

用C#做windows服务变得简单对了===按照下面步骤来就行了用C#创建Windows服务(Windows Services)例子服务功能:这个服务在启动和停止时,向一个文本文件中写入一些文字信息. 第一步:创建服务框架 要创建一个新的 Windows 服务,可以从Visual C# 工程中选取 Windows 服务(Windows Service)选项,给工程一个新文件名,然后点击 确定.你可以看到,向导向工程文件中增加WebService1.cs类:其中各属性的含意是: Autolog 是

angular的splitter案例学习

angular的splitter案例学习,都有注释了,作为自己的备忘. 运行下面代码          Angular pane splitter example                     Pane 1                                       Pane 2                 Pane 3 .split-panes {left: 0px;right: 0px;top: 0px;bottom: 0px;position: absolut

MMS服务学习

1.分类: 1)带确认服务 如后台的遥控操作 2)不带确认的 如测控装置定时的电流.电压遥测量的上送 2.MMS基本思想 屏蔽具体装置技术细节实现异构装置之间的互联互通 ,各装置必须按照MMS的规范完成VMD与实际装置之间的映射 3.MMS对象和服务 1)VMD模型包含以下内容: i)对象.属性(名字.值.类型) ii)服务Read Write MMS服务学习,布布扣,bubuko.com

微服务学习和认识

微服务学习和认识 1.什么是微服务 微服务架构风格:是一类将单一应用程序作为由众多小型服务构成之套件加以开发的方式,其中各项服务都拥有自己的进程并利用轻量化机制(通常为HTTP源API)实现通信.这些服务围绕业务功能建立而成,且凭借自动化部署机制实现独立部署.这些服务匹配一套最低限度的中央式管理机制,且各服务可通过不同编程语言编写而成并使用不同的数据存储技术 2.微服务的目 有效的拆分应用,实现敏捷开发和部署 3.微服务的优点 开发简单 技术栈灵活 服务独立无依赖 独立部署.按需扩展 可用性高

介绍Angular的注入服务

其实angular的注入服务是挺复杂的,目前看源码也只看懂了一半,为了不误导大家,我也不讲敢讲太复杂,怕自己都理解错了. 首先我们要知道angular的三种注入方式: 第一种:inference var myModule = function($scope){ } 第二种:annotation var myModule = function($location){ console.log('Module:代码注入$location成功'); console.log($location); } m

angular访问后台服务及监控会话超时的封装

angular访问后台服务及监控会话超时的封装 angular本身自带访问组件http和httpclient,组件本身都是异步模式访问.本文只列举了对http组件的封装同时也一同处理会话超时监控. 获取组件源码请入QQ群706224870,在群文件中下载. 入群验证消息codefc. 实现思路概述: 1.将请求入参和出参统一约定 2.封装方法将请求参数.数据处理方法.数据呈现方法.访问错误处理方法封装在一起,业务调用通过服务调用该封装方法, 同时把请求参数.数据处理方法.数据呈现方法.访问错误处