angularJS概念集

模板(Template) (带有Angular扩展标记的HTML

Angular的模板是一个声明式的视图,它指定信息从模型、控制器变成用户在浏览器上可以看见的视图。 它把一个静态的DOM —— 只包含HTML,CSS以及Angular添加的标记和属性,然后引导Angular为其加上一些行为和格式转换器,最终变成一个动态的DOM。

在Angular中有以下元素属性可以直接在模板中使用:

指令(Directive)(用于通过自定义属性和元素扩展HTML的行为)

指令就是一些附加在HTML元素上的自定义标记(例如:属性,元素,或css类),它告诉AngularJS的HTML编译器($compile) 在元素上附加某些指定的行为,甚至操作DOM、改变DOM元素,以及它的各级子节点。

Angular内置了一整套指令,如ngBindngModel, 和ngView。 就像你可以创建控制器和服务那样,你也可以创建自己的指令来让Angular使用。 当Angular 启动器引导你的应用程序时, HTML编译器就会遍历整个DOM,以匹配DOM元素里的指令。

规范化的过程如下所示:

  1. 从元素或属性的名字前面去掉x- and data-
  2. :-, 或 _分隔的形式转换成小驼峰命名法(camelCase).

模型(Model)(用于显示给用户并且与用户互动的数据

作用域(Scope)(用来存储模型(Model)的语境(context)。模型放在这个语境中才能被控制器、指令和表达式等访问到)

作用域(Scope)

  • 是一个存储应用数据模型的对象
  • 为 表达式 提供了一个执行上下文
  • 作用域的层级结构对应于 DOM 树结构
  • 作用域可以监听 表达式 的变化并传播事件

作用域有什么

  • 作用域提供了 ($watch) 方法监听数据模型的变化

  • 作用域提供了 ($apply) 方法把不是由Angular触发的数据模型的改变引入Angular的控制范围内(如控制器,服务,及Angular事件处理器等)

  • 作用域提供了基于原型链继承其父作用域属性的机制,就算是嵌套于独立的应用组件中的作用域也可以访问共享的数据模型(这个涉及到指令间嵌套时作用域的几种模式)

  • 作用域提供了 表达式 的执行环境,比如像 {{username}} 这个表达式,必须得是在一个拥有属性这个属性的作用域中执行才会有意义,也就是说,作用域中可能会像这样 scope.username 或是 $scope.username,至于有没有 $ 符号,看你是在哪里访问作用域了

作用域作为数据模型使用

作用域是Web应用的控制器和视图之间的粘结剂。在Angular中,最直观的表现是:在自定义指令中,处在模版的 链接(linking) 阶段时, 指令(directive)会设置一个 $watch 函数监听着作用域中各表达式(注:这个过程是隐式的)。这个 $watch 允许指令在作用域中的属性变化时收到通知, 进而让指令能够根据这个改变来对DOM进行重新渲染,以便更新已改变的属性值(注:属性值就是scope对象中的属性,也就是数据模型)。

其实,不止上面所说的指令拥有指向作用域的引用,控制器中也有(注:可以理解为控制器与指令均能引用到与它们相对应的DOM结构所处的作用域)。 但是控制器与指令是相互分离的,而且它们与视图之间也是分离的,这样的分离,或者说耦合度低,可以大大提高对应用进行测试的工作效率。

注:其实可以很简单地理解为有以下两个链条关系:

  • 控制器 --> 作用域 --> 视图(DOM)
  • 指令 --> 作用域 --> 视图(DOM)

表达式(Expression)(模板中可以通过它来访问作用域(Scope)中的变量和函数)

Angular表达式与JavaScript表达式有如下的区别:

  • 属性解析: 所有的属性的解析都是相对于作用域(scope)的,而不像JavaScript中的表达式解析那样是相对于全局'window'对象的。

  • 容错性: 表达式的解析对'undefined'和'null'具有容错性,这不像在JavaScript中,试图解析未定义的属性时会抛出ReferenceErrorTypeError错误.

  • 禁止控制流语句: 表达式中不允许包括下列语句:条件判断(if),循环(for/while),抛出异常(throw)。应用逻辑应该在控制器中,而不是在视图中控制。

过滤器(Filter)(负责格式化表达式(Expression)的值,以便呈现给用户)

过滤器用来格式化表达式中的值。它可以用在视图模板(templates)、控制器(controllers)或者服务(services)中。

过滤器可以应用在视图模板中的表达式中,按如下的格式:{{ 表达式 | 过滤器名 }}

创建自定义过滤器

创建自定义过滤器的过程很简单:仅仅需要在模块中注册一个新的过滤器工厂方法。其中使用了filterProvider。这个工厂方法应该返回一个以输入值为第一个参数的新过滤方法,过滤器中的参数都会作为附加参数传递给它。

视图(View)(用户看到的内容(即DOM))

数据绑定(Data Binding)自动同步模型(Model)中的数据和视图(View)表现)

在Angular网页应用中,数据绑定是数据模型(model)与视图(view)组件的自动同步。Angular的实现方式允许你把应用中的模型看成单一数据源。而视图始终是数据模型的一种展现形式。当模型改变时,视图就能反映这种改变,反之亦然。

控制器(Controller)(视图(View)背后的业务逻辑

在Angular中,控制器就像 JavaScript 中的构造函数一般,是用来增强 Angular作用域(scope) 的。

当一个控制器通过 ng-controller 指令被添加到DOM中时,ng 会调用该控制器的构造函数来生成一个控制器对象,这样,就创建了一个新的子级 作用域(scope)。在这个构造函数中,作用域(scope)会作为$scope参数注入其中,并允许用户代码访问它。

一般情况下,我们使用控制器做两件事:

  • 初始化 $scope 对象
  • 为 $scope 对象添加行为(方法)

正确使用控制器

通常情况下,控制器不应被赋予太多的责任和义务,它只需要负责一个单一视图所需的业务逻辑。

最常见的保持控制器“纯度”的方法是将那些不属于控制器的逻辑都封装到服务(services)中,然后在控制器中通过依赖注入调用相关服务。详见指南中的 依赖注入 服务 这两部分。

注意,下面的场合千万不要用控制器

  • 任何形式的DOM操作:控制器只应该包含业务逻辑。DOM操作则属于应用程序的表现层逻辑操作,向来以测试难度之高闻名于业界。把任何表现层的逻辑放到控制器中将会大大增加业务逻辑的测试难度。ng 提供数据绑定 (数据绑定) 来实现自动化的DOM操作。如果需要手动进行DOM操作,那么最好将表现层的逻辑封装在 指令 中
  • 格式化输入:使用 angular表单控件 代替
  • 过滤输出:使用 angular过滤器 代替
  • 在控制器间复用有状态或无状态的代码:使用angular服务 代替
  • 管理其它部件的生命周期(如手动创建 service 实例)

将控制器与 scope 对象关联

通过两种方法可以实现控制器和 scope 对象的关联:

简单的控制器范例

为了更深入地阐释Angular的控制器是如何工作的,我们用以下几个部件来构建一个小型应用:

  • 一个由两个按钮和一条简单反馈构成的模板
  • 一个名为 spice 的数据模型对象,是一个字符串
  • 一个拥有两个方法的控制器,可以设置spice 的值

模板中的消息包含了一个到数据模型 spice 的绑定,默认值为 very。之后,取决于哪个按钮被点击,spice 的值会被置为 chili 或是 jalapeño ,受益于数据绑定,模板中的这个消息会在 spice 变化时自动更新。

依赖注入(Dependency Injection)(负责创建和自动装载对象或函数)

对象或函数可以通过三种方式获得所依赖的对象(简称依赖):

  1. 创建依赖,通常是通过 new 操作符

  2. 查找依赖,在一个全局的注册表中查阅它

  3. 传入依赖,需要此依赖的地方等待被依赖对象注入进来

前两种方式:创建或是查找依赖都不是那么理想,因为它们都将依赖写死在对象或函数里了。问题在于,想要修改这两种方式获得依赖对象的逻辑是很困难的。尤其是在测试的时候,会遇到很多问题,因为测试时常常需要我们提供所依赖对象的替身(MOCK)。

第三种方式是最理想的,因为它免除了客户代码里定位相应的依赖这个负担,反过来,依赖总是能够很简单地被注入到需要它的组件中。

依赖注释

(译注:此处注释非代码注释,应理解为依赖声明的方法)

推断依赖

最简单的获取依赖的方法是让你的函数的参数名直接使用依赖名。

function MyController($scope, greeter) {...}

$inject 注释

为了让重命名了参数名的压缩版的 JavaScript 代码能够正确地注入相关的依赖服务。函数需要通过 $inject 属性进行标注,这个属性是一个存放需要注入的服务的数组。

var MyController = function(renamed$scope, renamedGreeter) {...}MyController['$inject'] = ['$scope', 'greeter'];

行内注释

有时候用 $inject 注释的方式不方便,比如标注指令的时候(译注:这里标注指令可以理解为告诉指令需要加载哪些服务依赖的说明)。

someModule.factory('greeter', ['$window', function(renamed$window) {...}]);

在 Angular 中,DI 无处不在。通常在控制器和工厂方法(译注:所谓的工厂方法个人理解为 Angular 中的API)中使用较多。

注入器(Injector)(用来实现依赖注入(Injection)的容器)

模块(Module)(用来配置注入器)

大多数应用程序都有个 main 函数来初始化、连接以及启动整个应用。ng 中虽然没有 main 函数,但它用模块来描述应用将如何启动。这种策略有如下几种优势:

  • 整个过程是声明式的,更容易理解
  • 在单元测试中,没有必要加载所有模块,这样有利于单元测试的代码书写
  • 在场景测试中,额外的模块可以被加载进来,进而重写一些配置,这样有助于实现应用的端到端的测试
  • 第三方代码可以很容易被打包成可重用的模块
  • 模块可以用任意顺序或并行顺序加载(得益于模块执行的延迟性)

模块是配置代码块和运行代码块的集合,在启动阶段被执行。最简单的模块中包含下面两种代码块:

  1. 配置代码块 - 在 provider 注册和配置阶段执行(注:provider 是 ng 服务的一种)。只有 provider 和 constant 可以被注入配置代码块。这是为了防止服务在完全配置好之前被意外地初始化。
  2. 执行代码块 - 在 injector 被创建后执行,被用来启动整个应用。只有服务的实例对象以及 constant 可以被注入到执行代码块。这是为了防止在应用执行期间系统的更进一步的配置。

执行代码块

执行代码块是 ng 中最接近 main 函数的一个东西。执行代码块是应用启动时运行的代码。它在所有的服务被配置好以及 注入器(injector) 被创建好之后执行。通常,执行代码块包含的代码都很难进行单元测试,正因为如此,它通常应该被丢在一个单独的模块中,这样我们可以在单元测试时忽略它。

模块依赖

模块声明时可以列出它所需要依赖的其它模块。一个模块依赖某模块,意味着这个被依赖的模块需要在模块被加载之前加载完毕。更具体些,假设模块A依赖于模块B,那么模块A的配置代码块的执行,必须发生在模块B的配置代码块之后;模块A的执行代码块亦同理,也在模块B的执行代码块之后被执行。每个模块只能被加载一次,即使有多个别的模块依赖它。

异步加载

模块是一种管理 $injector 配置的方式,它和将脚本加载到JavaScript虚拟机(VM)没有半毛钱关系。现在已经有很多项目用来在 ng 中处理脚本的动态加载。由于模块在加载期什么都不做,所以它们可以按任何顺序载入到虚拟机中,脚本加载器也可以充分利用这一特性来并行地加载模块和脚本。

服务(Service)(独立于视图(View)的、可复用的业务逻辑)

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值