因为工作上需要维护一个项目,所以围绕着“能顺利开展工作”这个目标,对AngularJS进行了一些学习。
一些基本认识
- ng2之前的叫AngularJS,之后改名Angular了,看起来名字差不多,其实2基本算是把1推倒重来了,比如双向绑定改成了单向之类的…
- AngularJS是一个基于MVC处理模式,实现了MVVM数据双向绑定的用于开发动态Web框架
- MVC不是一个技术,是一种处理问题的思路,也就是编程思想。核心是将数据和展示分离,通过控制器挂载并进行一定的功能逻辑的处理以达到让代码具备强大的扩展性的目的。
- Model:数据模型——用与数据的封装和运算
- View:视图——用于数据的展示和交互
- Controller:控制器——用于对视图和数据模型之间的联系进行逻辑关系的处理
- 补充说明一下,jQuery时代,jQuery是一个库而不是框架。
- 框架:一种封装了原生JavaScript实现的函数、功能、组件、处理流程的特殊实现。通过引入框架中的配置,可以实现一定的逻辑处理功能。
- 库:封装原生JavaScript实现的大量函数的集合,是提供了各种特定功能的函数库。通过引入的库中的函数,可以快捷实现一些特定的处理过程
AngularJS的一些基本概念
- template模版:包含了Angular特殊扩展标记的HTML代码
- directive指令:扩展的HTML代码,自定义的标签、属性等等
- model模型:保存在JS中,用来和用户交互的数据
- scope作用域:模型数据在HTML页面中产生作用的范围
- expression表达式:AngularJS在HTML页面中可以运算的语法
- compiler编译器:被用来解释HTML代码中的Angular代码
- filter过滤器:对HTML页面中输出的数据进行指定格式展示
- view视图:统称用户看到的HTML视图页面
- data binding数据绑定:JS中特定的数据和HTML页面上的数据的关联关系
- controller控制器:给视图view提供功能支持的
- dependency injection依赖注入:Angular自动创建对象并传递对象的一种方式
- injector注入器:专门用来实现依赖注入(DI)的容器
- module模块:Angular用来对项目进行最高层次封装的
- service服务:Angular用来对视图view提供业务功能支持
- component组件:用于对网页的公共部分进行封装重用的Angular代码,通常会包含模板、指令、服务等等
AngularJS代码上的一些要点
ng-app
常规情况下,是写在我们前端项目的入口文件的根标签上的,用于在项目启动的时候引导AngularJS应用angular.module(..)
AngularJS通过模块来管理我们前端项目中的数据,模块时需要在应用启动的时候就需要加载的,所以AngularJS在设计的过程中对于入口指令进行了改造可以绑定一个值,这个值就是系统的主模块。var app = angular.module(“name”, [])
:用于定义一个AngularJS的模块,跌二个参数是依赖;app.controller(“name”, function() {})
:用于定义一个AngularJS的控制器,名字后是这个控制器要处理功能的函数。- 指令:
ng-app=”myApp”
:程序运行的入口,通过名称绑定一个模块ng-init
:用于在程序运行的过程中,初始化一些变量的数据的操作,使用ng-init不是很常见。在控制器中有一个更好的初始化数据的方式。ng-repeat
指令会重复一个HTML元素- 因为ng会追踪,你如果看到
For example: item in items is equivalent to item in items track by $id(item). This implies that the DOM elements will be associated by item identity in the array.
可能是因为对于数字对象而言,它的id就是它的值,所以导致了重复 track by
,这个是用于自己指定track的东西,甚至可以简单的使用$index
——当然我们都知道,现在和当时不同了,是不会直接用索引的。
- 因为ng会追踪,你如果看到
ng-model=”param”
:数据绑定的指令,主要用于表单元素上的数据绑定,另外:ng-model
指令根据表单域的状态添加/移除以下类:ng-empty
ng-not-empty
ng-touched
ng-untouched
ng-valid
ng-invalid
ng-dirty
ng-pending
ng-pristine
ng-bind
:数据绑定的指令,用于将变量的数据显示到页面上,用于替代mustache语法的ng-controller
:控制器指令,用于在页面中指定控制器作用范围,通常作为属性出现ng-[event]
:事件指令,用于在页面中发生某些事件时调用指令的函数
$scope
- 是放在控制器函数中的一个参数。这个参数不需要传值,而是AngularJS自动赋值。
- 相当于一个容器,可以在
$scope
上声明变量或者函数,$scope
上的变量和函数会自动和视图页面中的变量进行绑定,称为$scope挂载数据。 $scope.$watch()
挂载在$scope上,用于监控数据的变化。监控单个变量:$scope.$watch(“name”,function() {// 处理代码})
,监控多个变量:$scope.$watch(“name1, name2, ..” + function() {})
$rootScope
是AngularJS的根作用域,也是全局作用域,放在控制器函数中的一个参数,参数不需要传值,AngularJS会自动赋值,挂载在$rootScope上的数据,会被AngularJS应用中的所有模块下的子模块和控制器公用。app.run(function($rootScope) { // 处理代码})
run()配合$rootScope使用,用于声明和初始化全局数据。- 事件处理:自己虚拟DOM结构所以不支持普通事件,通常通过它自己的事件指令来调用通过$scope挂在到控制器中的函数执行完成,事件指令其实就是对于常见事件的封装,以
ng-
开头,加上事件名称即可,如鼠标单击事件ng-click
。
控制器Controller
- 主要对于视图中的数据和事件处理函数进行挂载,同时进行一定的业务功能的底层封装和处理。
- 通过$scope进行数据状态的初始化操作
- 通过$scope进行事件处理函数的挂载操作
- 不应该使用控制器做的事情
- DOM操作:使用AngularJS中的数据双向绑定和自定义指令执行操作
- 表单处理:使用AngularJS中的form controls进行操作
- 数据格式化展示:使用AngularJS中的过滤器Filter来进行操作
- 不同控制器之间的数据共享:使用AngularJS中的自定义服务Service进行处理
- 组件生命周期的操作:使用AngularJS中的自定义服务Service进行处理
控制器语法结构
1
2
3
4
5var app = angular.module(“myApp”, []);
app.controller(“myCtrl”, function($scope) {
/* 控制器函数操作部分 */
/* 控制器主要进行数据的初始化操作和事件函数的定义 */
});在AngularJS1.2版本以后,进行了更新,不再添加任何全局控制器,而是将控制器挂载到模块上注册成为局部作用域,方便数据的高可控性和可操作性。(在Angular1.2版本之前,控制器是通过静态函数直接定义的,并且控制器中的变量是作用于全局的。但是全局控制器的作用域造成了控制器数据的全局污染。)
ng-controller="myCtrl"
属性是一个 AngularJS 指令。用于定义一个控制器。AngularJS 使用$scope 对象来调用控制器。控制器的 $scope (相当于作用域、控制范围)用来保存AngularJS Model(模型)的对象。
作用域$scope、$rootScope
- 每个
$scope
都拥有自己控制器的作用域,并且都独立于当前的控制器。AngularJS为每个controller分配一个独立的$scope,Controller之间的关系也对应着$scope之间的关系。 - 每一个AngularJS应用,都有唯一的一个全局作用域范围,也称为根作用域:
$rootScope
;AngularJS中其他的作用域都是这个根作用域的后代/子作用域。 - Scope(作用域) 是应用在HTML(视图)和JavaScript(控制器)之间的纽带。当在控制器中添加 $scope 对象时,视图 (HTML) 可以获取了这些属性。视图中,你不需要添加 $scope 前缀, 只需要添加属性名即可,如:
。
- scope 是模型(Model),本质也是一个 JavaScript 对象,带有属性和方法,这些属性和方法可以在视图和控制器中使用。
- $rootScope 可作用于整个应用中。是各个 Controller 中 scope 的桥梁。用 rootscope 定义的值,可以在各个 Controller 中使用——创建控制器时,将
$rootScope
作为参数传递,可在应用中使用。
$apply
- 用来对绑定在View(视图层)上的数据进行更新。
- AngularJS会在任何一轮JavaScript运行结束时候,去看一下这些被绑定的数据是否发生了变化,如果绑定数据变化了,那么就去改变相应的View。但AngularJS并不是智能的去看我们的绑定数据,它需要我们用
$scope.$apply
来显式的告诉它。 - AngularJS会在ng-click事件、$http回调等自动触发apply。注意,这个时候不要手动去调用apply,否则反而会报错
- 可是,如果需要在一个新的JavaScript轮回中调用一个方法,并且这个方法不是用AngularJS库方法调用时,就需要使用
$scope.$apply
,比如你自己根据某些数据来判断,然后手动的修改了某一个绑定数据,那么你就需要手动调用这个方法。
$broadcast、$emit、$on事件机制
- broadcast和emit用于发布事件,
$scope.$broadcast('EVENT_NAME', 'Data to send')
$broadcast
是自上而下的广播,所有能听到的都可以对其进行反应——从一个$scope上通过$broadcast发布的事件,他的所有后代$scope都可以对此事件做出响应。- 通过
$emit
发布的事件,只有他的祖先$scope可以做出响应,其兄弟$scope将无法订阅。,并且其中任一祖先都可以将此事件终结掉,不让其继续传播(event.stopPropagation(); // 终止事件继续“冒泡”
)。
- on用于订阅事件。事件名称
EVENT_NAME
是事件的唯一标识$scope.$on('EVENT_NAME', function(event, args) {// todo...})
- 退订事件的方法很特别:on订阅时会返回一个函数,调用它就能退订。
$rootScope
是特殊的,通过$rootScope的$broadcast发布的事件可以被所有$scope接收到,包括$rootScope;而$rootScope.$emit发布的事件,只能通过$rootScope.$on订阅,而其他$scope对此一无所知。其事件退订也比较特殊,on返回的函数需要在其所在的$scope里的$destory里退订:1
2
3$scope.$on('$destory', function() {
deregister(); // 退订事件
});事件命名规范:在AngularJS的事件机制中,因为事件可能会跨函数,甚至可能跨文件,所以对于事件名一定要保证唯一性,所以建议事件名都加上特定的前缀,以便区分。——这其实也是所谓约定大于配置了。一般推荐用冒号
:
来分割。比如$rootScope.$on('2018Anniversary:prize:get', function (e, args) { // todo...})
服务Service
- 服务是一个函数或对象,可在你的 AngularJS 应用中使用
- AngularJS 内建了30多个服务,AngularJS 会一直监控应用,处理事件变化, AngularJS 使用
$location
服务比使用window.location
对象更好。 - 要使用自定义服务,需要在定义控制器的时候独立添加,设置依赖关系(就如同$scope那样,传参的形式引入)
ng-include
- 包含 HTML 内容,如
<div ng-include="'runoob.htm'"></div>
- 还可以包含 AngularJS 代码(其实就是引用的文件里就是有ng代码)
默认情况下, ng-include 指令不允许包含其他域名的文件。需要包含其他域名的文件,你需要设置域名访问白名单。
1
2
3
4
5
6var app = angular.module('myApp', [])
app.config(function($sceDelegateProvider) {
$sceDelegateProvider.resourceUrlWhitelist([
'http://c.runoob.com/runoobtest/**'
]);
});
依赖注入
- 依赖注入(Dependency Injection,简称 DI)是一种软件设计模式,在这种模式下,一个或更多的依赖(或服务)被注入(或者通过引用传递)到一个独立的对象(或客户端)中,然后成为了该客户端状态的一部分。使得程序设计变得松耦合,并遵循了依赖反转和单一职责原则。与服务定位器模式形成直接对比的是,它允许客户端了解客户端如何使用该系统找到依赖。
- AngularJS 提供很好的依赖注入机制。以下5个核心组件用来作为依赖注入:
- value:一个简单的 javascript 对象,用于向控制器传递值(配置阶段
- factory:一个函数用于返回值。在 service 和 controller 需要时创建,通常我们使用 factory 函数来计算或返回值。
- service
- provider:创建一个 service、factory等(配置阶段),Provider 中提供了一个 factory 方法 get(),它用于返回 value/service/factory
- constant:用来在配置阶段传递数值,注意这个常量在配置阶段是不可用的
路由注册和导航
<div ng-view></div>
angular.module('routingDemoApp',['ngRoute'])
一些项目上的做法
- Controller层和Service层的关系:看起来好像都是JS逻辑,能公用的被抽离出来成Service
- Scope可以向上追溯,但不能跨Controller
- 为什么有一些haml上有
ng-controller
,有一些没有?(是ionic的约定>配置,还是说在哪里配置了对应关系)是在route里注册的时候关联起来的;有一些没有在路由注册的(比如弹窗之类的)就自己通过ng-controller
指令绑定 - 过滤器是怎么引入的,在哪里有配置,还是只要放在filter里都可以引用——app.coffee
- 引用关系,文件层级关系是怎么确定的——
.run
这个是ng的,app.coffee里是根 - 父子关系其实就是通过Dom的嵌套关系产生的
- module的名字并不是命名空间,注入Service的时候,也是注入Factory的名字,并且,开发的时候要注意避免重名问题。