# AngularJS
## AngularJS概述

### 介紹
- 簡稱:`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應用程序。
```
- 單頁面應用程序:
+ 
- 傳統多頁面應用程序:
+ 
### 優勢
- 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 文本框值的變化會導致數據模型的變化,數據模型的變化也會導致表達式的變化

### 案例強化
- 加法計算器案例
## 查看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

```
## $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模型對象。
```