搭建环境
项目github地址
项目中涉及了json-server模拟get请求,用了angular-route;
AngularJS的一些出众
构建一个CRUD应用可能用到的全部内容包括:数据绑定、基本模板标识符、表单验证、路由、深度链接、组件重用、依赖注入。
测试方面包括:单元测试、端对端测试、模拟和自动化测试框架。
具有目录布局和测试脚本的种子应用作为起点。
简单使用
指令:
扩展 HTML,为应用添加功能。 AngularJS 允许你自定义指令(实现组件化)。
项目中使用angular时,在模板上,使用一系列基本的指令指名不同的作用,如下:
//ng-app指明这是个Angular应用
var myApp = angular.module('myApp', ['ngRoute']); //在初始化时会初始化Angualr相关的地方;
ng-app .指令初始化一个 AngularJS 应用程序。
ng-init.指令初始化应用程序数据。
ng-model.指令把元素值(比如输入域的值)绑定到应用程序。
作用域:
如果把Angular理解成一个MVVM框架,那么Scope充当的就是ViewModel;它是View和Model的粘合体,负责View和Model的交互和协作,它负责给View提供显示的数据,以及提供了View中Command事件操作Model的途径。
Angular中的scope很重要,它是连接模板到模型的一个很重要纽带,它作用域在一个controller范围内,在这个范围内的模板和模型都是通过这个scope联系。
由于它,实现了数据的双向绑定,后面讲解的渲染机制会突出scope在渲染中的作用。
控制器:
如果把Angular理解成一个MVVM框架,它负责ViewModel对象的初始化,它将组合一个或者多个service来获取业务领域Model放在ViewModel对象上,使得应用界面在启动加载的时候达到一种可用的状态。但并不是MVVM模式的核心元素。
如下是scope和controller的使用:
1.controller中的scope ``` myApp.controller("messageTypeController",function($scope,$http){ $scope.title = "D"; $scope.msgType=[{id:1,name:"push",option:'xxx'}]; $scope.text="'Hello World!'"; });
对于model的数据可以从网络中获取,然后赋值个scope这个中间人,最后会渲染到界面上。
依赖注入:
这虽不是angular提出的一个新的概念,但angular是用这用方式管理各个对象确实吸引很大,方便设计低耦合高复用的对象。
provider(创建可提供的依赖)
AngularJS 通过$provider对象的provider创建一个可注入的东西,还有其他接口如,value,factory,service,Constant但这四个内部实现都是通过provider创建的,他们是provider的简写。
value
用来将值传递过程中的配置相位控制器 factory
工厂是用于返回函数的值。它根据需求创造值,每当一个服务或控制器需要。它通常使用一个工厂函数来计算并返回对应值。 service
服务是一个单一的JavaScript包含了一组函数对象来执行某些任务。服务使用service()函数,然后注入到控制器的定义。 constant
常量用于通过配置相位值考虑事实,值不能使用期间的配置阶段被传递
注入器( injector)注入器负责从我们通过provide创建的服务中创建注入的实例,每一个AngularJS应用都有唯一一个injector,当应用启动的时候它被创造出来,你可以通过将injector注入到任何可注入函数中来得到它($injector知道如何注入它自己!)。
一旦你拥有了$injector,你可以动过调用get函数来获得任何一个已经被定义过的服务的实例
//../utils/utils.js module.exports = function() { var factory = {}; factory.reComputeResult = function(value,a, b) { switch(value){ case "option1": return a+b; break; case "option2": return a-b; break; case "option3": return a*b; break; case "option4": return a/b; break; } } return factory; } var utils=require('../utils/utils.js'); myApp.value("defaultInput1", 1); myApp.value("defaultInput2", 2); myApp.constant("_constant","can't modify"); myApp.config(function($provide,_constant) { $provide.provider('MathService', function() { this.$get = function() { var factory = {}; factory.reComputeResult = function(value,a, b) { switch(value){ case "option1": return a+b; break; case "option2": return a-b; break; case "option3": return a*b; break; case "option4": return a/b; break; } } return factory; }; }); }); myApp.factory('MathService', utils); //这里的MathService和上面创建的是一样的效果,实际中只保留一个。 myApp.service('CalcService', function($injector){ var MathService=$injector.get('MathService'); //使用$injector的方式注入 this.reComputeResult = function(value,a,b) { return MathService.reComputeResult(value,a,b); } }); myApp.controller('strategyController', function($scope, CalcService, defaultInput1,defaultInput2,_constant) {//直接在controler中注入 $scope.title = "B"; $scope.redioValue="option1"; $scope.firstNum = defaultInput1; $scope.secondNum = defaultInput2; console.log(_constant); $scope.computeResult = CalcService.reComputeResult($scope.redioValue,$scope.firstNum,$scope.secondNum); $scope.reComputeResult = function() { $scope.computeResult = CalcService.reComputeResult($scope.redioValue,$scope.firstNum,$scope.secondNum); } });
scope的嵌套关系
子作用域一般都会通过javascript原型继承机制继承其父作用域的属性和方法:
子作用域与父作用域中相同的值会覆盖父作用域中的值,但修改子作用域中的值不会修改父作用域中的值; 对于通过drirective创建的组件的作用域可以根据scope的true配置成原型继承,false配置为独立的作用域。
简单的作用域间的通信
子作用域利用原型继承可以直接获取父作用域上的对象和方法,但是不能修改;若要修改可以调用父作用域中的方法修改; 父作用域修改子作用域中的值通过$broadcast广播的形式传递。
渲染方式
AngularJS修改了一般的javascript工作流,并且提供了它自己的事件处理机制:把javascript的context分隔成两部分,一部分是原生的javascript的context,另一部分是AngularJS的context.
一般的工作流
浏览器UI线程基于一个简单的队列系统,任务hi被添加到队列中直到UI线程空闲,一旦空闲,队列中的下一个任务会被提取出来并运行。这些任务要么是运行javascript,要么是执行UI更新,包括重绘和重排。
AngularJS的工作流
首先将任务加入UI队列。 如果队列中的任务被$apply()包起来的话,就会进入到AngularJS的执行上下文(AngularJS执行的线程)中。 AngularJS的执行上下文执行这个任务。 然后AngularJS会进入到由两个小循环组成的digest循环中,一个循环是用来处理evalAsync队列,一个循环是处理watch列表。digest循环会一直迭代直到evalAsync队列为空并且watch列表也为空。 一旦AngularJS的$digest循环结束,整个执行就会离开AngularJS执行上下文,回到一般的工作流,将相应的UI的更新任务加入队列等待UI线程执行,最后更新DOM。
数据脏检测,如何实现双向效果
编译的时候,遍历DOM,找出与这部分DOM相关的指令,这些指令有一个compile方法,它表明如何去修改DOM的结构,每个指令最后都返回一个链接函数。这部分DOM的有一个Scope,这个Scope与这些指令的连接函数集合链接。这样就形成了Scope的DOM的动态绑定。任何一个Scope的改变都会在DOM上体现出来。
每次由视图触发的事件都会在angular执行上下文中循环检测watch列表的变化,watch队列里的每个$watch监听着一个视图上的model的变化,若model发生变化也就是这个Scope发生了改变,从而导致相应的视图重新渲染。
大概就是:
model使scope发生改变,直接根据指令解析时与scope的链接关系更新相应的dom; view使scope发生改变,angularjs都会进入一个循环,里面有两个小循环,其中的一个$watch他会检测由于任务的执行改变的那些Scope,将改变的表示为脏,对于这些脏的scope会在返回一般工作流中一次性更新相应的dom,如何判断更新那部分,则是根据scope与指令返回的链接函数可以得到。
angular是如何确定值为脏的呢?
一般的脏值检测:使用“===”; 基于值得脏值检测:深度拷贝; 集合监控:浅度拷贝;
angular的$apply作用及应用
apply可以触发digest循环; 内部的angular指令比如ng-click事件中的函数都被apply包裹,触发事件既可以触发digest循环; 对于直接使用javascript的事件,若要触发一次渲染则需要手动对事件用$apply包裹;
Delayed Message: {{message}} angular.module('myApp',[]).controller('MessageController', function($scope) { $scope.getMessage = function() { setTimeout(function() { $scope.$apply(function() { //wrapped this within $apply $scope.message = 'Fetched after 3 seconds'; console.log('message:' + $scope.message); }); }, 2000); } $scope.getMessage(); });