angular学习笔记

angular framework 知识点 开发实践 

angularJS 主要 须要掌握下面几个基本点 进行平常的开发是没有问题的
 
第一个:理解 angularjs 中的数据绑定
第二个:理解angularjs 的模块和模块的加载机制
第三个:知道 angularjs中的做用域 $scope
第四个:理解 angularjs 中的控制器
第五个:熟悉 angularjs中的过滤器,表达式
第六个:理解angularjs中的指令包括 内置指令 和自定义的指令中的10来个参数的含义
第七个:理解angularjs中的视图和路由
第八个:理解angularjs中的依赖注入
第九个:理解angularjs中的服务 包括熟悉一些内置的服务和 自定义的服务
第十个:理解angularjs中的事件
 
若是想用angularjs进行开发至少都须要掌握上面的10点 仍是有点多的 ,angularjs 后面还有一些本地化 安全性,测试,缓存一些技术 ,若是是初学者我其实并不建议一上来就学习 angularjs 能够先看一些简单的MVC框架 例如 backbone.js
  
其实 angularjs我的理解是把java的那套东东搬到前端来了,之后这种angularjs的项目应该是作一些内部系统比较多,由于和java同样方便搭积木。
下面咱们就来分别说一说angularjs中的这10点
 

第一点: 理解 angularjs 中的数据绑定

 
数据绑定这个东西 最先我接触到的是从asp.net开始的那个时候的asp.net 网页每一个VIEW 其实都至关于一个模板,当时这样作就是但愿 控制器逻辑和页面分离开来,这样不会显得整个工程杂乱无章 ,到后来 jquery的兴起,也有一些很优秀的前端模板开始出现 例如jquery-templ 这些基于jquery的模板插件,若是你作过一些 后端的前端开发的化应该很快能够理解 angularjs 中的数据绑定
 
HTML (VIEW) JS(controller)
[javascript]  view plain  copy
 
  1. <div ng-controller="MyController">  
  2.          <span style="white-space:pre">   </span><h1>来自controller scope My:{{name}}</h1>  
  3.  </div>  
  4. <h1>来自 rootscope {{name}}</h1>  
[javascript]  view plain  copy
 
  1. testapp.controller('MyController',function($scope, $parse){  
  2.    $scope.name = "ARI";  
  3.   });  
 
运行后的效果
 
其实这里就是一个简单的 数据绑定 当我随意修改 controller 中name 的值的时候  相应的视图中的 显示也会变化
 

第二个:理解angularjs 的模块和模块的加载机制

 
熟悉java的同窗 都知道 java 中有不少的 包 不一样的包 有着不一样的功能,由于javascript中的一些很差的东西 ,也不叫很差,就是在通常的场景下有些不方便的东西例如全局命名空间,angularjs 中的模块 能够保持命名空间的清洁,能够从多个地方复用咱们的模块代码
 
[javascript]  view plain  copy
 
  1. <span style="color:#330033;">angular.module('myApp', []);  
  2. //这就是 angular 中定义模块的 方式  
  3. 这个方法接受两个参数,第一个是名字name,这里 name = myAPP 第二个参数是一个数组,这个里面存放的是其余模块的名字myApp这个模块 依赖这些模块</span>  
那么问题来了 这些模块应该怎么加载呢,他们之间顺序变化有问题吗? 这里就涉及到angularjs 中的模块加载机制
咱们先看一个小例子理解一下 
[javascript]  view plain  copy
 
  1. <span style="color:#330033;">function SomeClass(greeter) {  
  2. this.greeter = greeter;  
  3. }  
  4. SomeClass.prototype.greetName = function(name) {  
  5. this.greeter.greet(name);  
  6. };</span>  

SomeClass可以在运行时访问到内部的greeter,但它并不关心如何得到对greeter的引用。
为了得到对greeter实例的引用,SomeClass的建立者会负责构造其依赖关系并传递进去。

angularjs 经过一个 $injector 的一个组件 来去实现各类状况下模块的依赖和注入
 

下面咱们来简单看一下 angularjs 的 $injector api

第一个方法 annotate()
annotate()方法的返回值是一个由服务名称组成的数组,这些服务会在实例化时被注入到目
标函数中。annotate()方法能够帮助$injector判断哪些服务会在函数被调用时注入进去。
annotate()方法能够接受一个参
 
参数fn能够是一个函数,也能够是一个数组。annotate()方法返回一个数组,数组元素的
值是在调用时被注入到目标函数中的服务的名称。

[javascript]  view plain  copy
 
  1. <span style="color:#330033;">var injector = angular.injector(['ng', 'myApp']);  
  2. injector.annotate(function($q, greeter) {});  
  3. // ['$q', 'greeter']</span>  

第二个 get() 方法
get()方法返回一个服务的实例,能够接受一个参数
参数name是想要获取的实例的名称
get()根据名称返回服务的一个实例
 
第三个方法 has()
has()方法返回一个布尔值,在$injector可以从本身的注册列表中找到对应的服务时返回
true,不然返回false。它能接受一个参数:
参数name是咱们想在注入器的注册列表中查询的服务名称
 
第四个方法 instantiate()
instantiate()方法能够建立某个JavaScript类型的实例。它会经过new操做符调用构造函数,
并将全部参数都传递给构造函数。它能够接受两个参数:
 
type(函数) locals(对象,可选)
instantiate()方法返回Type的一个新实例
 
第五个方法 invoke()

invoke()方法会调用方法并从$injector中添加方法参数。
invoke()方法接受三个参数。
fn(function)
这个函数就是要调用的函数。这个函数的参数由函数声明设置。
self (object-可选)
self参数容许咱们设置调用方法的this参数。
locals (object-可选)
这个可选参数提供另外一种方式在函数被调用时传递参数名给该函数。
invoke()方法返回fn函数返回的值
 
上面介绍的 都是文档里面描述的 你们只要了解一下就好了
 

第三点 angularjs 中的做用域

 
首先应用的做用域是和应用的数据模型相关联的,同时做用域也是表达式执行的上下文。$scope
对象是定义应用业务逻辑、控制器方法和视图属性的地方
 
做用域是视图和控制器之间的胶水。在应用将视图渲染并呈献给用户以前,视图中的模板会
和做用域进行链接,而后应用会对DOM进行设置以便将属性变化通知给AngularJS
 
做用域是应用状态的基础。基于动态绑定,咱们能够依赖视图在修改数据时马上更新$scope,
也能够依赖$scope在其发生变化时马上从新渲染视图
 
AngularJS将$scope设计成和DOM相似的结构,所以$scope能够进行嵌套,也就是说咱们可
以引用父级$scope中的属性
 
做用域有如下的基本功能:
 
提供观察者以监视数据模型的变化;

能够将数据模型的变化通知给整个应用,甚至是系统外的组件;

能够进行嵌套,隔离业务功能和数据;

给表达式提供运算时所需的执行环境。
 

$scope 的生命周期:

 
建立
在建立控制器或指令时,AngularJS会用$injector建立一个新的做用域,并在这个新建的控
制器或指令运行时将做用域传递进去。
 
连接
当Angular开始运行时,全部的$scope对象都会附加或者连接到视图中。全部建立$scope对
象的函数也会将自身附加到视图中。这些做用域将会注册当Angular应用上下文中发生变化时需
要运行的函数。
 
更新
当事件循环运行时,它一般执行在顶层$scope对象上(被称做$rootScope),每一个子做用域
都执行本身的脏值检测。每一个监控函数都会检查变化。若是检测到任意变化,$scope对象就会触
发指定的回调函数。
 
销毁
当一个$scope在视图中再也不须要时,这个做用域将会清理和销毁本身。
尽管永远不会须要清理做用域(由于Angular会为你处理),可是知道是谁建立了这个做用域
仍是有用的,由于你可使用这个$scope上叫作$destory()的方法来清理这个做用域。
 

第四个:理解 angularjs 中的控制器

 
控制器在AngularJS中的做用是加强视图
[javascript]  view plain  copy
 
  1. <span style="color:#330033;">var app = angular.module('app', []);  
  2. app.controller('FirstController', function($scope) {  
  3. $scope.message = "hello";  
  4. });</span>  

设计良好的应用会将复杂的逻辑放到指令和服务中。经过使用指令和服务,咱们能够将控制
器重构成一个轻量且更易维护的形式:
 

第五个:熟悉 angularjs中的过滤器,表达式

 
表达式在AngularJS应用中被普遍使用,所以深刻理解AngularJS如何使用并运算表达式是非
常重要的。
前面已经见过使用表达式的示例。用{{ }}符号将一个变量绑定到$scope上的写法本质上就
是一个表达式:{{ expression }}。当用$watch进行监听时,AngularJS会对表达式或函数进行
运算。
表达式和eval(javascript)很是类似,可是因为表达式由AngularJS来处理,它们有如下显
著不一样的特性:
        全部的表达式都在其所属的做用域内部执行,并有访问本地$scope的权限;
        若是表达式发生了TypeError和ReferenceError并不会抛出异常;
       不容许使用任何流程控制功能(条件控制,例如if/eles);
       能够接受过滤器和过滤器链。
       对表达式进行的任何操做,都会在其所属的做用域内部执行,所以能够在表达式内部调用那
           些限制在此做用域内的变量,并进行循环、函数调用、将变量应用到数学表达式中等操做。
 

过滤器用来格式化须要展现给用户的数据。AngularJS有不少实用的内置过滤器,同时也提
供了方便的途径能够本身建立过滤器。
 
过滤器本质上是一个会把咱们输入的内容看成参数传入进去的函数。上面这个例子中,咱们
在调用过滤器时简单地把input看成字符串来处理。
 
[javascript]  view plain  copy
 
  1. <span style="color:#330033;">angular.module('myApp.filters', [])  
  2. .filter('capitalize', function() {  
  3. return function(input) {  
  4. // input是咱们传入的字符串  
  5. if (input) {  
  6. return input[0].toUpperCase() + input.slice(1);  
  7. }  
  8. });</span>  


第六个:理解angularjs中的指令

 
我的认为 要搞清楚指令 把这个 弄透就完事了
第六个:理解angularjs中的指令
[javascript]  view plain  copy
 
  1. <span style="color:#330033;">angular.module('myApp', [])  
  2. .directive('myDirective', function() {  
  3. return {  
  4. restrict: String,  
  5. priority: Number,  
  6. terminal: Boolean,  
  7. template: String or Template Function:  
  8. function(tElement, tAttrs) (...},  
  9. templateUrl: String,  
  10. replace: Boolean or String,  
  11. scope: Boolean or Object,  
  12. transclude: Boolean,  
  13. controller: String or  
  14. function(scope, element, attrs, transclude, otherInjectables) { ... },  
  15. controllerAs: String,  
  16. require: String,  
  17. link: function(scope, iElement, iAttrs) { ... },  
  18. compile: // 返回一个对象或链接函数,以下所示:  
  19. function(tElement, tAttrs, transclude) {  
  20. return {  
  21. pre: function(scope, iElement, iAttrs, controller) { ... },  
  22. post: function(scope, iElement, iAttrs, controller) { ... }  
  23. }  
  24. // 或者  
  25. return function postLink(...) { ... }  
  26.              }  
  27.         };  
  28. });</span>  
固然 还忘记了一点  angularjs 中还有许多内置的指令 这些也必须了解一下
点   这里  能够查看详细的 内置 指令文档 里面讲的很清楚
 
很久没更新了 来 咱们继续!
 
下面咱们来一一介绍一下 
第一个 :restrict
他限制directive为指定的声明方式 能够经过四种方式进行声明

E: element A:attribute C:class  M: anotationjavascript

默认是经过 属性的方式 

priority : 优先级html

当有多个directive定义在同一个DOM元素时,有时须要明确它们的执行顺序。这属性用于在directive的compile function调用以前进行排序。若是优先级相同,则执行顺序是不肯定的(经初步试验,优先级高的先执行,同级时按照相似栈的“后绑定先执行”。前端

terminal :java

 若是设置为”true”,则表示当前的priority将会成为最后一组执行的directive。任何directive与当前的优先级相同的话,他们依然会执行,但顺序是不肯定的(虽然顺序不肯定,但基本上与priority的顺序一致。当前优先级执行完毕后,更低优先级的将不会再执行)。jquery

template:git

若是replace 为true,则将模版内容替换当前的HTML元素,并将原来元素的属性、class一并迁移;若是为false,则将模版元素看成当前元素的子元素处理。angularjs

一个能够接受两个参数的函数,参数为tElement和tAttrs,并返回一个表明模板的字符串。tElement和tAttrs中的t表明template,是相对于instance的。github

 

[javascript]  view plain  copy
 
  1. <span style="color:#330033;"> angular.module('app',[])  
  2.     .directive('myDirective', function () {  
  3.             return {   
  4.                 restrict: 'E',   
  5.                 template: '<a href="http://www.baidu.com">百度</a>'   
  6.             };  
  7.         })</span>  
[javascript]  view plain  copy
 
  1. <span style="color:#330033;">  HtmlCode:  
  2.  <my-directive></my-directive></span>  
[javascript]  view plain  copy
 
  1. <span style="color:#330033;">angular.module('app',[])  
  2.     .directive('myDirective', function () {  
  3.             return {   
  4.                 restrict: 'EAC',   
  5.                 template: function (elem, attr) {  
  6.                     return "<a href='" + attr.value + "'>" + attr.text + "</a>";  
  7.                 }  
  8.         };  
  9.     })  
  10. </span>  

 

templateUrl:express

与template基本一致,但模版经过指定的url进行加载。由于模版加载是异步的,因此compilation、linking都会暂停,等待加载完毕后再执行。后端

replace:

若是设置为true,那么模版将会替换当前元素,而不是做为子元素添加到当前元素中。

scope:

 scope参数是可选的,能够被设置为true或一个对象。默认值是false。

 scope:false  此时,directive没有独立的scope对象,link函数中引用的scope对象来自于当前节点的默认controller

 scope:true  此时,directive拥有独立的scope对象,此scope是由父scope对象继承而来,能够访问父scope中的全部属性,此时其实是经过继承连接(prototype链)访问的父scope中的属性,要注意的是,当给此scope继承而来的属性名称赋值的时候,子scope中会相应创建一个本地属性,此时改变的是本scope的变量属性,父scope中的属性是不会改变的。

下面 咱们来讲说 directive 与scope 的 隔离交互

AngularJS 的 directive 默认能共享父 scope 中定义的属性,例如在模版中直接使用父 scope 中的对象和属性。一般使用这种直接共享的方式能够实现一些简单的 directive 功能。当你须要建立一个可重复使用的 directive,只是偶尔须要访问或者修改父 scope 的数据,就须要使用隔离 scope。当使用隔离 scope 的时候,directive 会建立一个没有依赖父 scope 的 scope,并提供一些访问父 scope 的方式。

 当你想要写一个可重复使用的 directive,不能再依赖父 scope,这时候就须要使用隔离 scope 代替。共享 scope 能够直接共享父 scope,而隔离 scope 没法共享父scope。下图解释共享 scope 和隔离 scope 的区别:

 



使用共享 scope 的时候,能够直接从父 scope 中共享属性。所以下面示例能够将那么属性的值输出出来。使用的是父 scope 中定义的值。

 

 

[javascript]  view plain  copy
 
  1. <span style="color:#330033;">app.controller("myController", function ($scope) {  
  2.     $scope.name = "hello world";  
  3.     }).directive("shareDirective", function () {  
  4.     return {  
  5.             template: 'Say:{{name}}'  
  6.     }  
  7. });</span>  

 

 

[html]  view plain  copy
 
  1. <span style="color:#330033;"><div ng-controller="myController">  
  2. <div share-directive=""></div>  
  3. </div></span>  

结果输出 hello world

 

使用隔离 scope 的时候,没法从父 scope 中共享属性。所以下面示例没法输出父 scope 中定义的 name 属性值。

 

[javascript]  view plain  copy
 
  1. <span style="color:#330033;">app.controller("myController", function ($scope) {  
  2.     $scope.name = "hello world";  
  3. }).directive("isolatedDirective", function () {  
  4.         return {  
  5.             scope: {},  
  6.             template: 'Say:{{name}}'  
  7.         }  
  8. });</span>  

[html]  view plain  copy
 
  1. <span style="color:#330033;"><div ng-controller="myController">  
  2. <div isolated-directive=""></div>  
  3. </div></span>  

输出结果 say:

 

从上面看出共享 scope 容许从父 scope 渗入到 directive 中,而隔离 scope 不能,在隔离 scope 下,给 directive 创造了一堵墙,使得父 scope 没法渗入到 directive 中。

在 Directive 中建立隔离 scope 很简单,只须要定义一个 scope 属性便可,这样,这个 directive 的 scope 将会建立一个新的 scope,若是多个 directive 定义在同一个元素上,只会建立一个新的 scope。

 

[javascript]  view plain  copy
 
  1. <span style="color:#330033;">angular.module('app').controller("myController", function ($scope) {  
  2.     $scope.user = {  
  3.             id:1,  
  4.             name:"hello world"  
  5.     };  
  6. }).directive('isolatedScope', function () {  
  7.     return {  
  8.         scope: {},  
  9.         template: 'Name: {{user.name}} Street: {{user.addr}}'  
  10.     };  
  11. });</span>  

如今 scope 是隔离的,user 对象将没法从父 scope 中访问,所以,在 directive 渲染的时候 user 对象的占位将不会输出内容。

 

 

directive 在使用隔离 scope 的时候,提供了三种方法同隔离以外的地方交互。这三种分别是

 

@ 绑定一个局部 scope 属性到当前 dom 节点的属性值。结果老是一个字符串,由于 dom 属性是字符串。

& 提供一种方式执行一个表达式在父 scope 的上下文中。若是没有指定 attr 名称,则属性名称为相同的本地名称。

= 经过 directive 的 attr 属性的值在局部 scope 的属性和父 scope 属性名之间创建双向绑定。

 

以下示例:directive 声明未隔离 scope 类型,而且使用@绑定 name 属性,在 directive 中使用 name 属性绑定父 scope 中的属性。当改变父 scope 中属性的值的时候,directive 会同步更新值,当改变 directive 的 scope 的属性值时,父 scope 没法同步更新值。

 

[javascript]  view plain  copy
 
  1. <span style="color:#330033;"> app.controller("myController", function ($scope) {  
  2.         $scope.name = "hello world";  
  3.     }).directive("isolatedDirective", function () {  
  4.         return {  
  5.             scope: {  
  6.                 name: "@"  
  7.             },  
  8.             template: 'Say:{{name}} <br>改变隔离scope的name:<input type="buttom" value="" ng-model="name" class="ng-pristine ng-valid">'  
  9.         }  
  10. })</span>  

[html]  view plain  copy
 
  1. <span style="color:#330033;"><div ng-controller="myController">  
  2.    <div class="result">  
  3.        <div>父scope:  
  4.            <div>Say:{{name}}<br>改变父scope的name:<input type="text" value="" ng-model="name"/></div>  
  5.        </div>  
  6.        <div>隔离scope:  
  7.            <div isolated-directive name="{{name}}"></div>  
  8.        </div>  
  9.         <div>隔离scope(不使用{{name}}):  
  10.              <div isolated-directive name="name"></div>  
  11.     </div>  
  12. </div></span>  


 

= 局部 scope 属性



= 经过 directive 的 attr 属性的值在局部 scope 的属性和父 scope 属性名之间创建双向绑定。
意思是,当你想要一个双向绑定的属性的时候,你可使用=来引入外部属性。不管是改变父 scope 仍是隔离 scope 里的属性,父 scope 和隔离 scope 都会同时更新属性值,由于它们是双向绑定的关系。

 

[javascript]  view plain  copy
 
  1. <span style="color:#330033;"> app.controller("myController", function ($scope) {  
  2.         $scope.user = {  
  3.             name: 'hello',  
  4.             id: 1  
  5.         };  
  6.     }).directive("isolatedDirective", function () {  
  7.         return {  
  8.             scope: {  
  9.                 user: "="  
  10.             },  
  11.             template: 'Say:{{user.name}} <br>改变隔离scope的name:<input type="buttom" value="" ng-model="user.name"/>'  
  12.         }  
  13.     })</span>  

[html]  view plain  copy
 
  1. <span style="color:#330033;"><div ng-controller="myController">  
  2.     <div>父scope:  
  3.         <div>Say:{{user.name}}<br>改变父scope的name:<input type="text" value="" ng-model="user.name"/></div>  
  4.     </div>  
  5.     <div>隔离scope:  
  6.         <div isolated-directive user="user"></div>  
  7.     </div>  
  8.     <div>隔离scope(使用{{name}}):  
  9.         <div isolated-directive user="{{user}}"></div>  
  10.     </div>  
  11. </div></span>  


 

 

& 局部 scope 属性

& 方式提供一种途经是 directive 能在父 scope 的上下文中执行一个表达式。此表达式能够是一个 function。
好比当你写了一个 directive,当用户点击按钮时,directive 想要通知 controller,controller 没法知道 directive 中发生了什么,也许你能够经过使用 angular 中的 event 广播来作到,可是必需要在 controller 中增长一个事件监听方法。
最好的方法就是让 directive 能够经过一个父 scope 中的 function,当 directive 中有什么动做须要更新到父 scope 中的时候,能够在父 scope 上下文中执行一段代码或者一个函数。

 

 

[javascript]  view plain  copy
 
  1. <span style="color:#330033;"> app.controller("myController", function ($scope) {  
  2.         $scope.value = "hello world";  
  3.         $scope.click = function () {  
  4.             $scope.value = Math.random();  
  5.         };  
  6.     }).directive("isolatedDirective", function () {  
  7.         return {  
  8.             scope: {  
  9.                 action: "&"  
  10.             },  
  11.             template: '<input type="button" value="在directive中执行父scope定义的方法" ng-click="action()"/>'  
  12.         }  
  13.     })</span>  

[html]  view plain  copy
 
  1. <span style="color:#330033;"<div  ng-controller="myController">  
  2.         <div>父scope:  
  3.             <div>Say:{{value}}</div>  
  4.         </div>  
  5.         <div>隔离scope:  
  6.             <div isolated-directive action="click()"></div>  
  7.         </div>  
  8. </div></span>  


 

Transclusion(嵌入)

Transclusion是让咱们的指令包含任意内容的方法。咱们能够延时提取并在正确的scope下编译这些嵌入的内容,最终将它们放入指令模板中指定的位置。 若是你在指令定义中设置 transclude:true,一个新的嵌入的scope会被建立,它原型继承子父scope。 若是你想要你的指令使用隔离的scope,可是它所包含的内容可以在父scope中执行,transclusion也能够帮忙。

 

有时候我咱们要嵌入指令元素自己,而不只仅是它的内容。在这种状况下,咱们须要使用 transclude:’element’。它和 transclude:true 不一样,它将标记了 ng-transclude 指令的元素一块儿包含到了指令模板中。使用transclusion,你的link函数会得到一个名叫 transclude 的连接函数,这个函数绑定了正确的指令scope,而且传入了另外一个拥有被嵌入DOM元素拷贝的函数。你能够在这个 transclude 函数中执行好比修改元素拷贝或者将它添加到DOM上等操做。 

 

transclude:true

 

[html]  view plain  copy
 
  1. <span style="color:#330033;"><body ng-app="myApp">  
  2.   <div class="AAA">  
  3.    <hero name="superman">Stuff inside the custom directive</hero>  
  4. </div>  
  5. </body></span>  

[javascript]  view plain  copy
 
  1. <span style="color:#330033;">angular.module('myApp', []).directive('hero', function () {  
  2.     return {  
  3.       restrict: 'E',  
  4.       transclude: true,  
  5.       scope: { name:'@' },  
  6.       template: '<div>' +  
  7.                   '<div>{{name}}</div><br>' +  
  8.                   '<div ng-transclude></div>' +  
  9.                 '</div>'  
  10.     };  
  11.   });</span>  

 

通过指令编译之后

 

指令 中的 controller  controllerAs require

controller参数能够是一个字符串或一个函数。当设置为字符串时,会以字符串的值为名字,来查找注册在应用中的控制器的构造函数:

 

[javascript]  view plain  copy
 
  1. <span style="color:#330033;">angular.module('myApp', [])  
  2. .directive('myDirective', function() {  
  3. restrict: 'A', // 始终须要  
  4. controller: 'SomeController'  
  5. })  
  6. // 应用中其余的地方  
  7. angular.module('myApp')  
  8. .controller('SomeController', function($scope, $element, $attrs, $transclude) {  
  9. // 控制器逻辑放在这里  
  10. });</span>  
能够在指令内部经过匿名构造函数的方式来定义一个内联的控制器:

 

 

[javascript]  view plain  copy
 
  1. <span style="color:#330033;">angular.module('myApp',[])  
  2. .directive('myDirective', function() {  
  3. restrict: 'A',  
  4. controller:  
  5. function($scope, $element, $attrs, $transclude) {  
  6. // 控制器逻辑放在这里  
  7. }  
  8. });</span>  
咱们能够将任意能够被注入的AngularJS服务传递给控制器。例如,若是咱们想要将$log服
务传入控制器,只需简单地将它注入到控制器中,即可以在指令中使用它了。
控制器中也有一些特殊的服务能够被注入到指令当中。这些服务有:

 

1. $scope
与指令元素相关联的当前做用域。
2. $element
当前指令对应的元素。

3. $attrs
由当前元素的属性组成的对象。例如,下面的元素:
<div id="aDiv"class="box"></div>
具备以下的属性对象:
{
id: "aDiv",
class: "box"
}

4. $transclude
嵌入连接函数会与对应的嵌入做用域进行预绑定。
transclude连接函数是实际被执行用来克隆元素和操做DOM的函数。

例如,咱们想要经过指令来添加一个超连接标签。能够在控制器内的$transclude函数中实现:

 

[javascript]  view plain  copy
 
  1. <span style="color:#330033;">angular.module('myApp')  
  2. .directive('link', function() {  
  3. return {  
  4. restrict: 'EA',  
  5. transclude: true,  
  6. controller:  
  7. function($scope, $element, $transclude, $log) {  
  8. $transclude(function(clone) {  
  9. var a = angular.element('<a>');  
  10. a.attr('href', clone.text());  
  11. a.text(clone.text());  
  12. $log.info("Created new a tag in link directive");  
  13. $element.append(a);  
  14. });  
  15. }  
  16. };  
  17. });</span>  
指令的控制器和link函数能够进行互换。控制器主要是用来提供可在指令间复用的行为,但连接函数只能在当前内部指令中定义行为,且没法在指令间复用

 

因为指令能够require其余指令所使用的控制器,所以控制器常被用来放置在多个指令间共享的动做。
若是咱们但愿将当前指令的API暴露给其余指令使用,可使用controller参数,不然可使用link来构造当前指令元素的功能性。若是咱们使用了scope.$watch()或者想要与DOM元素作实时的交互,使用连接会是更好的选择。
技术上讲,$scope会在DOM元素被实际渲染以前传入到控制器中。在某些状况下,例如使用了嵌入,控制器中的做用域所反映的做用域可能与咱们所指望的不同,这种状况下,$scope对象没法保证能够被正常更新。

controller,link,compile有什么不一样

 

 

[javascript]  view plain  copy
 
  1. <span style="color:#330033;">//directives.js增长exampleDirective  
  2. phonecatDirectives.directive('exampleDirective', function() {  
  3.     return {  
  4.         restrict: 'E',  
  5.         template: '<p>Hello {{number}}!</p>',  
  6.         controller: function($scope, $element){  
  7.             $scope.number = $scope.number + "22222 ";  
  8.         },  
  9.         //controllerAs:'myController',  
  10.         link: function(scope, el, attr) {  
  11.             scope.number = scope.number + "33333 ";  
  12.         },  
  13.         compile: function(element, attributes) {  
  14.             return {  
  15.                 pre: function preLink(scope, element, attributes) {  
  16.                     scope.number = scope.number + "44444 ";  
  17.                 },  
  18.                 post: function postLink(scope, element, attributes) {  
  19.                     scope.number = scope.number + "55555 ";  
  20.                 }  
  21.             };  
  22.         }  
  23.     }  
  24. });  
  25.   
  26. //controller.js添加  
  27. dtControllers.controller('directive2',['$scope',  
  28.     function($scope) {  
  29.         $scope.number = '1111 ';  
  30.     }  
  31. ]);  
  32.   
  33. //html  
  34. <body ng-app="phonecatApp">  
  35.     <div ng-controller="directive2">  
  36.         <example-directive></example-directive>  
  37.     </div>  
  38. </body></span>  

运行结果:

 

 

[javascript]  view plain  copy
 
  1. <span style="color:#330033;">Hello 1111 22222 44444 55555 !  </span>  
由结果能够看出来,controller先运行,compile后运行,link不运行(link就是compile中的postLink)。将上例中的compile注释掉运行结果:

 

 

[javascript]  view plain  copy
 
  1. <span style="color:#330033;">Hello 1111 22222 33333 !    </span>  
由结果能够看出来,controller先运行,link后运行,link和compile不兼容。compile改变dom,link事件的触发和绑定

 

 

controllerAs



controllerAs参数用来设置控制器的别名,能够以此为名来发布控制器,而且做用域能够访问controllerAs。这样就能够在视图中引用控制器,甚至无需注入$scope。
例如,建立一个MainController,而后不要注入$scope,以下所示:

 

[javascript]  view plain  copy
 
  1. <span style="color:#330033;">angular.module('myApp')  
  2. .controller('MainController', function() {  
  3. this.name = "Ari";  
  4. });</span>  

如今,在HTML中无需引用做用域就可使用MainController。
[html]  view plain  copy
 
  1. <span style="color:#330033;"><div ng-appng-controller="MainControllerasmain">  
  2. <input type="text" ng-model="main.name" />  
  3. <span>{{ main.name }}</span>  
  4. </div></span>  

这个参数看起来好像没什么大用,但它给了咱们能够在路由和指令中建立匿名控制器的强大能力。这种能力能够将动态的对象建立成为控制器,而且这个对象是隔离的、易于测试的。
例如,能够在指令中建立匿名控制器,以下所示:
 
[javascript]  view plain  copy
 
  1. <span style="color:#330033;">angular.module('myApp')  
  2. .directive('myDirective', function() {  
  3. return {  
  4. restrict: 'A',  
  5. template: '<h4>{{ myController.msg }}</h4>',  
  6. controllerAs: 'myController',  
  7. controller: function() {  
  8. this.msg = "Hello World"  
  9. }  
  10. };  
  11. });</span>  

require

 
require参数能够被设置为字符串或数组,字符串表明另一个指令的名字。require会将控制器注入到其值所指定的指令中,并做为当前指令的连接函数的第四个参数。
字符串或数组元素的值是会在当前指令的做用域中使用的指令名称。
scope会影响指令做用域的指向,是一个隔离做用域,一个有依赖的做用域或者彻底没有做用域。
在任何状况下,AngularJS编译器在查找子控制器时都会参考当前指令的模板。
require参数的值能够用下面的前缀进行修饰,这会改变查找控制器时的行为:
?
若是在当前指令中没有找到所须要的控制器,会将null做为传给link函数的第四个参数。
^
若是添加了^前缀,指令会在上游的指令链中查找require参数所指定的控制器。
?^
将前面两个选项的行为组合起来,咱们可选择地加载须要的指令并在父指令链中进行查找。

没有前缀
若是没有前缀,指令将会在自身所提供的控制器中进行查找,若是没有找到任何控制器(或具备指定名字的指令)就抛出一个错误。

下面这个例子可以很好的解释
 
[javascript]  view plain  copy
 
  1. <span style="color:#330033;">var app = angular.modeule('myapp',[]);  
  2.   
  3. app.directive('common',function(){  
  4.     return {  
  5.     ...  
  6.     controller: function($scope){  
  7.         this.method1 = function(){  
  8.         };  
  9.         this.method2 = function(){  
  10.         };  
  11.     },  
  12.     ...  
  13.     }  
  14. });  
  15.   
  16. app.directive('d1',function(){  
  17.     return {  
  18.     ...  
  19.     require: '?^common',  
  20.     link: function(scope,elem,attrs,common){  
  21.         scope.method1 = common.method1;  
  22.         ..  
  23.         },  
  24.     ...  
  25.     }  
  26. });</span>  
 

ngModal

 
ngModel是一个用法特殊的指令,它提供更底层的API来处理控制器内的数据。当咱们在指令中使用ngModel时可以访问一个特殊的API,这个API用来处理数据绑定、验证、CSS更新等不实际操做DOM的事情。
 
ngModel控制器会随ngModel被一直注入到指令中,其中包含了一些方法。
 
[javascript]  view plain  copy
 
  1. <span style="color:#330033;">angular.module('myApp')  
  2. .directive('myDirective',function(){  
  3. return {  
  4. require: '?ngModel',  
  5. link: function(scope, ele, attrs, ngModel) {  
  6. if (!ngModel) return;  
  7. // 如今咱们的指令中已经有ngModelController的一个实例  
  8. }  
  9. };  
  10. });</span>  
若是不设置require选项,ngModelController就不会被注入到指令中。
注意,这个指令没有隔离做用域。若是给这个指令设置隔离做用域,将致使内部ngModel没法更新外部ngModel的对应值:AngularJS会在本地做用域之外查询值。

为了设置做用域中的视图值,须要调用ngModel.$setViewValue()函数。ngModel.$setViewValue()函数能够接受一个参数。
value(字符串):value参数是咱们想要赋值给ngModel实例的实际值。这个方法会更新控制器上本地的$viewValue,而后将值传递给每个$parser函数(包括验证器)。

$setViewValue()方法适合于在自定义指令中监听自定义事件(好比使用具备回调函数的jQuery插件),咱们会但愿在回调时设置$viewValue并执行digest循环。
[javascript]  view plain  copy
 
  1. <span style="color:#330033;">angular.module('myApp')  
  2. .directive('myDirective', function() {  
  3. return {  
  4. require: '?ngModel',  
  5. link: function(scope, ele, attrs, ngModel) {  
  6. if (!ngModel) return;  
  7. $(function() {  
  8. ele.datepicker({  
  9. onSelect: function(date) {  
  10. // 设置视图和调用apply  
  11. scope.$apply(function() {  
  12. ngModel.$setViewValue(date);  
  13. });  
  14. }  
  15. });  
  16. });  
  17. }  
  18. };  
  19. });</span>  
看到这里 可能你们仍是不明白 what fuck this
首先让咱们在控制台输出ngmodel这个参数看看
[html]  view plain  copy
 
  1. <span style="color:#330033;"><!DOCTYPE html>  
  2. <html lang="en" ng-app="app">  
  3. <head>  
  4.   <meta charset="UTF-8">  
  5.   <title>Document</title>  
  6.   <script src="angular.js" charset="utf-8"></script>  
  7. </head>  
  8. <body ng-controller='ctrl'>  
  9.   <input type="text" test ng-model=_val>  
  10.   <script>  
  11.     var app = angular.module('app',[]);  
  12.     app.controller('ctrl',function ($scope){  
  13.       $scope._val = "leifengshushu";  
  14.     })  
  15.     app.directive('test',function(){  
  16.       return{  
  17.         restrict: 'AE',  
  18.         require: 'ngModel',  
  19.         link: function (scope,iElem,iAttr,ngmodel){  
  20.           console.log(ngmodel)  
  21.         }  
  22.       }  
  23.     })  
  24.   </script>  
  25. </body>  
  26. </html></span>  
这个对象包含不少属性和方法
$viewValue为视图值,即显示在视图(页面)的实际值(就是上面例子中input输入框的值)
$modelValue为模型值,即赋给ng-model的值(与控制器绑定的值)
二者不必定相等,由于$viewValue同步到$modelValue要通过一系列的操做。
虽然大多数状况下二者是相等的(例如上面的例子)
 
$parsers为一个执行它里面每个元素(每个元素都是一个函数)的数组,主要是用来作验证和转换值的过程,ngModel从DOM读取的值会被传入到其中的函数
它会依次执行每个函数,把每个函数执行的结果传个下一个函数,而最后一个函数执行的值将会传到model中,咱们能够将函数push进去,那样它就会执行。
 
$formatters也是一个执行它里面每个元素(每个元素都是一个函数)的数组,主要用来对值进行格式化和转换,以便在绑定了这个值的控件中显示。
当数据的模型值发生变化的时候,里面的函数会被一一执行,一样咱们就能够将函数push进去,让它执行

$viewChangeListeners的值也是一个由函数组成的数组,当视图的值发生变化的时候里面的函数会被一一调用,
实现跟$watch相似的功能。

$render函数负责将模型值同步到视图上, 若是模型值被改变,须要同步视图的值。

$setViewValue用于设置视图值(上面的例子就是将input的value值赋值给$viewValue);
 
还有一些属性 在这里就不详解 请你们 百度 ngmodal 
 

下面咱们来讲说 $apply() 和 $digest();

 
$apply()和$digest()在AngularJS中是两个核心概念,可是有时候它们又让人困惑。而为了了解AngularJS的工做方式,首先须要了解$apply()和$digest()是如何工做的。这篇文章旨在解释$apply()和$digest()是什么,以及在平常的编码中如何应用它们。
 
探索$apply()和$digest()
AngularJS提供了一个很是酷的特性叫作双向数据绑定(Two-way Data Binding),这个特性大大简化了咱们的代码编写方式。数据绑定意味着当View中有任何数据发生了变化,那么这个变化也会自动地反馈到scope的数据上,也即意味着scope模型会自动地更新。相似地,当scope模型发生变化时,view中的数据也会更新到最新的值。那么AngularJS是如何作到这一点的呢?当你写下表达式如{{ aModel }}时,AngularJS在幕后会为你在scope模型上设置一个watcher,它用来在数据发生变化的时候更新view。这里的watcher和你会在AngularJS中设置的watcher是同样的:
 
[javascript]  view plain  copy
 
  1. <span style="color:#330033;">$scope.$watch('aModel', function(newValue, oldValue) {    
  2.   //update the DOM with newValue    
  3. });</span>  

传入到$watch()中的第二个参数是一个回调函数,该函数在aModel的值发生变化的时候会被调用。当aModel发生变化的时候,这个回调函数会被调用来更新view这一点不难理解,可是,还存在一个很重要的问题!AngularJS是如何知道何时要调用这个回调函数呢?换句话说,AngularJS是如何知晓aModel发生了变化,才调用了对应的回调函数呢?它会周期性的运行一个函数来检查scope模型中的数据是否发生了变化吗?好吧,这就是$digest循环的用武之地了。
 
在$digest循环中,watchers会被触发。当一个watcher被触发时,AngularJS会检测scope模型,如何它发生了变化那么关联到该watcher的回调函数就会被调用。那么,下一个问题就是$digest循环是在何时以各类方式开始的?
 
在调用了$scope.$digest()后,$digest循环就开始了。假设你在一个ng-click指令对应的handler函数中更改了scope中的一条数据,此时AngularJS会自动地经过调用$digest()来触发一轮$digest循环。当$digest循环开始后,它会触发每一个watcher。这些watchers会检查scope中的当前model值是否和上一次计算获得的model值不一样。若是不一样,那么对应的回调函数会被执行。调用该函数的结果,就是view中的表达式内容(译注:诸如{{ aModel }})会被更新。除了ng-click指令,还有一些其它的built-in指令以及服务来让你更改models(好比ng-model,$timeout等)和自动触发一次$digest循环。
 
目前为止还不错!可是,有一个小问题。在上面的例子中,AngularJS并不直接调用$digest(),而是调用$scope.$apply(),后者会调用$rootScope.$digest()。所以,一轮$digest循环在$rootScope开始,随后会访问到全部的children scope中的watchers。
 
如今,假设你将ng-click指令关联到了一个button上,并传入了一个function名到ng-click上。当该button被点击时,AngularJS会将此function包装到一个wrapping function中,而后传入到$scope.$apply()。所以,你的function会正常被执行,修改models(若是须要的话),此时一轮$digest循环也会被触发,用来确保view也会被更新。
 
Note: $scope.$apply()会自动地调用$rootScope.$digest()。$apply()方法有两种形式。第一种会接受一个function做为参数,执行该function而且触发一轮$digest循环。第二种会不接受任何参数,只是触发一轮$digest循环。咱们立刻会看到为何第一种形式更好。
 
何时手动调用$apply()方法?
若是AngularJS老是将咱们的代码wrap到一个function中并传入$apply(),以此来开始一轮$digest循环,那么何时才须要咱们手动地调用$apply()方法呢?实际上,AngularJS对此有着很是明确的要求,就是它只负责对发生于AngularJS上下文环境中的变动会作出自动地响应(即,在$apply()方法中发生的对于models的更改)。AngularJS的built-in指令就是这样作的,因此任何的model变动都会被反映到view中。可是,若是你在AngularJS上下文以外的任何地方修改了model,那么你就须要经过手动调用$apply()来通知AngularJS。这就像告诉AngularJS,你修改了一些models,但愿AngularJS帮你触发watchers来作出正确的响应。
 
好比,若是你使用了JavaScript中的setTimeout()来更新一个scope model,那么AngularJS就没有办法知道你更改了什么。这种状况下,调用$apply()就是你的责任了,经过调用它来触发一轮$digest循环。相似地,若是你有一个指令用来设置一个DOM事件listener而且在该listener中修改了一些models,那么你也须要经过手动调用$apply()来确保变动会被正确的反映到view中
 
让咱们来看一个例子。加入你有一个页面,一旦该页面加载完毕了,你但愿在两秒钟以后显示一条信息。你的实现多是下面这个样子的:

[javascript]  view plain  copy
 
  1. <span style="color:#330033;"><body ng-app="myApp">    
  2.   <div ng-controller="MessageController">    
  3.     Delayed Message: {{message}}    
  4.   </div>      
  5. </body></span>  

[javascript]  view plain  copy
 
  1. <span style="color:#330033;">    angular.module('myApp',[]).controller('MessageController', function($scope) {    
  2.         
  3.       $scope.getMessage = function() {    
  4.         setTimeout(function() {    
  5.           $scope.message = 'Fetched after 3 seconds';    
  6.           console.log('message:'+$scope.message);    
  7.         }, 2000);    
  8.       }    
  9.           
  10.       $scope.getMessage();    
  11.         
  12.     }); </span>  

经过运行这个例子,你会看到过了两秒钟以后,控制台确实会显示出已经更新的model,然而,view并无更新。缘由也许你已经知道了,就是咱们忘了调用$apply()方法。所以,咱们须要修改getMessage(),以下所示:
 
[javascript]  view plain  copy
 
  1. <span style="color:#330033;">angular.module('myApp',[]).controller('MessageController', function($scope) {    
  2.         
  3.       $scope.getMessage = function() {    
  4.         setTimeout(function() {    
  5.           $scope.$apply(function() {    
  6.             //wrapped this within $apply    
  7.             $scope.message = 'Fetched after 3 seconds';     
  8.             console.log('message:' + $scope.message);    
  9.           });    
  10.         }, 2000);    
  11.       }    
  12.           
  13.       $scope.getMessage();    
  14.         
  15.     });  </span>  



$digest循环会运行多少次?
当一个$digest循环运行时,watchers会被执行来检查scope中的models是否发生了变化。若是发生了变化,那么相应的listener函数就会被执行。这涉及到一个重要的问题。若是listener函数自己会修改一个scope model呢?AngularJS会怎么处理这种状况?
 
答案是$digest循环不会只运行一次。在当前的一次循环结束后,它会再执行一次循环用来检查是否有models发生了变化。这就是脏检查(Dirty Checking),它用来处理在listener函数被执行时可能引发的model变化。所以,$digest循环会持续运行直到model再也不发生变化,或者$digest循环的次数达到了10次。所以,尽量地不要在listener函数中修改model。

service 

服务提供了一种能在应用的整个生命周期内保持数据的方法,它可以在控制器之间进行通讯,而且能保证数据的一致性。
服务是一个单例对象,在每一个应用中只会被实例化一次(被$injector实例化),而且是延迟加载的(须要时才会被建立)。
服务提供了把与特定功能相关联的方法集中在一块儿的接口。
 

注册一个服务

使用angular.module的factoryAPI建立服务,是最多见也是最灵活的方式:
[javascript]  view plain  copy
 
  1. angular.module('myApp.services', [])  
  2. .factory('githubService', function() {  
  3.   var serviceInstance = {};  
  4.   // 咱们的第一个服务  
  5.   return serviceInstance;  
  6. });  
 
[javascript]  view plain  copy
 
  1. var Person = function($http) {  
  2. this.getName = function() {  
  3. return $http({ method: 'GET', url: '/api/user'});  
  4. };  
  5. };  
  6. angular.service('personService', Person);  
 
[javascript]  view plain  copy
 
  1. angular.module('myApp')  
  2. .factory('myService', function() {  
  3. return {  
  4. 'username': 'auser'  
  5. };  
  6. })  
  7. // 这与上面工厂的用法等价  
  8. .provider('myService', {  
  9. $get: function() {  
  10. return {  
  11. 'username': 'auser'  
  12. };  
  13. }  
  14. });  

[javascript]  view plain  copy
 
  1. angular.module('myApp.services', [])  
  2. .factory('githubService', function($http) {  
  3. var githubUrl = 'https://api.github.com';  
  4. var runUserRequest = function(username, path) {  
  5. // 从使用JSONP调用Github API的$http服务中返回promise  
  6. return $http({  
  7. method: 'JSONP',  
  8. url: githubUrl + '/users/' +  
  9. username + '/' +  
  10. path + '?callback=JSON_CALLBACK'  
  11. });  
  12. };  
  13. // 返回带有一个events函数的服务对象  
  14. return {  
  15. events: function(username) {  
  16. return runUserRequest(username, 'events');  
  17. }  
  18. };  
  19. });  

能够在控制器、指令、过滤器或另一个服务中经过依赖声明的方式来使用服务。 
将服务的名字看成参数传递给控制器函数,能够将服务注入到控制器中。当服务成为了某个控制器的依赖,就能够在控制器中调用任何定义在这个服务对象上的方法。

[javascript]  view plain  copy
 
  1. angular.module('myApp', ['myApp.services'])  
  2. .controller('ServiceController', function($scope, githubService) {  
  3. // 咱们能够调用对象的事件函数  
  4. $scope.events = githubService.events('auser');  
  5. });  

constant('name','value')

[javascript]  view plain  copy
 
  1. angular.module('myApp') .constant('apiKey','123123123')  
  2. //这个常量服务能够像其余服务同样被注入到配置函数中:  
  3. angular.module('myApp')  
  4. .controller('MyController', function($scope, apiKey) {  
  5. // 能够像上面同样用apiKey做为常量  
  6. // 用123123123做为字符串的值  
  7. $scope.apiKey = apiKey;  
  8. });  

value('name','value')

[javascript]  view plain  copy
 
  1. angular.module('myApp')  
  2. .value('apiKey','123123123');  

value()方法和constant()方法之间最主要的区别是,常量能够注入到配置函数中,而值不行。一般状况下,能够经过value()来注册服务对象或函数,用constant()来配置数据。

[javascript]  view plain  copy
 
  1. angular.module('myApp', [])  
  2. .constant('apiKey', '123123123')  
  3. .config(function(apiKey) {  
  4. // 在这里apiKey将被赋值为123123123  
  5. // 就像上面设置的那样  
  6. })  
  7. .value('FBid','231231231')  
  8. .config(function(FBid) {  
  9. // 这将抛出一个错误,未知的provider: FBid  
  10. // 由于在config函数内部没法访问这个值  
  11. });  


下面咱们来讲说他们的区别 (provider,value,constant,service,factory,decorator)

 
provider是干啥的?
provider能够为应用提供通用的服务,形式能够是常量,也能够是对象。
好比咱们在controller里经常使用的$http就是AngularJS框架提供的provider

[javascript]  view plain  copy
 
  1. myApp.controller(‘MainController', function($scope, $http) {  
  2.    $http.get(…)  
  3. }  
在上面的代码里,就能够直接使用$http包好的各类功能了
下面咱们本身定义一个provider
[javascript]  view plain  copy
 
  1. //定义:  
  2. $provide.provider('age', {  
  3.     start: 10,  
  4.     $get: function() {  
  5.         return this.start + 2;  
  6.     }  
  7. });  
  8. //或  
  9. $provide.provider('age', function($filterProvider){  
  10.     this.start = 10;  
  11.     this.$get = function() {  
  12.         return this.start + 2;  
  13.     };  
  14. });  
  15. //调用:  
  16. app.controller('MainCtrl', function($scope, age) {  
  17.     $scope.age = age; //12  
  18. });  

provider的基本原则就是经过实现$get方法来在应用中注入单例,使用的时候拿到的age就是$get执行后的结果。 上面例子中的两种定义方法均可以
 
下面咱们本身定义 factory
[javascript]  view plain  copy
 
  1. $provide.provider('myDate', {  
  2.     $get: function() {  
  3.         return new Date();  
  4.     }  
  5. });  
  6. //能够写成  
  7. $provide.factory('myDate', function(){  
  8.     return new Date();  
  9. });  
  10. //调用:  
  11. app.controller('MainCtrl', function($scope, myDate) {  
  12.     $scope.myDate = myDate; //current date  
  13. });  

下面咱们去定义service
[javascript]  view plain  copy
 
  1. $provide.provider('myDate', {  
  2.     $get: function() {  
  3.         return new Date();  
  4.     }  
  5. });  
  6. //能够写成  
  7. $provide.factory('myDate', function(){  
  8.     return new Date();  
  9. });  
  10. //能够写成  
  11. $provide.service('myDate', Date);  

value 和 constant ,value能够被修改 而constant 不能被修改,value 不能在 config 里面注入可是constant 能够


angularjs 与服务器的 交互

  1. 主要了解$http
  2. 熟悉$resource
  3. 学会使用 restangular 库 http://ngmodules.org/modules/restangular
  4. 理解promise
相关文章
相关标签/搜索