Angular入门教程四
4.8依赖注入DI
通过依赖注入,ng想要推崇一种声明式的开发方式,即当我们需要使用某一模块或服务时,不需要关心此模块内部如何实现,只需声明一下就可以使用了。在多处使用只需进行多次声明,大大提高可复用性。
比如我们的controller,在定义的时候用到一个$scope参数。
app.controller('testC',function($scope){});
如果我们在此处还需操作其他的东西,比如与浏览器地址栏进行交互。我们只需再多添
一个参数$location进去:
app.controller('testC',function($scope,$location){});
这样便可以通过$location来与地址栏进行交互了,我们仅仅是声明了一下,所需的其他代码,框架已经帮我们注入了。我们很明显的感觉到了这个函数已经不是常规意义上的javascript函数了,在常规的函数中,把形参换一个名字照样可以运行,但在此处若是把$scope换成别的名字,程序便不能运行了。因为这是已经定义好的服务名称。
这便是依赖注入机制。顺理成章的推断,我们可以自己定义模块和服务,然后在需要的地方进行声明,由框架来替我们注入。
来看下我们如何定义一个服务:
app.factory('tpls',function(){
return ['tpl1','tpl2','tpl3','tpl4'];
});
看上去相当简单,是因为我在这里仅仅是直接返回一个数组。在实际应用中,这里应该是需要向服务器发起一个请求,来获取到这些模板们。服务的定义方式有好几种,包括使用provider方法、使用factory方法,使用service方法。它们之间的区别暂且不关心。我们现在只要能创建一个服务出来就可以了。我使用了factory方法。一个需要注意的地方是,框架提供的服务名字都是由$开头的,所以我们自己定义的最好不要用$开头,防止发生命名冲突。
定义好一个服务后,我们就可以在控制器中声明使用了,如下:
app.controller('testC',function($scope,tpls){
$scope.question = questionModel;
$scope.nowTime = new Date().valueOf();
$scope.templates = tpls; //赋值到$scope中
$scope.addOption = function(){
var o = {content:''};
$scope.question.options.push(o);
};
$scope.delOption = function(index){
$scope.question.options.splice(index,1);
};
});
此时,若在模板中书写如下代码,我们便可以获取到服务tpls所提供的数据了:
模板:
<a href="javascript:void(0);" target="_blank" rel="nofollow">
4.9路由(route)
在谈路由机制前有必要先提一下现在比较流行的单页面应用,就是所谓的single page APP。为了实现无刷新的视图切换,我们通常会用ajax请求从后台取数据,然后套上HTML模板渲染在页面上,然而ajax的一个致命缺点就是导致浏览器后退按钮失效,尽管我们可以在页面上放一个大大的返回按钮,让用户点击返回来导航,但总是无法避免用户习惯性的点后退。解决此问题的一个方法是使用hash,监听hashchange事件来进行视图切换,另一个方法是用HTML5的history API,通过pushState()记录操作历史,监听popstate事件来进行视图切换,也有人把这叫pjax技术。基本流程如下:
如此一来,便形成了通过地址栏进行导航的深度链接(deeplinking ),也就是我们所需要的路由机制。通过路由机制,一个单页应用的各个视图就可以很好的组织起来了。
4.9.1 ngRoute内容
ng的路由机制是靠ngRoute提供的,通过hash和history两种方式实现了路由,可以检测浏览器是否支持history来灵活调用相应的方式。ng的路由(ngRoute)是一个单独的模块,包含以下内容:
l 服务$routeProvider用来定义一个路由表,即地址栏与视图模板的映射
l 服务$routeParams保存了地址栏中的参数,例如{id : 1, name : 'tom'}
l 服务$route完成路由匹配,并且提供路由相关的属性访问及事件,如访问当前路由对应的controller
l 指令ngView用来在主视图中指定加载子视图的区域
以上内容再加上$location服务,我们就可以实现一个单页面应用了。下面来看一下具体如何使用这些内容。
4.9.2 ng的路由机制
第一步:引入文件和依赖
ngRoute模块包含在一个单独的文件中,所以第一步需要在页面上引入这个文件,如下:
<script src="http://code.angularjs.org/1.2.8/angular.min.js" rel="nofollow"/>
<script src="http://code.angularjs.org/1.2.8/angular-route.min.js" rel="nofollow"/>
光引入还不够,我们还需在模块声明中注入对ngRoute的依赖,如下:
var app = angular.module('MyApp', ['ngRoute']);
完成了这些,我们就可以在模板或是controller中使用上面的服务和指令了。下面我们需要定义一个路由表。
第二步:定义路由表
$routeProvider提供了定义路由表的服务,它有两个核心方法,when(path,route)和otherwise(params),先看一下核心中的核心when(path,route)方法。
when(path,route)方法接收两个参数,path是一个string类型,表示该条路由规则所匹配的路径,它将与地址栏的内容($location.path)值进行匹配。如果需要匹配参数,可以在path中使用冒号加名称的方式,如:path为/show/:name,如果地址栏是/show/tom,那么参数name和所对应的值tom便会被保存在$routeParams中,像这样:{name : tom}。我们也可以用*进行模糊匹配,如:/show*/:name将匹配/showInfo/tom。
route参数是一个object,用来指定当path匹配后所需的一系列配置项,包括以下内容:
l controller //function或string类型。在当前模板上执行的controller函数,生成新的scope;
l controllerAs //string类型,为controller指定别名;
l template //string或function类型,视图z所用的模板,这部分内容将被ngView引用;
l templateUrl //string或function类型,当视图模板为单独的html文件或是使用了<script type="text/ng-template">定义模板时使用;
l resolve //指定当前controller所依赖的其他模块;
l redirectTo //重定向的地址。
最简单情况,我们定义一个html文件为模板,并初始化一个指定的controller:
function emailRouteConfig($routeProvider){
$routeProvider.when('/show', {
controller: ShowController,
templateUrl: 'show.html'
}).
when('/put/:name',{
controller: PutController,
templateUrl: 'put.html'
});
};
otherwise(params)方法对应路径匹配不到时的情况,这时候我们可以配置一个redirectTo参数,让它重定向到404页面或者是首页。
第三步:在主视图模板中指定加载子视图的位置
我们的单页面程序都是局部刷新的,那这个“局部”是哪里呢,这就轮到ngView出马了,只需在模板中简单的使用此指令,在哪里用,哪里就是“局部”。例如:
<div ng-view></div> 或:<ng-view></ng-view>
我们的子视图将会在此处被引入进来。完成这三步后,你的程序的路由就配置好了。
4.9.3 路由示例
下面我们将用一个例子(例09)来说明路由的使用方式及步骤:
1.为demoApp添加一个路由,代码如下:
demoApp.config(['$routeProvider',function($routeProvider) {
$routeProvider.when('/list', {
templateUrl: 'route/list.html',
controller: 'routeListController'
}).when('/list/:id', {
templateUrl: 'route/detail.html',
controller: 'routeDetailController'
}).otherwise({
redirectTo: '/list'
});
}]);
/list 对应为:route/list.html页面,显示用户列表;/list/:id对应于route/detail.html页面,显示用户详细信息。
2.为list.html和detail.html分别声明Controller:routeListController和routeDetailController。
demoApp.controller('routeListController',function($scope) {
$scope.users = [{userId:"zhangsan",userName:"张三",userInfo:"我是张三,我为自己带盐!"},
{userId:"lisi",userName:"李四",userInfo:"我是李四,我为卿狂!"},
{userId:"woshishui",userName:"我是谁",userInfo:"我是谁!我是谁!我是谁!"}];
});
demoApp.controller('routeDetailController',function($scope, $routeParams, userService) {
$scope.userDetail = userService.getUser($routeParams.id);
});
routeDetailController中如上面提到的一样,注入了userService服务,在这里直接拿来用。
3.创建list.html和detail.html页面,代码如下:
<hr/>
<h3>Route : List.html(用户列表页面)</h3>
<ul>
<li ng-repeat="user in users">
<a href="http://m.cnblogs.com/142260/3817063.html?full=1#/list/{{ user.userId }}" target="_blank" rel="nofollow">
</li>
</ul>
<hr/>
<h3>Route : detail.html(用户详细信息页面)</h3>
<h3>用户名:<span style="color: red;">{{userDetail.userName}}</span></h3>
<div>
<span>用户ID:{{userDetail.userId}}</span><span>用户名:{{userDetail.userName}}</span>
</div>
<div>
用户简介:<span>{{userDetail.userInfo}}</span>
</div>
<div>
<a href="http://m.cnblogs.com/142260/3817063.html?full=1#/list" target="_blank" rel="nofollow">返回</a>
</div>
4. 路由局部刷新位置:
<h1>AngularJS路由(Route) 示例</h1>
<div ng-view></div>
4.10 NG动画效果
4.10.1 NG动画效果简介
NG动画效果,现在可以通过CSS3或者是JS来实现,如果是通过JS来实现的话,需要其他JS库(比如JQuery)来支持,实际上底层实现还是靠其他JS库,只是NG将其封装了,
使其更易使用。
NG动画效果包含以下几种:
- enter:元素添加到DOM中时执行动画;
- leave:元素从DOM删除时执行动画;
- move:移动元素时执行动画;
- beforeAddClass:在给元素添加CLASS之前执行动画;
- addClass:在给元素添加CLASS时执行动画;
- beforeRemoveClass:在给元素删除CLASS之前执行动画;
- removeClass:在给元素删除CLASS时执行动画。
其相关参数为:
var ngModule = angular.module('YourApp', ['ngAnimate']);
demoApp.animation('.my-crazy-animation', function() {
return {
enter: function(element, done) {
//run the animation here and call done when the animation is complete
return function(cancelled) {
//this (optional) function will be called when the animation
//completes or when the animation is cancelled (the cancelled
//flag will be set to true if cancelled).
};
},
leave: function(element, done) { },
move: function(element, done) { },
//animation that can be triggered before the class is added
beforeAddClass: function(element, className, done) { },
//animation that can be triggered after the class is added
addClass: function(element, className, done) { },
//animation that can be triggered before the class is removed
beforeRemoveClass: function(element, className, done) { },
//animation that can be triggered after the class is removed
removeClass: function(element, className, done) { }
};
});
4.10.2 动画效果示例
下面我们来看下DEMO中的例子(例10)。
1.首先,我们在demoApp下定义一个动画效果,匹配CLASS:” .border-animation”
/*定义动画*/
demoApp.animation('.border-animation', function(){
return{
beforeAddClass : function (element, className, done) {
$(element).stop().animate({
'border-width':1
},2000, function() {
done();
});
},
removeClass : function (element ,className ,done ) {
$(element).stop().animate({
'border-width':50
},3000, function() {
done();
});
}
};
});
动画效果的含义就是:在匹配CLASS为border-animation的元素添加一个CLASS之前使其边框的宽度在2秒内变为1PX;并在其移除一个CLASS时使其边框的宽度在3秒内变为50PX。
2. 视图中的代码如下(主要,其他相关样式请查看例子代码):
<div class="border-animation" ng-show="testShow"></div>
<a href="javascript:void(0);" target="_blank" rel="nofollow">
ng-show为false时会为其加上“ng-hide“的CLASS; ng-show为true时会为其移除“ng-hide“的CLASS,从而触发动画效果。
3.其他代码:
demoApp.controller("test10Controller", function($scope, $animate){
$scope.testShow = true;
});