Posts List

AngularJS中的依赖注入

本文译自https://docs.angularjs.org/guide/di 依赖注入 依赖注入(DI)是一种解决组件如何获取其依赖这一问题的设计模式。 Angular injector子系统负责创建组件,解决它们的依赖,并按要求将它们提供给其他组件。 如果想深入了解DI,可以查看依赖注入的Wikipedia以及Martin Fowler的这篇Inversion of Control。 DI in a Nutshell 一个组件要想获取它的依赖可以通过以下三种途径: 直接创建这个依赖的一个实例,通常使用new操作符 通过全局变量查找已经被创建的依赖 依赖可以作为参数传递给需要它的组件 前两种获取依赖的方式并不理想,因为这会导致依赖关系被硬编码进代码中。这会导致更新依赖关系变得很复杂。在测试时尤其如此,我们通常会提供mock过的依赖关系以隔离不同组件间的测试,硬编码会导致每次测试都需要修改相关的源码。 相比之下,第三种方式是最可行的,因为它将解决依赖关系的责任从组件中移除。依赖只是简单的作为参数传递给组件: function SomeClass(greeter) { this.greeter = greeter; } SomeClass.prototype.doSomething = function(name) { this.greeter.greet(name); } 在上边的例子中,SomeClass并不关心如何创建或找到greeter,它只是在初始化时从参数列表里取出需要的依赖即可。 但这样做的问题是,寻找/创建依赖的责任被转交给了调用SomeClass的代码。 为了统一管理依赖关系,每个Angular应用都有一个injector。injector是一个服务定位器,它负责查找或创建依赖。 下面是一个使用injector服务的例子: // Provide the wiring information in a module var myModule = angular.module('myModule', []); 下面的代码告诉injector如何创建greeter服务。需要注意的是greeter又依赖于$window服务。greeter实际上是一个包含greet方法的对象。 myModule.factory('greeter', function($window) { return { greet: function(text) { $window.alert(text); } }; }); 下面的代码演示了如何创建一个injector并通过它来请求greeter服务:

AngularJS源码阅读之ngModel

数据的双向绑定可能是Angular最为人们熟知的特性之一。举个最简单的例子 可以在result页面中看到,每当在input中输入时,$scope中对应model的值也改变了。反之,当用户点击Set按钮在$scope中更新了model的值时,input输入框中的内容也对应更新了。 所有的这些魔法只需要我们在input元素上指定一个ng-model属性。可见ng-model这个directive是双向绑定这一特性不可缺少一点。本文就继续从源代码入手看看ngModel的实现方式以及其它directive是如何与ngModel交互的。 ngModel被定义在input.js。从名字上可看出,该文件还定义input这个directive,从这点也可以看出ngModel与input之间的紧密关系。 注: 文中所有涉及到的AngularJS源码均来自angular-1.2.8版本。 var ngModelDirective = function() { return { require: ['ngModel', '^?form'], controller: NgModelController, link: function(scope, element, attr, ctrls) { // notify others, especially parent forms var modelCtrl = ctrls[0], formCtrl = ctrls[1] || nullFormCtrl; formCtrl.$addControl(modelCtrl); scope.$on('$destroy', function() { formCtrl.$removeControl(modelCtrl); }); } }; }; 可以看出ngModel依赖于ngModelController以及可选的formController。ngModel在link方法中只做了2件事: 如果声明了ngModel的元素出现在一个form中,那么就向上层form注册自身 注册了$destroy事件的监听器,该事件触发时告知上层form移除对自身的引用 link方法如此简单以至于我们还没看到关于数据绑定的任何信息。看来问题的答案都藏在ngModelController中了。接下来看看ngModelController中都发生了什么。 var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$parse', function($scope, $exceptionHandler, $attr, $element, $parse) { this.

Angular中的Controller

在Angular中,一个Controller是一个JavaScript构造函数。当一个Controller通过ng-controller附加到某个DOM元素上时,Angular会使用相应Controller的构造函数来初始化一个新的Controller对象。一个新的child scope会被创建并作为可注入的参数(就是我们看到的$scope)传递给该Controller的构造函数。 使用Controller: 为$scope对象设置初始状态 为$scope对象添加行为 不要使用Controller: 修改DOM —— Controllers应该只负责业务逻辑。将任何UI的逻辑放进Controller都会极大地破坏其可测试性。Angular中可以使用数据绑定用来更新页面中的数据或使用directive来封装DOM操作。 格式化input数据 —— 使用angular form controls 过滤输出数据 —— 使用Angular中的filter机制 跨Controller之间的代码或状态共享 —— 使用Angular中的service机制 管理其他组件的生命周期

AngularJS源码阅读之ngRepeat

注: 文中所有涉及到的AngularJS源码均来自angular-1.2.8版本。 注2: 本文结构为一段代码配一段说明,代码在上,说明在下。 从3月3号到现在,进入新公司也有1个月了。过去这一个月里主要负责公司内部某个DevOps工具开发过程中的前端部分。由于涉及到较多的网页交互,该项目的前端部分使用了AngularJS这个框架。本人这正好借助这个机会进一步了解了AngularJS的相关知识。 这两天项目主体完成,准备上线,算是有了一些自由时间,正好借此机会学习一下AngularJS内部的实现机制。 本篇来说说最常见的directive之一 ngRepeat 该文件的整体结构如下: var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) { var NG_REMOVED = '$$NG_REMOVED'; var ngRepeatMinErr = minErr('ngRepeat'); return { transclude: 'element', priority: 1000, terminal: true, $$tlb: true, link: function($scope, $element, $attr, ctrl, $transclude){ ..... } }; function getBlockStart(block) { return block.clone[0]; } function getBlockEnd(block) { return block.clone[block.clone.length - 1]; } }]; ngRepeatDirective是一个数组,它会被传递给directive方法用来生成ngRepeat这个directive。可以看到ngRepeat依赖于$parse和$animate两个服务。$parse用来将字符串解析成javascript函数。$animate用来给DOM改变附加上动画效果。文件最后声明了两个helper方法,会在稍后的分析中介绍到。 接下来把最大的篇幅交给这个directive的核心——link方法。 var expression = $attr.ngRepeat; var match = expression.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?\s*$/), trackByExp, trackByExpGetter, trackByIdExpFn, trackByIdArrayFn, trackByIdObjFn, lhs, rhs, valueIdentifier, keyIdentifier, hashFnLocals = {$id: hashKey}; if (!

AngularJS Directive之compile与link

刚开始学习 AngularJS Directive 时,总是不太能搞明白 compile 和 link 两个属性的含义和区别。最近写的多一些,读的资料也多了一些,渐渐有了一些理解,简单记录下来: compile 属性是一个函数。该函数会针对每一个 directive 的实例执行一次,在 compile 阶段: 不能访问 scope 元素还没有被插入 document 可以修改 template,最终结果会被 angular 缓存 link 属性可以有两种配置方式。 link 属性可以被设置为一个对象,包含 pre, post 两个属性,分别对应一个函数(preLink, postLink)。preList,postLink 的区别在于执行顺序。preLink 是自顶向下的,先父节点,后子节点,postLink 正好相反。 .directive{ return { link: { pre: function preLink(){ ... }, post: function postLink() { ... } } } } link 属性也可以被直接设置为一个函数,则默认相当于设置了 postLink 函数。 .directive{ return { link: function link(){ ... } } } 等于