AngularJS 学习之旅(一)

前言:出来工作也快一年了,给我的感觉是学到的新东西并不是很多。不过,最近由于云盘前端需要重新改版,部门请来了一位前端大牛压阵。所以有幸认识了AngularJS,跟着大牛的脚步走进AngularJS的世界痛并快乐着….

一、简介

AngularJS是谷歌是Google推出的开源JavaScript MV*(MVW、MVVM、MVC)结构性的动态WEB程序应用框架。它允许你使用HTML模板语言,允许您扩展HTML语法,清晰、简洁地表达您的应用程序的组件。AngularJS的数据绑定和依赖注入减少了大部分你当前不得不写的代码,而且这一切都运行在浏览器内,使它能成为与任何服务器技术的理想合作伙伴。

并不是所有的WEB APP都适合使用AngularJS,AngularJS最适合CRUD(CREATE READ UPDATE DELETE)类型的WEB APP。像GamesGUI editors这一不同于CRUD类型的app,它们更适合使用一些抽象层级较低的库,如JQuery好吧,关于AngularJS的历史我觉得只要记住三点就行:谷歌的、开源的、适合CRUD类型的APP。

二、AngularJS 一些概念

下面列举一些AngularJS的概念

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Template:使用了额外标记的HTML  
Directives:使用自定义属性和元素扩展的HTML   
Model:展示给用户和与用户进行交互的数据  
Scope:model存数的上下文,以便controllers, directives and expressions能够访问       
Expressions:能从Scope里访问的变量和函数    
Compiler:解析模板和实例化指令和表达式  
Filter:格式化表达式
View:用户能看到的的数据(the DOM)
Data Binding:model和view之间动态的数据
Controller:展示视图(view)之前的业务逻辑处理   
Dependency Injection:创建和连接对象或函数  
Injector:依赖注入的容器  
Module: 注入器的配置  
Service:可重用业务逻辑独立的观点    

三、核心内容(core concepts)

1 Templates

AngularJS的应用程序中,您将用数据填充页面模板的工作从服务器到客户机。结果是一个系统更好的结构化动态页面更新。下面是您将使用的核心特性。

1.1 Data binding

AngularJS中,Data-binding是指在model和view组件间动态同步的数据。这意味着在应用程序里使用AngularJSData-binding可以让你像single-source-of-truth一样操作model。当model内容改变时,view也跟着变化,反之亦然。

Data Binding in Angular Templates

One_Way_Data_Binding

大部分的templating系统仅仅是单向绑定数据,它们把template和model组件合并到一起,并在一个view里边展示。在合并发生之后,model或者关联view代码的改变不会自动的反映在view上。更糟糕的是任何用户在view组件上的改变都不会在model组件上反映。这就意味着开发者不得不编写代码来不断同步model及view的数据。

Data Binding in Angular Templates

Two_Way_Data_Binding

AngularJs的template工作模式不一样,首先template(未解析的、添加了标记(markup)和指令(directives)的HTML文件)在浏览器中解析的,解析的结果产生了视图(view),view中的任何改变都会立刻反射到model上,而model的任何改变也会立刻呈现在view上。这大大简化开发人员的编程model.由于view仅仅是model的数据渲染,所以控制器(controoler)完全从view中分离。所以,这就是的测试Controller变的很简单,因为不需要关联view。

1.2 Angular 表达式(Expressions)

Angular expressions 是类似Javascript的代码段通常用于绑定,如:{{ expression }}。例如:下面是有效的Angular expressions

 1+2  
 a+b   
 user.name   
 items.[index]  

Javascript expressions相比,Angular expressions有以下不同点:

1. Conext(上下文):在JavaScript expressions的值相对于全局的window(窗口),而在Angular中,expression得值相对了一个scope(作用域)对象。   
2. forgiving(宽恕):在JavaScript中求表达式值时若未定义的属性会发生“ReferenceError”或“TypeError”。而在Angular 中,express是被宽容的设置为“undefined”和“null”。
3. 没有控制流语句:在AngularJS中你不需要使用这些表达式:conditionals,loops或者exceptions。
4. Filters:你可以在展示expressions之前使用“filters”来格式化数据。

如果你想运行更复杂的JavaScript代码,你应该把它作为一个Controller方法在view中调用。如果你自己想eval()一个Angular expression,那么就使用 $eval()方法。

不像Javascript,默认定义的是全局的window属性,Angular expressions必须使用$window明确的指出为全局window对象。例如:假如你想在一个expression中调alert()函数,你必须用$window.alert()。这个限制是有意的,因为它能阻止意外的访问全局状态。

1.3 Directives(指令)

1.3.1 what are Directives

处于更高水准的,Directives是指那些在DOM元素(例如一个属性,元素名称,css class)上的标记,用来告诉AngularJSHTML compiler($compile解析器)给DOM元素附加一个特殊的行为或改变DOM元素及子元素。Angular 内置了一系列的diresctives,如:

ngBind,ngModel,和ngView

What does it mean to "compile" an HTML template? For AngularJS, "compilation" 
means attaching event listeners to the HTML to make it interactive. The reason we   
use the term "compile" is that the recursive process of attaching directives      
mirrors the process of compiling source code in compiled programming languages.         
1.3.2 Matching Directives(匹配指令)

在我们编写一个directive之前,我们需要了解AngularHTML解析器处理执行指令的过程。在一下的列子中,我们称<input>元素匹配ngModel指令:

1
<input ng-model="foo">

下面也一样能匹配ngModel:

1
<input data-ng:model="foo">

Angular规范的命名了每一个元素的的标签和属性从而使得元素能和directives想匹配。通常要求指令是一些区分大小写、规范化的名字(如:ngModel)。然而,由于HTML是不区分大小写的,我们将指令在DOM中定为小写形式,通常使用dash-delimited的属性写在DOM元素(如ng-model)。标准化的处理如下:

1
2
Strip x- and data- from the front of the element/attributes.    
Convert the :, -, or _-delimited name to camelCase.    

以下绑定效果是一样的(Demo演示):

<div ng-controller="Controller">
  Hello <input ng-model='name'> <hr/>
  <span ng-bind="name"></span> <br/>
  <span ng:bind="name"></span> <br/>
  <span ng_bind="name"></span> <br/>
  <span data-ng-bind="name"></span> <br/>
  <span x-ng-bind="name"></span> <br/>
</div>    

小贴士:

Best Practice: Prefer using the dash-delimited format (e.g. ng-bind for ngBind).   
If you want to use an HTML validating tool, you can instead use the data-prefixed    
version (e.g. data-ng-bind for ngBind). The other forms shown above are accepted     
for legacy reasons but we advise you to avoid them.

基于元素的名称、属性名、类名$compile可以匹配directives.所有Angular提供的directives都能匹配属性名、标签名、comments或class名。

Text and attribute binding(文本及属性绑定)

编译器在编译过程中匹配文本和属性并使用$interpolate判断是否包含嵌入式表达式。如:

1
<a ng-href="img/.jpg">Hello !</a>
1.2.4 ngAttr 属性绑定

Web浏览器对它们认为是正确的属性值有时候是很挑剔的。如:

    <svg>
      <circle cx=""></circle>
    </svg>

我们希望Angular能够正常绑定,但是当我们检查浏览器控制台时候我们会发现错误Rrrpr:Invalid value for attribute cx=。因为SVG DOM API的严格性,你不能简单的写成 cx=。采用ng-attr-cx你可以解决这个问题。也就是说如果一个属性的绑定前缀是ngAttr,那么在绑定时将被应用到相应的无前缀的属性:

 <svg>
  <circle ng-attr-cx=""></circle>
</svg> 
1.2.5 create directives

首先,让我们了解一下API for registering directives,像controller一样,directives也是用modules注册。注册directive详情见module.directive API
关于directive命名:

Best Practice: In order to avoid collisions with some future standard, it's best    
to prefix your own directive names. For instance, if you created a <carousel>    
directive, it would be problematic if HTML7 introduced the same element. A two or     
three letter prefix (e.g. btfCarousel) works well. Similarly, do not prefix your    
owndirectives with ng or they might conflict with directives included in a future      
version of Angular. 

如下directive:

1
2
3
4
5
6
7
8
9
10
11
12
13
angular.module('docsRestrictDirective', [])
  .controller('Controller', ['$scope', function($scope) {
    $scope.customer = {
      name: 'Naomi',
      address: '1600 Amphitheatre'
    };
  }])
  .directive('myCustomer', function() {
    return {
      restrict: 'E',
      templateUrl: 'my-customer.html'
    };
  });

restrict的可选参数有:

1
2
3
'A' - only matches attribute name
'E' - only matches element name
'C' - only matches class name

这些限制条件可以被组合使用:如 ‘AEC-matches either attribute or element or class name’。默认为’A’。

1.2.6 creating a directive that Manipulates the DOM

下面一个例子是展示当前时间,并且每过一秒钟,DOM时间也跟着更新 Demo
directive若要修改DOM则需要使用link选项。link能接收一个有一下参数的function,function link(scope,element,attrs){...}:

  • scope:一个Angular作用域对象
  • element:directive 匹配的jqLite-wrapped元素
  • attrs:是一个散列对象的键值对,有着规范化属性名称及其对应的属性值

1.2.7 Filters

filter是用来格式化一个表达式的值的。他们能在view templatecontrollerservice中使用,你能很方便的定义自己的filter

(1) 在view templates中使用filter

Filters能在view tmeplate中的表达式中使用,语法如下:

比如:标记12 表示使用currencyfilter 格式化数字12为货币形式。结果为:$12.00。
Filters 能使用一系列的filter,这称为“chaining(链)”,语法如下:

Filters也可以携带参数,语法如下:

例如:标记1234表示数字1234使用number过滤器过滤保留2位小数点,结果为:1,234.00。

(2)在controller,services,和directives中使用filters

在controller,services,和directives中使用filters你需要给他们注入依赖<filterName>Filter。例如,使用依赖numberFilter会注入number过滤器。见demo

(3)创建自定义filters

编写自己的filter很简单,仅仅需要在module中注册一个新的filter factory函数。在内部,使用了filterProviderfactory函数需要一个新的filter函数,该函数的第一个参数为需要过滤的表达式的值。见demo

表单(Forms)

通常,inputselecttextarea等控件是用来给用户输入数据的。而Forms是对这些控件的一种集合。AngularFormcontrols(控件)提供了数据验证服务,所以用户可以避免无效输入。这些功能能给用户更好的体验。

(1)simple form(简单的表单)

理解双向绑定(data-binding)的关键指令(directive)是ngModelngModel指令提供的双向绑定功能能使modelview数据自动同步。另外,它提供了一个API给其他的directive来增强它的功能。见demo

    <div ng-controller="Controller">
      <form novalidate class="simple-form">
        Name: <input type="text" ng-model="user.name" /><br />
        E-mail: <input type="email" ng-model="user.email" /><br />
        Gender: <input type="radio" ng-model="user.gender" value="male" />male
        <input type="radio" ng-model="user.gender" value="female" />female<br />
        <button ng-click="reset()">RESET</button>
        <button ng-click="update(user)">SAVE</button>
      </form>
      <pre>form = </pre>
      <pre>master = </pre>
    </div>

    <script>
      function Controller($scope) {
        $scope.master = {};

        $scope.update = function(user) {
          $scope.master = angular.copy(user);
        };

        $scope.reset = function() {
          $scope.user = angular.copy($scope.master);
        };

        $scope.reset();
      }
    </script>  

注意novalidate是用来禁用浏览器的表单验证。

(2)使用CSS classes

为了想控件一样定义样式,ngModel增加了一下CSS classes:

  • ng-valid
  • ng-invalid
  • ng-pristine
  • ng-dirty

其效果见demo

(3) 绑定表单及控制状态

一个form是一个FormController的实例,form实例的name属性可以请求到scope中。类似的,一个input含有ngModel的控件会拥有一个NgModelController的实例。这允许我们扩展一些特性如:

  • RESET button is enabled only if form has some changes
  • SAVE button is enabled only if form has some changes and is – valid
  • custom error messages for user.email and user.agree-

(4) custom tirggers

默认地,任何的内容改变都会触发model的更新和form的验证。您可以使用ngModelOptions指令覆盖此行为将只绑定到指定的事件列表。如:ng-model-option="{updateOn:"blur"}"当控件失去焦点时候会进行更新和验证。你还可以设置几个事件使用空格分隔如:

`ng-model-options="{updateOn:"mousedown blur"}"`。

如果你想保留默认的行为和增加新的事件来触发model的更新和验证:

ng-model-options="{ updateOn: 'default blur' }"

(5) 推迟model更新时间

你可以使用ngModelOption指令的key debounce来延迟model的更新和验证时间。这种延迟也会适用于解析器,validatorsmodel的标记为$dirty$pristine。如ng-model-options="{debounce: 500}"表示当内容发生变化后在经过0.5秒才会触发model的更新和form的验证。在特殊环境这能很有效的阻止立刻更新(如:blur事件)

ng-model-options="{updateOn:'default blur',debounce:{default:500,blur:0}}" 

如果是用在一个元素上面,则该原色的全部子元素和控件都会从它那继承,除非子元素是隐藏的。见demo

(6) 自定义验证

Angular 提供了html5常用的输入类型:(text,number,url,email,radio,checkbox),同时也提供了一些验证指令(required,pattern,minlength,maxlength,min,max)。定义你自己的验证可以给ngModel Controller定义你自己的directive的时候增加一个自定义的validation function.两种情况下可能需要自定义验证:

  • Model to View update: 只要绑定的模型发生改变,所有在NgModelController#$formatters的函数数组都是pipe-lined,通过NgModelController#$setValidity这些函数都有机会还改变或者验证控件和表单。
  • View to Model update:同样的方式,当用户和一个控件交互时,它会调用NgModelController#$setViewValue
    英文太烂硬是没看懂,蛋疼! 见demo

文章评论

返回顶部