0

AngularJS框架 依赖注入 实战

依赖注入是一个在组件中给出的替代了硬的组件内的编码它们的依赖关系的软件设计模式。


依赖注射概念最早是 Uncle Bob 在他May 1996的 C++报告, The Dependency Inversion Principle.


Bob 在他的书籍中也谈论了这个原理, “Agile Software Development, Principles, Patterns, and Practices, and Agile Principles, Patterns, and Practices in C#”。


依赖注入可以看成是 反转控制 inversion of control 的一个特例。反转的是依赖,而不是其他,JNDI也是一种反转控制,它反转的JNDI名称或资源。参考: “Inversion of Control Containers and the Dependency Injection pattern”。


依赖注入框架最早是由Spring和piconcontainer等实现,如今已经是一个缺省主流模式,并扩展到前端如Angular.js。


《JavaScript开发框架AngularJS基本概念》里,我们知道AngularJS提供的标准服务组件有以下:


  1. $http:用于处理 XMLHttpRequest 。
  2. $location:提供当前URL的信息。
  3. $q: 异步请求使用,promise/deferred模块。
  4. $routeProvider:配置路由。
  5. $log:AngularJS框架日志服务。


下面我们来讲解一下最常用以及AngularJS调用后端的$http的用法:



AngularJS的$http有下面短方法:


$http.get()、$http.head()、$http.post()、 $http.put()、$http.delete()、$http.jsonp()


我们假设调用后端的REST URL是app/phones/phones.json,其返回的是一个Json:


[
    {
        "age": 13,
        "id": "motorola-defy-with-motoblur",
        "name": "Motorola DEFY\u2122 with MOTOBLUR\u2122",
        "snippet": "Are you ready for everything life throws your way?"
        ...
    },
    ...
]


AngularJS框架它的所有Service服务组件都是通过依赖注入DI来精心管理的,DI能够分离表现 数据和控制器,实现分离关注和松耦合。


AngularJS控制器的代码是:


var phonecatApp = angular.module('phonecatApp', []);
 
phonecatApp.controller('PhoneListCtrl', function ($scope, $http) {
    $http.get('phones/phones.json').success(function(data) {
        $scope.phones = data;
    });
 
    $scope.orderProp = 'age';
});


请大伙留意这里:


phonecatApp.controller('PhoneListCtrl', function ($scope, $http) {...}


这表示创建一个名称为'PhoneListCtrl'的服务,其内容是function ($scope, $http) {...},也就是类似


var PhoneListCtrl =function ($scope, $http) {...}


AngularJS 将PhoneListCtrl注入到控制器中,当然PhoneListCtrl也依赖于$scope和$http,它们也是一样,都是被注入器注入。


$http 对象实现对后端Web服务器的GET请求,URL是: phone/phones.json,路径是相对于当前 index.html文件。


当$http成功后 success() 将会方法返回一个叫做 promise($q) 的对象,详细请参考这里:结合RxJS + AngularJS 实现异步处理


我们则可以使用返回的数据Data赋值给当前的作用域(胶水)$scope中。而 AngularJS 它会侦测到JSON响应,然后它会转换成类似的数组格式。


使用 AngularJS 创建自己的服务


虽然之前已经提过了很多 AngularJS 的服务,但是如果想要去创建一个有趣的应用,你还是需要自己去写服务。我们可以通过在模块Module api中注册一个服务工厂函数,或者通过Modeul#factory api或者直接通过模块配置函数中的 $provide api 去实现它。


而所有的AngularJS服务都符合依赖注入的原则。它们都用一个唯一的名字来将自己注册到 AngularJS 的依赖注入系统(injector)中去,与此同时声明需要提供给工厂函数的依赖。


如何 使用 AngularJS 的 angular.Module api 注册服务:


var myModule = angular.module('myModule', []);
myModule.factory('serviceId', function() {
    var shinyNewServiceInstance;
    //factory function body that constructs shinyNewServiceInstance
    return shinyNewServiceInstance;
});


如何使用 AngularJS 的 $provide 服务:


angular.module('myModule', [], function($provide) {
    $provide.factory('serviceId', function() {
        var shinyNewServiceInstance;
        //factory function body that constructs shinyNewServiceInstance
        return shinyNewServiceInstance;
    });
});


在 AngularJS框架 中 $的命名约定

AngularJS框架中的前缀 $ 是表示自己提供的服务的名称,比如 $scope 或 $provide 等,是为了防止命名间冲突,最好避免命名自己开发的服务以为$开头。


假设你检查一个作用域 scope的内部,你可能也会发现一些以 $ 开头的属性 。而这些特性都是被认为是私有的,并且它是不应该能访问或修改的。


下面这个代码是将$window注入到自己的服务中:


angular.module('myModule', [], function($provide) {
    $provide.factory('notify', ['$window', function(win) {
        var msgs = [];
        return function(msg) {
            msgs.push(msg);
            if (msgs.length == 3) {
                win.alert(msgs.join("\n"));
                msgs = [];
            }
        };
    }]);
});

我们看到这是一个通知服务,会将消息发送到所有 AngularJS 提供的window窗口中去显示它。


需要注意的是所有 AngularJS 服务都是单例的。这意味着在每一个注入器中都只有一个需要的服务的实例。因为 AngularJS 极度讨厌全局的东西,这是符合面向对象OO。


我们通常会去使用压缩简写对javascript进行缩小,使得更小的文件能有更快下载速度,压缩简写 minfy 可能对 AngularJS 注入有一定的影响。


所以需要用到以下办法:

function PhoneListCtrl($scope, $http) {...}
PhoneListCtrl.$inject = ['$scope', '$http'];
phonecatApp.controller('PhoneListCtrl', PhoneListCtrl);


使用 PhoneListCtrl.$inject 主动注入两个依赖。或者用括号符号[ ]

function PhoneListCtrl($scope, $http) {...}
phonecatApp.controller('PhoneListCtrl', ['$scope', '$http', PhoneListCtrl]);


这样之前案例控制器代码就写成了:

var phonecatApp = angular.module('phonecatApp', []);
 
phonecatApp.controller('PhoneListCtrl', ['$scope', '$http',
    function ($scope, $http) {
        $http.get('phones/phones.json').success(function(data) {
            $scope.phones = data;
    });
 
    $scope.orderProp = 'age';
}]);


AngularJS框架 的 $resource服务与 REST服务

$resource它是一个依赖 $Http 服务的组件,同时它创建了一个资源对象,能够让你与RESTful服务器端数据源实现交互的工厂。它返回的是资源对象,提供了高层次的行为,而不需要与低级别$ HTTP服务交互操作方法。


它需要ngResource 安装(<script src="lib/angular/angular-resource.js"></script>)。$resource服务具体用法见它的 API文档


$resource服务简单用法如下图所示:


angularjs的resource服务简单用代码


上图代码对返回的数据进行如下默认的操作:


{ 
    'get': {method:'GET'},
    'save': {method:'POST'},
    'query': {method:'GET', isArray:true},
    'remove': {method:'DELETE'},
    'delete': {method:'DELETE'}
};


例如:

var User = $resource('/user/:userId', {userId:'@id'});

var user = User.get({userId:123}, function() {
    user.abc = true;
    user.$save();
});


这里的 User 定义为资源 $resource 类型,小写的 user 则是其中的一个实例,实际上是从服务器抓取的根据id为123的一个User Json数组,那么下面就可以对 user 这个实例使用上面几个默认的操作,比如user.$save();,都可以轻松地执行CRUD操作(创建,读取,更新,删除)。


根据以上概念我们可以总结为:

var object = Data.get({id:123}, function() {
    object.isDefault = true;
    object.$save();
});


下面我们以phone举例返回列表:

var phonecatServices = angular.module('phonecatServices', ['ngResource']);
 
phonecatServices.factory('Phone', ['$resource',
    function($resource){
        return $resource('phones/:phoneId.json', {}, {
            query: {method:'GET', params:{phoneId:'phones'}, isArray:true}
        });
    }
]);

我们看到这是从后台返回的phone列表的$resource用法,它节省了 $http 之类转换。


下篇文章我们将依托前几篇文章来学习一个 AngularJS 完整进阶 phone 例子。

上一篇 下一篇

相关文章