在使用Angular的过程中,推荐都是将数据及数据的处理逻辑写在Service里边,Controller里边只负责与视图交互的逻辑,所以很多同学都很自然地会这样写:

1
2
3
4
5
6
7
// in service,e.g. ServiceA
//... other code
this.data = [];

// in controller,e.g. ControllerA
//... other code
$scope.data = ServiceA.data;

这样写好以后,当ServiceA中的data变化,ControllerA中的数据也会跟着变,如果数据绑定到视图了,那么视图上也会跟着相应的变化,但是现在有个问题,如果Service.data是异步获取过来之后变化的呢,像下面这样

1
2
3
4
5
6
7
8
9
10
11
12
13
//code in ServiceA
// ... other codes
var self = this; // for get this in async
$http(req).success(function(response,status,headers,config){
var res= angualr.fromJson(response); // e.g. repsonse is JSON

self.data = res.data;//e.g response is an object, and has a data property
//here if use this is a Window Object,you should use self instead of this

})
.error(function(){
// code for http error
});

视图上的数据会跟着变化吗?No!太天真!这个时候会发现,视图上屁的动静也没有,那么这是怎么回事,如何解决呢?

这里需要重补JavaScript基础知识,Javscript的变量类型分为不可变的原始值(undefined,null,bool,number,string)和可变的引用对象(Array,Object..),原始值是不可更改的,任何方法都无法更改,尤其对字符串,看上去返回了一个修改后的字符串都是返回了一个新的字符串对值,如下:

1
2
3
4
var s = "hello"; //定义s
s.toUpperCase(); //返回HELLO,但s没有变
s //输出hello
//这是不可变的原始值,对于Object和Array呢,他们都是引用对象,就像C++里边的指针,C#和Java中的引用对象概念,他们是可变的,如下:

1
2
3
var o = {x:1}; //定义一个对象
o.x = 2; //修改对象属性值
o.y = 3; //再次给对象增加一个新属性

那么现在来看,我们在service中到底做了什么吧,var res = angular.fromJson(response);这一句话的作用是根据返回的response(JSON字符串)创建了一个新的对象res,self.data = res;这句的作用是什么呢,把data的引用指向了新的res的基对象,原来data的基对象还在吗,这个时候是在的,由ControllerA中的$scope.data 引用,到这一步,controllerA中的data和serviceA中的data已经完全没有关系了,他们分别指向了两个不同的内存地址,controllerA中的对象根本没有变,视图中当然也就不会有变化!其前后如图所示(原谅手头没有好的画图工具,凑合吧):
异步前后引用对象的变化情况

这样就明白为什么视图没有更新了吧!那么如何解决这个问题?有两种办法,第一种copy,注意angualr.copy会所destination中的清空,再复制,如果不想清空,请用push或者其他操作:

1
2
3
4
// in async

var res = angular.formJson(response);
angular.copy(res,slef.data);

第二种:使用promise,$http本身就是一个promise对象,可以返回,在controller中获取pormise,然后把$scope.data 再指向service.data就可以了!

1
2
3
4
5
6
7
8
9
10
11
12
// in service
this.foo(){
return $http(req).success(function(){

var res= angualr.fromJson(response);
self.data = res.data;

})//other code

}
// in controller
service.foo().then(fucntion(){$scope.data = service.data}) //point to service.data again

注意,还有讲用$watch()实现的,由于监听效率会比较低,不推荐使用!

参考:
《JavaScript权威指南》,O’REILLY,机械工业出版社
angularJS API,http://docs.angularjs.cn/api/ng/function/angular.copy