# AngularJS
## AngularJS概述
![Angular图标](http://upload-images.jianshu.io/upload_images/267368-9085d773561de7b8.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
### 介绍
- 简称:`ng`
- Angular是一个MVC框架
```
AngularJS 诞生于2009年,由 Misko Hevery 等人创建,后为Google所收购。是一款优秀的前端JS框架,已经被用于Google的多款产品当中。
AngularJS有着诸多特性,最为核心的是:
MVC、模块化(编程)、自动化双向数据绑定、语义化标签、指令、依赖注入等等。
```
- 其他前端框架: VueJS 、 Avalon 、 React 、 BackBone 、 KnockoutJS
- 什么是MVC?
```
1. Model模型 主要是负责业务数据的处理,前端项目中的表现为js变量
2. View视图 主要是业务数据在用户面前显示和数据的收集,前端项目中的表现为html代码
3. Controller控制器 主要负责是业务数据的curd操作【操作模型里面的数据,调用模型来对数据处理】,协调模型和视图之间的关系,前端项目中的表现为function
```
### Angular的核心特性
- **指令**、**插值表达式{{ express }}**、**MVC**、**模块化**、**双向数据绑定**、**依赖注入**
### 原则
- 不推崇开发人员手动操作DOM, 其底层还是操作DOM
- 解放双手,简化了HTML的操作(从DOM中解放出来)
### 优势
- Angular 最大程度的减少了页面上的 DOM 操作
- 让 JavaScript 开发专注业务逻辑
- 代码结构更合理
- 维护成本更低
- 通过简单的指令把页面结构和数据结合
- 通过自定义指令实现组件化编程
### 使用场景
- AngularJS主要考虑的是构建 CRUD 应用,一般是:单页面的应用程序。
## SPA -单页应用程序
- SPA: `Single Page Application`
- 介绍:
```
单页Web应用(single page application,SPA),就是只有一个Web页面的应用,
是加载单个HTML页面,并在用户与应用程序交互时动态更新该页面的Web应用程序。
```
- 单页面应用程序:
+ ![web-spa.png](http://upload-images.jianshu.io/upload_images/267368-aec55f1a9e3de5ee.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
- 传统多页面应用程序:
+ ![web-traditional.png](http://upload-images.jianshu.io/upload_images/267368-a1ebd35ab08a3bb3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
### 优势
- 1 无刷新加载页面,避免了不必要的跳转和重复渲染
- 2 更好的用户体验,让用户在web app感受native app的速度和流畅
- 3 减少了请求体积,节省流量,加快页面响应速度
- 4 可以选择性的保留状态,如音乐网站,切换页面时不会停止播放歌曲
- 传统ajax的劣势:
```
1 ajax请求不会留下历史记录
2 用户无法直接通过URL直接进入指定页面
3 ajax对SEO不友好
```
### 劣势
- 不利于SEO,但是有其他解决方案
### 主要技术点
- 1 ajax
- 2 锚点的使用(window.location.hash #)
- 3 hashchange 事件
### 实现思路
- 监听锚点值变化的事件,根据不同的锚点值,请求相应的数据
- 1 锚点(#)原本用作页面内部进行跳转,定位并展示相应的内容
- 2 NG中,锚点被用作请求不同资源的标识,请求数据并展示内容
### 实例和参考
- [SPA - 百度百科](http://baike.baidu.com/item/SPA/17536313#viewPageContent)
- [一种SPA(单页面应用)架构](https://segmentfault.com/a/1190000000607661)
- [网易云音乐](http://music.163.com/)
## AngularJS的基本使用
- AngularJS 是自动执行的,只需要我们告诉它要做什么,在哪个位置去做
### 案例
- 1 Hello World案例
- 2 文本框的值加1案例
### 使用步骤
- 1 引入 NG 的js文件
- 2 设置 `ng-app` 指令
- 3 给文本框添加 `ng-model` 指令
- 4 给按钮添加 `ng-click` 指令
## directive -指令
- AngularJS 有一套完整的、可扩展的、用来帮助 Web 应用开发的指令集
- 指令是DOM元素上的一些标记,让NG给DOM元素添加一些特殊的行为
- 指令包含:内置指令 和 自定义指令
### 指令是什么
- 将前缀为 `ng-` 的属性称之为指令,其作用是为DOM元素绑定数据、添加事件 等
```html
<input type="text" ng-model="userName">
```
- 指令的值是一个:表达式
### 指令的类型
- 属性(A)、元素(E)、类(C)、注释(M)
### 常用指令
#### ng-app
- 作用:该指令用来启动一个AngularJS应用
- 理解:指定AngularJS应用程序管理的边界,只有在ng-app内部的指令才会起作用
- 解释:
```
ng-app 指令指定了应用的根元素,通常放置在页面的根元素,也可以是任意的元素
例如:body或html标签
应用程序运行时,会自动执行边界内部的其他指令。
标记的范围尽可能小,提高性能
注意:每个页面中可以出现多次 `ng-app` 指令(不推荐!)
如果是多个需要手动引导:`angular.bootstrap()`
```
#### ng-click
- 作用:用来指定DOM元素被点击时执行的事件
- 语法:`ng-click="expression"`
```html
<button ng-click="val + 1"></button>
```
#### ng-model
- 作用:绑定数据,在 input/select/textarea 标签中使用
- 说明:
```
ng-model指令将尝试把属性绑定到当前作用域中。
如果当前作用域中没有该属性,那么AngluarJS会帮我们隐式创建并且添加到当前作用域中。
```
#### ng-init (了解)
- 作用:初始化属性的值
- 语法:`ng-init="uName='Jack'"`
## expression -表达式
- 介绍:是一些JavaScript的代码片段主要被用在插值绑定或者直接作为指令的属性值
```
从JS角度,使用运算符和数据 连接起来的有 结果 的代码就是:表达式
注意:不带分号
例如:
可以使用 console.log(); 打印出来, 或者
console.log( expression );
可以用作 赋值运算符 的右值
var test = expression;
```
```html
<p>{{user.name}}</p>
<p>{{1 + 8}}</p>
<p>{{"hello" + "world"}}</p>
<div ng-click="sayHi()"></div>
```
## AngularJS的执行过程分析
- 示例代码:
```html
<body ng-app>
<input type="text" ng-model="user.name" />
<p>Hello {{user.name}}</p>
</body>
```
### 执行过程说明
- 1 `ng-app`告诉AngularJS让它来管理 body内部的代码
- 2 `ng-app`指令创建了一个对象,对象中包含了AngularJS的相关内容,例如:数据模型
- 3 `ng-model`指令查询数据模型中有没有 `user` 对象以及`name`属性,没有则创建
- 4 创建`user`对象以及`name`属性,并初始化`name`值为:空字符串
- 5 表达式 `{{user.name}}` 从数据模型中查找有没有该数据,如果有就取出来,并展示
- 6 `ng-model`和`{{}}` 中的 user.name 指向的是数据模型中同一个数据
- 7 文本框值的变化会导致数据模型的变化,数据模型的变化也会导致表达式的变化
![ng执行原理-图解.png](http://upload-images.jianshu.io/upload_images/267368-ce78512536a95e45.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
### 案例强化
- 加法计算器案例
## 查看AngularJS的文档
- 目标:学会查看官方文档资料
### 离线文档和在线文档
- [AngularJS官方文档](https://angularjs.org)
- [ng中文文档 - 1](http://www.apjs.net/)
- [ng中文文档 - 2](http://www.angularjsapi.cn/)
## module -模块
- 所有的其他内容,都是基于模块的,有模块才有其他的内容!
```
模块是一个容器包含了应用程序的不同组成部分,并且这些内容必须要依附于一个模块
例如:controllers, services, filters, directives, configs 等
模块是应用程序的组成单元,例如:登录模块、注册模块、商品列表模块 等,这些模块
组合在一起构成了一个完整的应用程序。
```
### 创建模块
- 语法:`var app = angular.module(moduleName, []);`
- 作用:创建一个模块,让AngluarJS对整个内容进行模块化管理
- 说明:模块也可以被创建多次,但很少这么做
- 示例:
```js
// 第一个参数:模块名称,字符串
// 第二个参数:数组,用来添加当前模块的依赖项
var app = angular.module("firstApp", ["otherModuleName"]);
```
### 获取模块
- 语法:`var app = angular.module(moduleName);`
- 作用:获取指定的模块
## controller -控制器
- 需要配合`ng-controller`指令来使用
### 创建控制器
- 语法:`app.controller(ctrlName, callback);`
- 作用:创建一个控制器,控制器必须出现在某个模块下
- 示例:
```js
app.controller("DemoController", function($scope) {
// $scope 相当于当前的数据模型
});
```
### 控制器的作用
- 1 初始视图中使用的数据,是存储数据的容器
- 2 通过$scope对象把数据模型或函数行为暴露给视图
- 3 监视模型的变化,做出相应的逻辑处理
### $scope的说明
- 1 $scope是控制器和视图之间的桥梁,用于在控制器和视图之间传递数据
- 2 推荐:给 $scope 添加数据应该使用对象,而不是作为其属性
- 2 在控制器中暴露 数据模型(数据和行为),在视图中通过指令或表达式来使用
+ 对比:局部变量
- 4 注意:`$scope`这个名称必须这么写!
- 5 `$scope` 是在控制器创建的时候,被注入进去的
```
1 ng 在使用的时候,页面中只要有 ng-app 就会创建一个 scope,名字是:$rootScope
2 $scope 是 HTML(视图)背后的“男人” ---->
视图:女人,负责美(展示)
$scope:男人,负责提供美的资源(数据)
3 所有的控制器都继承自 $rootScope
4 继承是按照:原型式继承 来实现
5 对于HTML来说,参照原型式继承:子节点继承自父节点
```
## 数据绑定方式
### 简介
+ 数据绑定的方式一般有单向数据绑定和双向数据绑定,在angularJS里面实现的是双向数据绑定,那么什么是双向数据绑定呢?既然是双向,那么数据的绑定方向就是两个方向。
+ 方向一: **Model绑定到View** 当模型的数据发生变化后,View会自动立马同步更新
+ 实现方式: {{ }} ngBind ngIf ngRepeat 等几乎可以显示数据数据的指令都可以实现
+ 方向二: **View*绑定到Model**当用户在视图中修改了HTML元素的值(即修改了表单控件的值),可以绑定到一个模型变量上面,此后,不论何时改变了表单控件的值,模型变量的值会立即随之改变。
+ 实现方式:只有ngModel可以实现,可以使用$scope.$watch来监视模型变量的变化
### 双向数据绑定
- 一般通过 `ng-model` 指令实现
- 概述:
```
数据模型的值发生改变,就会导致页面值的改变;页面值的改变,也会导致数据模型中值的改变,
这种相互影响的关系就是双向数据绑定。
```
### 单向数据绑定
- 一般通过 `{{}}` 表达式来实现
- 概述:数据模型的值发生改变,导致页面的值发生改变
## MVC 与 MVVM
- 优势:代码分离(视图代码、控制器代码),职责分离,解耦
- 目的:解决应用程序展示结构、业务逻辑之间的紧耦合关系,实现模块化和复用
- 提高了代码的结构和可维护性,但是不会提高代码执行的效率
### MVC介绍
```
MVC(Model–view–controller)是一种软件架构模式,
把软件系统分为三个基本部分:模型(Model)、视图(View)和控制器(Controller)。
MVC是一种应用程序的设计思想(不是设计模式)
```
- Model 进行数据的存储和数据的处理方法(CRUD)
- View 展示数据
+ 在Angluar中,View指的是在页面中被 `ng-app` 指令包裹的HTML代码
- Controller 是应用程序中处理用户交互的部分
+ 通常控制器负责从视图读取数据,控制用户输入,并向模型发送数据,是数据和视图的桥梁
```
例如:移动端和PC端两个View,共享同一个Model
在MVC设计模式中, Model 响应用户请求并返回响应数据,
View 负责格式化数据并把它们呈现给用户,业务逻辑和表示层分离,
同一个 Model 可以被不同的 View 重用,所以大大提高了代码的可重用性。
```
### MVVM
- 是由 MVC 模式演变出来的!
- 组成:
```
M: model 模型,相当于 User(构造函数)
V: view 视图, ng-app 管理的页面
VM: ViewModel 视图模型 在Angular中就是:$scope
```
#### ViewModel
- 1 $scope实际就是MVVM模式中的VM(视图模型)
- 2 Angular中大量的使用$scope, 盖过了C(控制器)的概念,所以很多人将其称为MVVM框架
- 3 不要深究到底是什么类型(MVC/MVVM),重要的是学会使用。
- 4 `MVW` ===> "Model View Whatever"
- 5 MVVM 首先出现在 微软的WPF 中
### 案例:用户注册
#### localStorage 的基本使用
- `getItem(keyName)`:读取,参数类型:string
- `setItem(keyName, keyValue)`:设置,参数类型:string
#### 参考
- [localStorage - MDN](https://developer.mozilla.org/zh-CN/docs/Web/API/Window/localStorage)
- [localStorage的基本使用](http://www.cnblogs.com/st-leslie/p/5617130.html)
## $watch -监听数据
- 语法:`$scope.$watch(attrName, callback, flag);`
- 作用:监听$scope中数据模型的变化,无法监视其他的数据(例如,普通变量)
- 注意:调用$watch方法时,会立即被调用一次。
```js
app.controller("demoController", function($scope) {
$scope.name = "jack";
// 参数一:表示监听的$scope中的属性名称,类型为:字符串
// 参数二:表示数据变化执行的回调函数,有两个参数分别是:当前值与变化之前的值
// 参数三:比较方式,false:表示比较引用;true:表示比较值。默认值为false
$scope.$watch("name", function(curValue, oldValue) {
// 只要被监听的数据发生变化,就会指定该回调函数中的代码!
// 略过第一次执行
if(curValue === oldValue) return;
});
});
```
## 启动NG的方式
- 1 通过 `ng-app` 指令启动
- 2 手动启动:`angular.bootstrap(document, ['MyModule'])`
```js
// 等待文档加载完成后,启动 angular
angular.element(document).ready(function() {
angular.bootstrap(document, ['MyModule']);
});
```
## 其他
### 多个app
- 注意:不推荐在同一个页面中创建多个 `ng-app`
- 注意:ng只会找到第一个 `ng-app` 并且启动,如果启动其他的,需要手动启动
```html
<div ng-app="FirstApp"></div>
<div ng-app="SecondApp"></div>
```
## 框架和库的区别
### Library
- jQuery is a library, Angular is a framework
- jQuery是API的集合,封装DOM操作,提高开发效率
```
使用jQuery的思路:
1 想要获取元素,我调用 $(selector)
2 元素绑定事件,我调用 .on()
3 进行什么DOM操作,我调用什么方法完成
总结:你告诉jQuery你要做的操作,jQuery就能帮你做好。
**在使用库的过程中,开发人员是 控制者**
```
### Framework
- 框架规定了一种编程方式
- 使用框架的时候,由框架控制一切,我们只需要按照规则写代码
```
Angular提供了一套完整的解决方案,所有的流程都设定好了
我们只需要按照流程规则,把我们的代码进行填坑。
```
### 主要区别是:
- 控制反转,框架中控制整个流程的是框架
- You call Library, Framework calls you.
- 好莱坞原则:Don't call us, we'll call you.
## 其他资料
### angular代码风格
- [johnpapa/angular-styleguide](https://github.com/johnpapa/angular-styleguide)
### 模块化
- [乐高](http://baobao.sohu.com/20151225/n432526267.shtml)
- [乐高-机器人](http://baidu.56.com/watch/05196900615515713942.html?page=videoMultiNeed)
### 参考网站
- [百度CDN](http://cdn.code.baidu.com/)
- [开源中国在线工具](http://tool.oschina.net/)
# AngularJS
## 在Angular中使用"jQuery"
- 语法:`angular.element`
- Angular中操作的功能称为:`jqLite`(轻量级jQuery)
### 示例
```js
// 获取 jqLite 对象
var $ = angular.element;
$(document).ready(function() { });
```
### 注意点
- 1 jqLite 中只实现了jQuery的部分功能
- 2 jqLite中选择器只能是DOM对象
- 3 **尽量使用ng中提供的功能**
## AngularJS的一般开发流程
- 1 引入 angular.js 文件
- 2 创建模块:`angular.module('模块名', [])`
- 3 在页面中指定`ng-app="模块名"`,告诉NG管理指定的页面部分
- 4 创建控制器:`模块名.controller('控制器名', function() {})`
- 5 在页面中指定`ng-controller="控制器名"`,指定管理内容的控制器
- 6 建模(根据页面原型抽象出数据模型),最终得到视图模型(ViewModel)
- 7 将抽象好的数据,添加到 `$scope`中,即:暴露数据和行为给视图
- 8 在页面中使用 `ng-model` 或者 `{{}}` 拿到并绑定数据
## 模块的划分
- 1 按照模块划分(推荐)
- 2 按照文件类型划分
### 按照模块划分
- 根据项目中具体的功能模块进行划分
- 比如:登录模块、注册模块、编写博客 等等不同的功能模块
- 每个功能模块都有自己的 Model View Controller 对应的文件
- 开发过程中,每个人完成一个独立的模块
### 按照文件划分
- 根据文件的功能进行划分
- 将所有的文件放到3个文件夹中:M、V、C
## 控制器(controller) -创建方式
### 低版本(1.2.29之前)
- 使用全局函数创建控制器,会造成:全局污染
### 推断式
```js
angular
.module('testApp', [])
.controller('DemoController', function($scope) {
});
```
### 安全方式创建
- 问题:项目上线的时候,会进行代码压缩,$scope会被修改
- 解决:代码会被压缩和混淆,但是 字符串 是不会被压缩的
#### 创建控制器
- 优势:根据指定的参数名直接获取到想要的参数,而非根据参数的顺序
```js
// 第一个参数:控制器的名称
// 第二个参数:数组,最后一项表示回调函数,除此之外其他的参数表示依赖的参数列表
app.controller("DemoController", ["$scope", "$log", function($scope, $log) {
$log.log("打印日志了");
}]);
```
### 面向对象方式
- 特点:将回调函数当作构造函数来使用,直接使用`this`添加数据
- 也可以通过 `$scope.age` 添加数据
- 注意:在html中使用指令的时候,格式变为:`DemoController as demo`
```html
<div ng-app="testApp" ng-controller="DemoController as demo">
<p>{{demo.name}}</p>
</div>
<script>
angular.module('testApp', [])
.controller('DemoController', ['$scope', function($scope) {
// 添加模型属性
this.name = 'Jack';
}]);
</script>
```
## 依赖注入(DI -Dependency injection)
- 目的: 简化传入参数的操作,防止代码压缩导致参数无法使用的问题
### 原理分析
- 1 获取到依赖项(参数)列表
- 2 查找依赖项所对应的对象
- 3 代码执行时,将其注入
```js
/**
* [提取参数]
* @param {Function} fn [回调函数]
* @return {[type]} [参数列表数组]
*/
function extractArgs(fn) {
var r = /^[^\(]*\(\s*([^\)]*)\)/;
var args = r.exec( fn.toString() );
return args[1].split(',');
}
extractArgs(function($scope, $log) {});
// 方法的返回值:["$scope", "$log"]
```
- 参考文章:[Angular依赖注入分析](http://www.cnblogs.com/etoah/p/5460441.html)
## 解决页面闪烁问题
- 方式一: 将引用angularjs文件放到head中
- 方式二: 使用 `ng-bind` 指令
- 方式三: 使用 `ng-cloak` 指令
### ng-bind 指令
- 作用:设置元素的 `textContent`,功能类似于:`{{}}`
- 注意:只能在双标签中使用(因为只有双标签才有 textContent 属性)
- 注意: `ng-bind`指令无法输出 html 内容(即:实现innerHTML的功能)
```html
<p ng-bind="name"></p>
```
### ng-cloak 指令
- 作用:用来解决表达式闪烁问题
- 原理:angular在加载完成后会移除所有带有"ng-cloak"的样式
- 使用场景:页面中存在大量表达式
```html
<style>
.ng-cloak {
display: none;
}
</style>
<p class="ng-clock">{{name}}</p>
```
## 常用指令介绍
- 指令:就是一个命令,让 Angular 按照我们预先设置好的规则办事
### ngSanitize 模块
- 语法: `ng-bind-html="<div></div>"`
- 作用: 在页面中输出 html内容
- 注意: 这个模块是一个独立的模块(需要单独下载,并在页面中引用)
- 安装: `npm install angular-sanitize`
```html
<div ng-bind-html="name"></div>
<script src="angular-sanitize.js"></script>
<script>
// 引入 ngSanitize 模块
var app = angular.module("testApp", ["ngSanitize"]);
app.controller("testController", ["$scope", function($scope) {
$scope.name = "<h1>雨啊雨</h1>";
}]);
</script>
```
### ng-repeat 指令
- 作用:遍历集合中的数据,为集合中的每条数据创建一个当前元素(即,带有指令的元素)
- 说明:功能类似于 for-in 循环
```html
<ul>
<li ng-repeat="item in datas"></li>
</ul>
<script>
app.controller('TestController', ['$scope', function($scope) {
$scope.datas = [
{name: 'jack', age: 19},
{name: 'tom', age: 21},
{name: 'rose', age: 22}
];
}]);
</script>
```
- 使用 `track by $index` 解决,数据重复的问题
```html
<ul>
<li ng-repeat="item in datas track by $index"></li>
</ul>
```
#### ng-repeat 的循环项属性
- `$odd`/`$even`,用来表示当前项的奇偶性,类型为:布尔值
- `$first`/`$last`/`$middle`,用来表示当前项的位置,类型为:布尔值
- `$index`,用来表示当前项的索引号,从0开始计算
```html
<ul>
<!-- 隔行变色效果的实现 -->
<li ng-repeat="item in datas" class="{{$odd?'red':'green'}}"></li>
</ul>
```
## ng-class指令
- 语法:`ng-class="expression"`,expression是model中的一个数据或表达式
- 作用:根据 expression 的值,给当前元素添加指定的类
### 对象值
- 示例:`ng-class="{red: $odd, green: $even}"`
- 解释:`ng-class`通过指定一个对象(对象字面量),键为:类名,值为:布尔值
- 作用:判断对象中属性的值,如果为true则添加与该属性名相同的类,否则不添加
### 模型中的变量
- 示例:`ng-class="type"`
```html
<div ng-class="type"></div>
<script>
app.controller("demoController", ["$scope", function($scope) {
$scope.type = "red";
}]);
</script>
```
## 其他指令
### ng-hide/ng-show 显示和隐藏(知道)
- 作用:控制当前元素的展示和隐藏,类型为:布尔值
- 语法: `ng-show="布尔值"`
```html
<div ng-show="isShow"></div>
```
### ng-if
- 作用:控当前元素的显示或隐藏状态,这里的隐藏指的是:页面中不存在当前元素
- 语法:`ng-if="布尔值"`
```html
<div ng-hide="false"></div>
```
### ng-switch (了解)
- 作用:类似于js中的switch-case,但一般配合`ng-switch-when`来使用
```html
<div ng-switch="name">
<div ng-switch-when="jack">我是jack</div>
<div ng-switch-when="tom">我是tom</div>
<div ng-switch-when="rose">我是rose</div>
</div>
<script>
$scope.name = "jack";
</script>
```
### 表单元素的指令
- `ng-checked`: 复选框是否选中
- `ng-selected`: 下拉框是否选中
- `ng-disabled`: 是否禁用
- `ng-readonly`: 是否只读
- 特点:都是单向数据绑定,只能实现从数据到视图的绑定
```
ng-checked / ng-selected 可以使用 ng-model 代替, 但是要注意ng-model是双向绑定
```
### 事件指令
- 作用:Angular中用来绑定事件的
```
ng-click / ng-submit / ng-dblclick / ng-blur / ng-focus / ng-change
```
## 兼容HTML5标准的指令
- 说明:HTML5中的自定义属性规定使用 `data-` 作为属性的开头,
angluar中的所有指令完全支持HTML5中的语法
# AngularJS
## TodoMVC案例
- [todomvc官网](http://todomvc.com)
- [todomvc素材](https://github.com/tastejs/todomvc-app-template)
### 功能划分
```
1 展示任务列表
2 添加任务
3 删除一条任务
4 修改任务
5 切换任务选中状态(单个或批量)
6 清除已完成任务
7 显示未完成任务数
8 显示不同状态的任务
以及当前任务高亮处理
9 根据URL变化显示相应任务
11 使用服务抽象数据模型管理
12 使用路由完成不同任务的切换
```
### $location.url()
- 作用:用于获取页面中的锚点值,不包含:`#`
- 注意:`$location` 与 `$scope`一样,都需要通过注入的方式传入
```
URL是: file:///F:/Angular_File/todomvc/index.html#/completed
通过调用 $location.url() 方法获取的是:'/completed'
```
## 过滤器
- 作用:格式化数据/筛选数据的小工具
- 语法:在数据模型的后面加上 `| 过滤器名称: 参数`
- 说明:过滤器通过 `|` 指定,参数通过 `:` 指定
### 格式化数据过滤器
- 作用:对数据进行格式化,以某种指定的格式输出
#### filter过滤器 -过滤数据
- 作用:对数据进行过滤,从多条数据中筛选出符合规则的数据
- 参数:
+ 基本类型参数:angular会根据参数对数据进行全局匹配
+ 对象类型参数:根据参数对象中的属性对数据进行匹配,只会匹配指定的属性
- 注意:配合`track by`使用的时候,`track by` 要放在最后面
```html
<!-- 取出 completed 属性为:true 的数据 -->
<p ng-repeat="item in data | filter:{completed: true} track by $index"></p>
<script>
app.controller('FilterController', ['$scope', '$filter',
function($scope, $filter) {
$scope.data = [
{name: '吃饭', completed: true },
{name: '睡觉', completed: false },
{name: '豆豆', completed: true }
];
}]);
</script>
```
#### currency 过滤器
- 作用:将数字转化为货币的形式显示
```html
<p>{{12345678.333 | currency: "¥"}}</p>
```
#### date 过滤器
- 作用:将整数形式的日期转化为常用日期形式
```html
<p>{{1412345678901 | date: "yyyy-MM-dd hh:mm:ss"}}</p>
```
#### limitTo 过滤器
- 作用:限制显示的文字个数
- 参数:`:5` 表示展示文字长度为:5,`:2` 表示开始的索引号
```html
<p>{{'是谁在唱歌,温暖了寂寞' | limitTo:5:2}}</p>
```
#### orderBy 过滤器
- 作用:对数据进行排序
- 参数:排序的属性,如果是倒序排列,属性名前加`-`,例如:`-age`
- 说明:一般与 `ng-repeat` 指令共同使用
```html
<p ng-repeat="item in data | orderBy: 'age'"></p>
```
### 在JavaScript中使用过滤器
- 语法:
- 使用 `$filter` 方法,参数为:过滤器名称
- `$filter`方法的返回值是一个方法:第一个参数表示要过滤的数据,后面的参数为:过滤器的参数
```javascript
var time = $filter("date")($scope.curDate, "yyyy-MM-dd hh:mm:ss");
```
## service 服务
- 公用(公共)的业务逻辑集中存放的一段代码
- 主要用于对重复业务的封装,达到复用的目的
- 一般主要封装针对于Model的CRUD
- 服务中的代码只会在使用服务的时候,执行一次,并且只会执行一次
- 服务给控制器提供了一些额外的功能
+ $log / $http 等以$开头的服务都是Angular的内置服务
### 创建服务
- 创建服务的语法,与创建控制器的语法相同
- `service`方法中的函数参数,是一个构造函数,通过`this`添加成员
- 控制器中通过服务的名字(实例对象)就可以使用服务的属性和方法
```javascript
app.service('TestService', [function() {
// this.get = function() {};
// this.set = function() {};
// this.update = function() {};
// this.delete = function() {};
}]);
// 在控制器中使用自定义服务
app.controller('DemoController', ['$scope', 'TestService',
function($scope, TestService) {
console.log(TestService);
}]);
```
### 模块之间的依赖关系
```
有三个模块:
1 app.js:主模块,应用程序的入口,实现统一调用所有其他模块
2 controller.js:控制器模块,处理视图中与用户交互的功能,即:处理业务逻辑
3 service.js:服务模块,抽象数据操作,提供数据的增删改查
每个模块都会放在一个独立的js文件中,因此,每个文件都会有一个模块,
即:angular.module("模块名", []);
建立模块之间的联系:
在 app.js 主模块中,引入:controller 和 service这两个模块
```
## ngRoute -路由
- 语法:`app.config(['$routeProvider', function($routeProvider) {}])`
- 安装:`npm install angular-route` 单独安装
- 注意:`ngRoute` -路由模块名称,作为依赖模块
### 使用步骤
- 1 引入 angular-route.js 文件
- 2 创建模块的时候,将`ngRoute`作为依赖项引入
- 3 通过调用模块的`config`方法来配置路由,并将`$routeProvider`注入进来
- 4 通过`$routeProvider`的两个方法:`when()`和`otherwise()`进行路由配置
- 5 在视图中,通过指令`ng-view`展示路由对应的内容
- 6 1.6 版本以后,添加 `$locationProvider.hashPrefix('')` 配合 `#/users` 使用
```html
<div ng-app="routeApp">
<a href="#/stu/li"></a>
<p>a</p>
<p>b</p>
<div ng-view></div>
<p>c</p>
<p>d</p>
</div>
<script>
var app = angular.module('routeApp', ['ngRoute']);
app.config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/stu/li', {
template: '<p>李四</p>'
});
}]);
</script>
```
### when() 方法
- 参数:
+ 第一个参数:url的hash值,例如:`/stu/li`
+ 第二个参数:是一个对象,对象中属性用来控制路由的相关功能
- `template`:指定路由的模板,显示在`ng-view`指令所有的html元素中
- `templateUrl`: 作用与 template 相同,取值:模板id 或者 路径
- `controller`: 为路由指定一个控制器,用于提供当前视图中的数据模型
```javascript
app.config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/stu/li', {
template: '<p>你好,我是{{name}}</p>',
controller: 'stuController'
});
}]);
app.controller('stuController', ['$scope', function($scope) {
$scope.name = '小明';
}]);
```
### otherwise() 方法
- 作用:匹配不合法(when无法匹配)的锚点值,与`switch-case`中的 `default` 类似
- 参数: 是一个对象
- `redirectTo`属性:指定默认跳转的锚点值
```javascript
$routeProvider
.otherwise({
redirectTo: '/stu/'
});
```
### $routeParams -路由的服务
- 作用:用于获取路由的参数,是 路由服务,在控制器中使用
- ":name?" 的 ":name"用于匹配URL路径,"?"表示可以省略
- 例如URL为 "#/stu/lisi" ,路由 "/stu/:name" 匹配:lisi
```javascript
app.config(['$routeProvider', function($routeProvider) {
// '/stu/:name?' 用来匹配:/stu/ 或 /stu 或 /stu/xxx 的任意一种
$routeProvider.when('/stu/:name?', {
template: '<p>你好,我是{{name}}</p>',
controller: 'stuController'
});
}]);
app.controller('stuController', ['$scope', '$routeParams', function($scope, $routeParams) {
// $routeParams 是一个对象,对象中包含了一个 name 属性。
// name属性,是路由的 when 方法的第一个参数
console.log($routeParams.name);
}]);
```
### $route -路由的服务
- 作用:控制当前的路由
- `$route.updateParams()`方法:更新路由参数的值
+ 参数:对象,具有路由参数属性,用于指定更新后的锚点值
- 可以使用 `$location.url('/teacher/laowang')` 来修改,路由的URL值
```javascript
app.controller('stuController', ['$scope', '$routeProvider', '$route', function($scope, $routeProvider, $route) {
console.log($routeProvider.name);
// 参数是一个对象,具有路由参数属性的对象
$route.updateParams({name: 'lisi'});
}]);
```
### hashPrefix
- 在 angular-1.6以上的版本,默认使用 `!` 前缀
```html
<a href="#!/users"></a>
<script>
$routeProvider
.when('/users', {
templateUrl: 'view.html',
controller: 'TestController'
})
// 1.6以上,默认值为:'!'
// $locationProvider.hashPrefix('!');
</script>
```
# AngularJS
## WebAPI
- [聚合数据](https://www.juhe.cn/)
- [百度API服务](http://apistore.baidu.com/)
```
API 应用程序编程接口,简单来说,就是:方法
依赖于Web而提供的API称为: WebAPI ,通过URL实现。
可以把 WebAPI 看作是有 输入和输出(I/O) 的方法
根据输入的参数,接口会返回不同的数据
webapi就相当于函数
webapi的参数(?username=小明&pwd=123465) 相当于 函数参数
function fn(username, pwd) {} fn('小明', 123456)
webapi接口返回的数据,就相当于函数的返回值
```
## 豆瓣电影项目
### 豆瓣电影API
- [豆瓣电影](https://developers.douban.com/)
- [加载动画](http://tobiasahlin.com/spinkit/)
```
1 输入上述网址
2 点击最上部的开发文档,进入 豆瓣API快速入门
3 从该页面中找到 'https://api.douban.com/v2/' 这是所有API的URL地址的前半部分
4 点击左侧菜单中的 '豆瓣Api V2(测试版)',进入 豆瓣Api V2(测试版)
5 将页面滑动到底部,找到 '电影Api V2',点击,然后会进入到 Movie API Doc 页面
6 在该页面中即可找到:"正在热映"、"即将上映"、"Top250"
```
- 正在热映API: "/v2/movie/in_theaters"
- 完整的URL: "https://api.douban.com/v2/movie/in_theaters"
### 模块的划分
- 原则:按照功能模块进行划分
```
首页模块、电影详情页模块
有三个模块: "正在热映"、"即将上映"、"Top250"
各个模块之间相互独立, 主模块中引入单个模块即可!
```
### 电影案例思路
- 1 首页模块的搭建
- 2 正在热映、即将上映、Top250模块的搭建
- 3 通过$http服务获取数据,展示列表(in_theaters)
- 4 创建跨域服务,获取数据,展示列表
- 5 实现分页功能
- 6 其他两个模块的功能实现
- 7 统一实现三个模块的功能
- 8 添加加载动画
- 9 导航栏焦点状态高亮处理
- 10 实现搜索功能
- 11 实现电影详情页
### ng-src
- 作用:设置图片的src属性
- 目的:为了解决浏览器优先解析img的src属性的问题
- 其他属性:`ng-href`
```html
![]({{item.iamges.large}})
```
## $http服务
- 说明:提供了XHR的功能,类似于jQuery中的$.ajax()
### $http.get
- 作用:发送get请求
- 语法:`$http.get(url, [option])`
```javascript
app.controller('DemoController', ['$scope', '$http', function($scope, $http) {
// 路径最好使用绝对路径
$http.get('url').then(function(response) {
// 成功的回调函数
}, function() {
// 失败的回调函数
});
}]);
```
## JSONP -实现跨域
### JSONP跨域原理分析
- 动态创建script标签并添加到页面中,浏览器会根据script标签的src属性发送请求
- script标签的src属性带有:'?callback="jsonpcallback"' 参数
- 由服务器返回的是:函数调用,格式为:';jsonpcallback({})'
### 其他跨域方式
- `window.name`:同一个标签也中的页面共享同一个 name 属性
- `iframe`
- `postMessage`
## 2 $http.jsonp -实现跨域
- 说明:angular为了防止全局污染,把JSONP的回到函数放在`angular.callbacks`对象中
- 注意:豆瓣API 支持JSONP方式的调用,但是不支持包含点的情况!
- 结论:无法使用angular的内置 $http.jsonp 跨域访问豆瓣API中的数据
```javascript
$http.jsonp("url地址?callback=JSON_CALLBACK").then();
// 获取手机号码归属地
// http://v.showji.com/Locating/showji.com2016234999234.aspx?m=13333333333&output=json&callback=JSON_CALLBACK×tamp=' + (new Date()-0)
```
## $scope.$apply()
- 作用:强制让 angular 监视数据的变化
- 注意:angular的内置方法,会自动调用$apply执行脏检查
- 说明:
```
1 angular代码执行会触发 Dirty Check 机制,进行数据的双向绑定
2 异步操作是在angular代码执行完毕之后才执行的
3 也就是说,angular代码执行完了,脏检查已经执行完毕,才执行的异步回调
4 此时,可以在异步操作中手动调用 $scope.$apply() 方法告诉angular让其立即执行一次 Dirty Check
5 执行完毕,angular知道了数据变化,就会展示出我们想要的数据
如果没有调用 $scope.$apply,数据已经改变了,但是双向绑定没有触发。
```
## 自定义指令
- 概述:
```
1 自定义指令用于扩展和增强HTML
2 用于封装一些常用而且共用的功能
3 AngularJS仍然有DOM操作,所有的DOM操作都应该集中在自定义指令中
4 内部指令基本满足我们平时开发的需求, 少数情况的一些特殊需求,会用到自定义指令
```
### 创建指令
- 语法:`模块.directive('指令名称', callback)`
- 说明:创建指令的语法与创建控制器的语法完全相同
```js
// 第一个参数:表示指令的名称,使用驼峰命名法,在视图中使用时修改为`-`分割的形式
// 第二个参数:是一个回调函数,让用户设置该指令的行为
angular.module('testApp', [])
.directive('myBtn', [function() {
return {};
}]);
```
### 指令常用属性说明
- `template`: 模板,设置自定义指令显示的内容
- `templateUrl`: 可以指定一个模板的id或者url地址
+ id:模板的id,需要给模板设置type属性为:`type="text/ng-template"`,该模板需要在`ng-app`内
+ url: 一个页面,页面中用于存放模板标签 (agnular会异步请求该路径,注意跨域问题)
- `restrict`: 限制指令的使用方式,取值:'E'/'C'/'M'/'A',取值是区分大小写的
```html
<!-- 标签 -->
<my-btn></my-btn>
<!-- 类名 -->
<div class="my-btn"></div>
<!-- 注释 -->
<!-- directive:my-btn -->
<!-- 属性 -->
<div my-btn></div>
```
- `link`: 该属性的值是一个函数,这个函数给当前指令提供了事件,该函数有3个参数
+ `scope`: 表示当前指令的作用域,用来暴露一些数据,类似与控制器的scope,只在当前指令中有效
+ `element`: 表示一个jqLite对象,是自定义指令所在标签对应的jqLite对象
+ `attribute`: 表示自定义指令所在标签的所有指令属性的集合
+ link 用于控制指令的行为
- `replace`: 需要一个布尔值。为true时,会将自定义指令所在的标签替换为模板字符串
### 参考文章
- [自定义指令参考](http://www.cnblogs.com/powertoolsteam/p/angularjs-custom-directive.html)
- [bootstrap指令库](http://angular-ui.github.io/bootstrap/)
## 面试题 angularJS最大的不足是什么呢?
```
angularJs内部的双向数据的绑定是通过脏检测来实现的,在angularJS的内部维持一个 $digest队列,每当在view视图里面使用 插值表达式 或者 指令 的时候,则会向该队列里面创建一个对应的回调函数fn来和对应的插值表达式或者指令相对应;最后当$scope模型对象里面的数据发生变化后,则会立马循环遍历当前的$digest队列,查看每个fn回调函数里面对应的模型数据是否发生变化,如果发生变化,则立马影响视图上面的变化。但是需要注意的是angularJS的脏检测的循环次数最大为10次,如果超过10次,则不再检测,防止无限循环。
解决方法: 最好将页面划分多个区域,每个区域单独创建属于自己的控制器,然后在控制器里面创建属于自己的$scope模型对象。
```