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: anotation

默认是通过 属性的方式 

priority : 优先级

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

terminal :

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

template:

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

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

 

[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:

与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

转载于:https://www.cnblogs.com/qq666666/p/7842745.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值