1. 介紹
AngularJS是一款由Google公司開發維護的前端MVC框架,其克服了HTML在構建應用上的諸多不足,從而降低了開發成本提升了開發效率。
1.1 特點
AngularJS與jQuery有什么區別?
jQuery更準確來說只一個類庫(類庫指的是一系列函數的集合),是以DOM做為驅動。
而AngularJS則一個框架(諸多類庫的集合)是以數據和邏輯做為驅動。
框架對開發的流程和模式做了約束,開發者遵照約束進行開發,更注重的實際的業務邏輯。
AngularJS有著諸多特性,最為核心的是:模塊化、雙向數據綁定、語義化標簽、依賴注入等。
1.2 MVC
MVC是一種開發模式,把網頁分成模型(Model)、視圖(View)、控制器(Controller)3部分構成。
模型(Model)用來處理數據(讀取/設置),一般指操作數據庫。
視圖(View)用來展示數據,比如通過HTML展示。
控制器(Controller)用做連接模型和視圖的橋梁。
MVC更多應用在后端開發程序里,后被引入到前端開發中,由于受到前端技術的限制便有了一些細節的調整,進而出現了很多MVC的衍生版(子集)如MVVM、MVW、MVP、MV*等。
2. AngularJS的模塊化
使用AngularJS構建應用(App)時是以模塊化(Module)的方式組織的,即將整個應用劃分成若干模塊,每個模塊都有各自的職責,最終組合成一個整體。采用模塊化的組織方式,可以最大程度的實現代碼的復用。
2.1 使用AngularJS的HTML基本結構
2.1.1 定義應用
通過為任一HTML標簽添加ng-app屬性,可以指定一個應用,表示此標簽所包裹的內容都屬于應用(App)的一部分。
<!-- 為html標簽添加ng-app表明整個文檔都是應用 -->
<!-- ng-app屬性可以不賦值,但是要關聯到相應模塊時則必須賦值 -->
<html lang="en" ng-app='App'>
2.1.2 定義模塊
AngularJS提供了一個全局對象angular,在此全局對象下存在若干的方法,其中angular.module()方法用來定義一個模塊。
// 通過module方法定義模塊
// 有兩個參數
// 第一個:表示模塊的名稱
// 第二個:表示此模塊依賴的其他模塊
var app = angular.module( 'app', [] );
2.1.3 定義控制器
控制器(Controller)作為連接模型(Model)和視圖(View)的橋梁存在,所以當我們定義好了控制器以后也就定義好了模型和視圖。
// app是一個模塊實例對象
// 通過這個實例對象定義控制器
// 傳入兩個參數
// 第1個:定義控制器的名稱
// 第2個:一個數組,數組的最后一項是函數,其余是字符串,寫明此控制器的依賴關系
app.controller('StarController', ['$scope', function($scope){
$scope.students = [
{name: '周杰倫', gender: '男', age: 39},
{name: '劉德華', gender: '男', age: 60},
{name: '孫燕姿', gender: '女', age: 36},
{name: '王力宏', gender: '男', age: 38},
{name: '陳小春', gender: '男', age: 42},
]
}]);
2.1.4 在視圖中關聯控制器
模型(Model)數據是要展示到視圖(View)上的,所以需要將控制器(Controller)關聯到視圖(View)上,通過為HTML標簽添加ng-controller屬性并賦值相應的控制器(Controller)的名稱,就確立了關聯關系。
<!-- 添加ng-controller屬性,并賦值為相應的控制器名稱 -->
<table ng-controller='StarController'>
<tr>
<th>姓名</th>
<th>性別</th>
<th>年齡</th>
</tr>
<tr ng-repeat='student in students'>
<td>{{student.name}}</td>
<td>{{student.gender}}</td>
<td>{{student.age}}</td>
</tr>
</table>
2.1.5 整個HTML文檔的結構
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>AngularJS的基本結構</title>
</head>
<body ng-app='app'>
<table ng-controller='StarController'>
<tr>
<th>姓名</th>
<th>性別</th>
<th>年齡</th>
</tr>
<tr ng-repeat='student in students'>
<td>{{student.name}}</td>
<td>{{student.gender}}</td>
<td>{{student.age}}</td>
</tr>
</table>
<script src="./libs/angular.min.js"></script>
<script>
var app = angular.module('app', []);
app.controller('StarController', ['$scope', function($scope){
$scope.students = [
{name: '周杰倫', gender: '男', age: 39},
{name: '劉德華', gender: '男', age: 60},
{name: '孫燕姿', gender: '女', age: 36},
{name: '王力宏', gender: '男', age: 38},
{name: '陳小春', gender: '男', age: 42},
]
}]);
</script>
</body>
</html>
3.指令
3.1 常用內置指令
ng-app
指定應用根元素,至少有一個元素指定了此屬性。不能互相嵌套。
ng-controller
指定控制器
ng-show
控制元素是否顯示,true顯示、false不顯示
ng-hide
控制元素是否隱藏,true隱藏、false不隱藏
ng-if
控制元素是否“存在”,true存在、false不存在(DOM元素存在與否)。
ng-src
增強圖片路徑。如果圖片路徑是src = {{path}}
這種形式,并且模型寫在文檔下面,瀏覽器從上至下依次解析代碼到這里時,還沒有解析到模型,當快速刷新網頁時,就會顯示圖片沒有請求到。如果是ng-src={{path}}
,執行到這里會強制先解析模型中的path,就不會出現未請求到的問題,并且解決了重復請求的問題。
ng-href
增強地址
ng-class
控制類名。它的值可以有兩種形式。ng-class = {{active}}
和ng-class = {{active : true}}
,boolen值控制該類名是否存在。
ng-include
引入模板。可以加載一個外部文件。應用時,可以把一些公共部分抽離成一個獨立文件,然后用ng-include = 'src'
引入。在沒用AngularJS之前,用的是<?php include(demo.html) ?>
。但是,要注意的是,ng-include
的地址要是同域的,這是因為,js本身讀取不了本地文件,需要借助請求的方式,并且這種請求不能是跨域的。
ng-disabled
表單禁用。值為boolen值。ng-disabled = true
表單禁用。
ng-readonly
表單只讀
ng-checked
單/復選框表單選中,值為boolen值。checked是一個無值屬性,ng-checked = true
時,就會存在checked這個屬性。
ng-selected
下拉框表單選中。selected也是一個無值屬性,ng-selected = true
時,選中。
ng-repeat
遍歷
3.2 自定義指令
AngularJS允許根據實際業務需要自定義指令,通過angular全局對象下的directive方法實現。
<body ng-app="App">
<div haoxiang></div>
<haoxiang></haoxiang>
<div class="haoxiang"></div>
<!-- Angular 只認識帶著directive的注釋 -->
<!-- directive:haoxiang -->
<script src="./libs/angular.min.js"></script>
<script>
var App = angular.module('App', []);
// 自定義指令
// App.directive()方法來實現自定義指令
// 第一個參數,指令名稱
// 第二個參數,是一個回調函數,在回調函數中實現指令邏輯
App.directive('haoxiang', function () {
// 在此實現指令的邏輯
// 返回一個對象
// 對象里的內容是固定的
return {
// A 是單詞 attribute 首字母,將指令當屬性來用,帶有haoxiang這個屬性的元素就加載下面的模板
// E 是單詞 element 首字母,將指令當標簽來用,haoxiang標簽就加載下面的模板
// C 是單詞 class 首字母,將指令當類來用,擁有haoxiang這個類名的元素就加載下面的模板
// M 是單詞 mark 首字母,將指令將注釋來用
restrict: 'ECMA',
replace: true,
// 定義模板
template: '<div><h1>Hello AngularJS!</h1>',
// 要求所有html模板中的元素,在同一個根元素下
template: '<div><h1>Hello AngularJS!</h1><a href="#">百度</a></div>',
// 下面這種寫法就是錯的,因為兩個元素不在一個根元素下
template: '<h1>Hello AngularJS!</h1><a href="#">百度</a>',
// 當模板內容太多,可以把模板放在獨立的html文檔中
// 注意:使用這種方式,要放在服務器環境下,保證同域名
// 同樣,這個html文檔中的各個元素也要在同一個根目錄下
templateUrl: './Hello.html',
// 也可以添加一些方法,比較復雜,這里不做介紹
link: function () {
//
},
compile: function () {
//
}
}
});
</script>
</body>
4.數據綁定
AngularJS是以數據做為驅動的MVC框架,所有模型(Model)里的數據經由控制器(Controller)展示到視圖(View)中。
所謂數據綁定指的就是將模型(Model)中的數據與相應的視圖(View)進行關聯,分為單向綁定和雙向綁定兩種方式。
4.1 單向綁定
單向數據綁定是指將模型(Model)數據,按著寫好的視圖(View)模板生成HTML標簽,然后追加到DOM中顯示。
4.2 雙向綁定
雙向綁定則可以實現模型(Model)數據和視圖(View)模板的雙向傳遞。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>數據綁定</title>
</head>
<body ng-app='App'>
<div class="box" ng-controller="DemoCtrl">
<form action="">
<input type="text" ng-model="msg">
<input type="button" value="顯示" ng-click="show()">
</form>
<p>您輸入的是:{{msg}}</p>
</div>
<script src="./libs/angular.min.js"></script>
<script>
var App = angular.module('App', []);
App.controller('DemoCtrl', ['$scope', function ($scope) {
$scope.show = function () {
alert($scope.msg);
}
}]);
</script>
</body>
</html>
4.3 相關指令
{{}} 和 ng-bind
在AngularJS中通過{{}}
或者ng-bind
指令來實現模型(Model)數據向視圖模板(View)的綁定,模型數據通過一個內置服務$scope來提供,這個$scope是一個空對象,通過為這個對象添加屬性或者方法便可以在相應的視圖(View)模板里被訪問。
{{}}
是ng-bind
的簡寫形式,但是它們還有區別。
第1個區別。ng-bind
不能綁定多個數據,如ng-bind = 'name age'
這種寫法是錯誤的。可以寫成{{name}}{{age}}
。通過ng-bind-template
可以綁定多個數據。
第2個區別。通過{{}}
綁定數據時會有“閃爍”現象,ng-bind
不會有閃爍現象。{{}}
使用方便,為標簽添加ng-cloak
并將script標簽移到上面可以解決“閃爍”現象。原理:頁面載入時,載入到引入angular的script標簽時,會自動在頁面中添加style,為添加了ng-cloak
的標簽設置display=none,隱藏了添加了ng-cloak
的標簽,解析完寫了模型的script后再將其顯示。
ng-model
通過為表單元素添加ng-model
指令實現視圖(View)模板向模型(Model)數據的綁定。
ng-init
通過ng-init
可以初始化模型(Model)也就是$scope。
<body ng-app="App">
<div ng-controller="DemoCtrl" ng-init="title='學習內容'; info='Angular'">
<h1>{{title}}{{info}}</h1>
</div>
<script src="./libs/angular.min.js"></script>
<script>
var App = angular.module('App', []);
App.controller('DemoCtrl', ['$scope', function ($scope) {
}])
</script>
</body>
事件處理
AngularJS對事件也進行了擴展,無需顯式的獲取DOM元素便可以添加事件,易用性變的更強。通過在原有事件名稱基礎上添加ng-做為前綴,然后以屬性的形式添加到相應的HTML標簽上即可。如ng-click、ng-dblclick、ng-blur等。
<body ng-app='App'>
<div class="box" ng-controller="DemoCtrl">
<li ng-click="single()">單擊事件</li>
<li ng-dblclick="double()">雙擊事件</li>
<li ng-mouseover="xuanting()">懸停事件</li>
<li ng-mouseout="likai()">離開事件</li>
</div>
<script src="./libs/angular.min.js"></script>
<script>
var App = angular.module('App', []);
App.controller('DemoCtrl', ['$scope', function ($scope) {
$scope.single = function () {
alert('單擊事件');
}
$scope.double = function () {
alert('雙擊事件');
}
$scope.xuanting = function () {
console.log('鼠標來了');
}
$scope.likai = function () {
console.log('鼠標走了');
}
}]);
</script>
</body>
ng-repeat
通過ng-repeat
可以將數組或對象數據迭代到視圖模板中,ng-switch on
配合ng-switch-when
可以對數據進行篩選。
<body ng-app="App">
<div ng-controller="DemoCtrl">
<ul>
<!-- 兩種寫法 -->
<li ng-repeat="item in list" ng-bind="item"></li>
<li ng-repeat="item in list">{{item}}</li>
</ul>
<ul>
<li ng-repeat="(key, val) in list">
{{key}}:{{val}}
</li>
</ul>
<table>
<tr ng-repeat="(key, val) in obj">
<td>{{key}}</td>
<td>{{val}}</td>
</tr>
</table>
<!-- 例如只有當值為css才顯示 -->
<ul>
<!-- 寫不寫on都可以 -->
<li ng-repeat='item in list' ng-switch on='item'>
<span ng-switch-when='css'>{{item}}</span>
</li>
<li ng-repeat="item in list" ng-switch="item">
<a href="javascript:;" ng-switch-when="js">{{item}}</a>
</li>
</ul>
</div>
<script src="./libs/angular.min.js"></script>
<script>
var App = angular.module('App', []);
App.controller('DemoCtrl', ['$scope', function ($scope) {
$scope.list = ['html', 'css', 'js'];
$scope.obj = {
name: '小明',
age: 18,
sex: '男',
hobby: '吹牛'
}
}]);
</script>
</body>
一個Tab欄切換小demo
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Tab 標簽</title>
<style>
body {
margin: 0;
padding: 0;
background-color: #F7F7F7;
}
.tabs {
width: 400px;
margin: 30px auto;
background-color: #FFF;
border: 1px solid #C0DCC0;
box-sizing: border-box;
}
.tabs nav {
height: 40px;
text-align: center;
line-height: 40px;
overflow: hidden;
background-color: #C0DCC0;
display: flex;
}
nav a {
display: block;
width: 100px;
border-right: 1px solid #FFF;
color: #000;
text-decoration: none;
}
nav a:last-child {
border-right: 0 none;
}
nav a.active {
background-color: #9BAF9B;
}
.cont {
overflow: hidden;
}
.cont ol {
line-height: 30px;
}
</style>
</head>
<body ng-app="Tabs">
<div class="tabs" ng-controller="TabCtrl">
<nav>
<a href="javascript:;" ng-class="{active: active == 'local'}" ng-click="change('local')">國內新聞</a>
<a href="javascript:;" ng-class="{active: active == 'global'}" ng-click="change('global')">國際新聞</a>
<a href="javascript:;" ng-class="{active: active == 'sports'}" ng-click="change('sports')">體育新聞</a>
<a href="javascript:;" ng-class="{active: active == 'funny'}" ng-click="change('funny')">娛樂新聞</a>
</nav>
<div ng-switch on="active">
<section class="cont" ng-switch-when="local">
<ol>
<li>國內新聞</li>
</ol>
</section>
<section class="cont" ng-switch-when="global">
<ol>
<li>國際新聞</li>
</ol>
</section>
<section class="cont" ng-switch-when="sports">
<ol>
<li>體育新聞</li>
</ol>
</section>
<section class="cont" ng-switch-when="funny">
<ol>
<li>娛樂新聞</li>
</ol>
</section>
</div>
</div>
<script src="../libs/angular.min.js"></script>
<script>
var Tabs = angular.module('Tabs', []);
Tabs.controller('TabCtrl', ['$scope', function ($scope) {
// 定義初始狀態
$scope.active = 'local';
$scope.change = function (key) {
$scope.active = key;
}
}]);
</script>
</body>
</html>
5.作用域
通常AngularJS中應用(App)是由若干個視圖(View)組合成而成的,而視圖(View)又都是HTML元素,并且HTML元素是可以互相嵌套的,另一方面視圖都隸屬于某個控制器(Controller),進而控制器之間也必然會產生嵌套關系。
每個控制器(Controller)又都對應一個模型(Model)也就是$scope對象,不同層級控制器(Controller)下的$scope便產生了作用域。
可以參考JavaScript中函數的作用域來理解。
5.1 全局作用域(根作用域)
一個AngularJS的應用(App)在啟動時會自動創建一個根作用域$rootScope,這個根作用域在整個應用范圍(ng-app所在標簽以內)都是可以被訪問到的。
5.2 子作用域
通過ng-controller指令可以創建一個子作用域,每當有一個控制器就有一個子作用域,新建的作用域可以訪問其父作用域的數據。
例子
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>AngularJS 作用域</title>
</head>
<!-- ng-app 所屬標簽所在的作用域就是根作用域 -->
<body ng-app="App" ng-init="name='努爾哈赤'">
<div class="parent" ng-controller="ParentCtrl">
<dl>
<dt>{{name}}</dt>
<dd>1200</dd>
</dl>
<div class="child" ng-controller="ChildCtrl">
<dl>
<dt>{{name}}</dt>
<dd>1100</dd>
</dl>
<div class="son" ng-controller="SonCtrl">
<dl>
<dt>{{name}}</dt>
<dd>1000</dd>
</dl>
</div>
</div>
</div>
<script src="./libs/angular.min.js"></script>
<script>
var App = angular.module('App', []);
App.controller('DemoCtrl', ['$scope', function ($scope) {
}]);
// 什么是AngularJS的作用域:當div的嵌套導致控制器的嵌套,被嵌套的控制器可以訪問到父級控制器中的數據
// 就像函數作用域一樣
// 父
App.controller('ParentCtrl', ['$scope', function ($scope) {
$scope.name = '順治';
}]);
// 子
App.controller('ChildCtrl', ['$scope', function ($scope) {
$scope.name = '康熙';
}]);
// 孫
App.controller('SonCtrl', ['$scope', function ($scope) {
$scope.name = '雍正';
}]);
// 根作用域:ng-app 所屬標簽所在的作用域就是根作用域
// 子作用域:每當有一個控制器,就有一個子作用域
</script>
</body>
</html>
6.過濾器
在AngularJS中使用過濾器格式化展示數據,在“{{}}”中使用“|”來調用過濾器,使用“:”傳遞參數。
6.1 內置過濾器
-
currency
將數值格式化為貨幣格式 -
date
日期格式化,年(y)、月(M)、日(d)、星期(EEEE/EEE)、時(H/h)、分(m)、秒(s)、毫秒(.sss),也可以組合到一起使用。 -
filter
在給定數組中選擇滿足條件的一個子集,并返回一個新數組,其條件可以是一個字符串、對象、函數 -
json
將Javascrip對象轉成JSON字符串。 -
limitTo
取出字符串或數組的前(正數)幾位或后(負數)幾位 -
lowercase
將文本轉換成小寫格式 -
uppercase
將文本轉換成大寫格式 -
number
數字格式化,可控制小位位數 -
orderBy
對數組進行排序,第2個參數布爾值可控制方向,true倒序
例子
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>AngularJS 過濾器</title>
<style>
dl {
line-height: 40px;
}
dt {
font-size: 18px;
font-weight: bold;
}
dd {
margin: 0;
}
</style>
</head>
<body ng-app="App">
<dl ng-controller="DemoCtrl">
<dt>使用過濾器將數據處理成一定的格式再進行展示</dt>
<dd>當前時間為: {{now|date:'yyyy-MM-d h:m:s EEEE'}}</dd>
<dd>價格: {{price|currency:'¥':3}}</dd>
<dd>介紹: {{info|uppercase}}</dd>
<dd>介紹2: {{brief|lowercase}}</dd>
<!-- 過濾器可以被連續使用 -->
<dd>介紹3: {{brief|lowercase|uppercase}}</dd>
<dd>limitTo: {{info|limitTo:4}}</dd>
<dd>limitTo: {{info|limitTo:-4}}</dd>
<dd>limitTo: {{list|limitTo:2}}</dd>
<dd>limitTo: {{list|limitTo:-2}}</dd>
<dd>number: {{num|number:2}}</dd>
<dd>number: {{num1|number}}</dd>
<dd>json: {{list|json}}</dd>
<dd>json: {{obj|json}}</dd>
<!-- 正序 -->
<dd>orderBy: {{list|orderBy}}</dd>
<!-- 默認第二個參數為false,正序 -->
<dd>orderBy: {{list|orderBy:'':false}}</dd>
<!-- 倒序 -->
<dd>orderBy: {{list|orderBy:'':true}}</dd>
<!-- 第一個參數指:以誰為排序方式 -->
<dd>orderBy: {{students|orderBy:'score':true}}</dd>
<!-- 把包含s的拿出來 -->
<dd>filter: {{list|filter:'s'}}</dd>
<!-- 把男的拿出來 -->
<!-- 注意:{} 后面一定要跟著一個空格,否則會報錯 -->
<dd>filter: {{students|filter:{sex:'男'} }}</dd>
</dl>
<script src="./libs/angular.min.js"></script>
<script>
var App = angular.module('App', []);
App.controller('DemoCtrl', ['$scope', function ($scope) {
// 當前時間
$scope.now = new Date();
$scope.price = 12.345;
$scope.info = 'my name is brother';
$scope.brief = 'My age is 18';
$scope.list = ['html', 'js', 'php', 'css', 'photoshop'];
// number 過濾器功能是保留幾個小數位 并不能轉換數值 例如'123abc' 不能 轉成 123
// 但是 字符串'123' 可以轉成 數字123
$scope.num = '12.345';
$scope.num1 = '123abc';
$scope.obj = {
name: 'jack',
age: 10
}
$scope.students = [
{name: '小明', sex: '男', age: 18, score: 94},
{name: '小米', sex: '女', age: 19, score: 92},
{name: '小雷', sex: '男', age: 16, score: 98},
{name: '小花', sex: '女', age: 19, score: 91}
];
}]);
</script>
</body>
</html>
6.2 自定義過濾器
通過模塊對象實例提供的filter方法自定義過濾器。
<body ng-app="App">
<div class="box" ng-controller="DemoCtrl">
{{str|uppercase}}
<!-- AngularJS 在使用"|"調用過濾器時會將前面的數據str當成參數傳遞給下面的自定義函數 -->
{{str|yell:'我是一個參數':123456}}
{{str|slice:3}}
<h1>{{str|capitalize}}</h1>
</div>
<script src="./libs/angular.min.js"></script>
<script>
var App = angular.module('App', []);
App.controller('DemoCtrl', ['$scope', function ($scope) {
$scope.str = 'hello angular!';
}]);
// App.filter()可以自定義過濾器
// 第一個參數 過濾器名稱
// 第二個參數 回調函數
App.filter('yell', function () {
return function (arg, arg2, arg3) {
// arg 就是 str
// arg2 就是 '我是一個參數'
// arg3 就是 123456
console.log(arg); //hello angular!
console.log(arg2); //'我是一個參數'
console.log(arg3); //123456
}
});
// 模擬字符串截取功能
App.filter('slice', function () {
return function (input, length) {
// input 就是需要格式化處理的數據
// length 表示截取幾個長度
return input.slice(0, length);
}
});
// 首字母大寫功能
App.filter('capitalize', function () {
return function (input) {
// input 就是需要格式化處理的數據
console.log(input[0])
return input[0].toUpperCase() + input.slice(1);
}
});
</script>
</body>
7. 依賴注入
AngularJS采用模塊化的方式組織代碼,將一些通用邏輯封裝成一個對象或函數,實現最大程度的復用,這導致了使用者和被使用者之間存在依賴關系。
所謂依賴注入是指在運行時自動查找依賴關系,然后將查找到依賴傳遞給使用者的一種機制。
常見的AngularJS內置服務有$http、$location、$timeout、$rootScope等
依賴注入有兩種方式:推斷式注入和行內注入。
7.1 推斷式注入
沒有明確聲明依賴,AngularJS會將函數參數名稱當成是依賴的名稱。
這種方式會帶來一個問題,當代碼經過壓縮后函數的參數被壓縮,這樣便會造成依賴無法找到。
// 這個控制器依賴$scope, $http服務
// 未明確聲明依賴,這時會自動將函數傳入的參數名當成依賴對待
App.controller('DemoCtrl', function ($scope, $http) {
});
7.2 行內注入
以數組形式明確聲明依賴,數組元素都是包含依賴名稱的字符串,數組最后一個元素是依賴注入的目標函數。
推薦使用這種方式聲明依賴
App.controller('DemoCtrl', ['$scope', '$http', function ($scope, $http) {
}]);
8.服務
服務是一個對象或函數,對外提供特定的功能。
8.1 內建服務
8.1.1 $location
$location是對原生Javascript中location對象屬性和方法的封裝。
<script>
var App = angular.module('App', []);
// $location服務用來獲取地址信息,與原生BOM對象 location 類似
App.controller('DemoCtrl', ['$location', '$scope', function ($location, $scope) {
console.log($location);
$scope.absUrl = $location.absUrl();
$scope.host = $location.host();
$scope.port = $location.port();
$scope.protocol = $location.protocol();
// AngularJS在原有基礎上重新定義了地址的組成部分
// AngularJS認為第1個#后面的?才是請求參數
$scope.search = $location.search();
// AngularJS 認為第1個#號不表示錨點
// 如果再次出現#才認為錨點
$scope.hash = $location.hash();
// path:與原生的pathname相對應
// AngularJS 認為第1個#號開始之后的/之后的部分屬于path,不包含請求參數
$scope.path = $location.path();
}]);
// location.search:地址上 ? 后的數據,表示參數
// location.hash:地址上 # 后的數據,表示錨點,也叫hash
// location.pathname:地址從根(/)開始的部分,不包含請求參數
// AngularJS 實質上是將原生BOM對象的location.hash
// 進行了封裝,重新定義的了 path hash search
// 重新定義之后的path hash search,都是從第一個#之后才開始算
</script>
8.1.2 $timeout&$interval
$timeout&$interval對原生Javascript中的setTimeout和setInterval進行了封裝。
var App = angular.module('App', []);
App.controller('DemoCtrl', ['$scope', '$timeout', '$interval', function ($scope, $timeout, $interval) {
// 延時執行
$timeout(function () {
console.log('延時了3秒')
}, 3000);
// 取消延時
$timeout.cancel();
// 間歇函數
$interval(function () {
console.log(1);
}, 500);
// 一秒刷新一下時間
var timer = $interval(function () {
$scope.now = new Date();
}, 1000);
$scope.stop = function () {
$interval.cancel(timer);
}
}]);;
8.1.3 $filter
$filter在控制器中格式化數據。
var App = angular.module('App', []);
// $filter是過濾器,過濾器可以在視圖中使用{{|}},也可在模型中使用
App.controller('DemoCtrl', ['$scope', '$filter', function ($scope, $filter) {
// AngularJS 自帶的過濾器有9種
// $filter是一個函數,傳遞不同的參數會返回不同具體功能的過濾器
// $filter('date') 其返回值就是一個格式化時間的過濾器
// $filter('currency') 其返回值就是一個格式化貨幣的過濾器
// $filter('uppercase')、$filter('limitTo') ....
// 原始數據
var now = new Date;
// 時間過濾器
var date = $filter('date');
// 格式化數據
now = date(now, 'yyyy-MM-dd H:m:s');
$scope.now = now;
var list = ['html', 'css', 'js', 'php', 'node'];
var limitTo = $filter('limitTo');
list = limitTo(list, 3);
$scope.list = list;
// 原始信息
var content = 'my name is xiaoming';
// 創建過濾器
var uppercase = $filter('uppercase');
// 格式化數據
$scope.content = uppercase(content);
}]);
8.1.4 $log
$log打印調試信息。
App.controller('DemoCtrl', ['$scope', '$log', function ($scope, $log) {
// $log 是一個對象
$log.log('調試信息');
$log.info('調試信息');
$log.error('調試信息,錯了嗎?');
$log.warn('調試信息,警告');
$log.debug('調試信息');
}]);
8.1.5 $http
$http用于向服務端發起異步請求。
App.controller('DemoCtrl', ['$scope', '$http', function ($scope, $http){
// 發起異步請求
$http({
method: 'post',
url: 'demo.php',
data: {name: 'xule', age: 10},
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
}),success(function (info){
// 可以把服務端返回的數據綁定到模型上
$scope.result = info;
}),error(function (info)){
};
}]);
要注意的是,請求方式不同,傳遞的數據格式也不同。
(1)當請求方式為post時,有兩種寫法。
數據為key=val&key1=val1格式時,需要通過Content-Type告知其類型為application/x-www/form-urlencoded。
數據為{key: val, key1: val1}格式時,需要通過Content-Type告知其類型為application/json。但是AngularJS默認使用這種格式,這里的headers可以寫也可以不寫。
$http({
url: 'example.php',
method: 'post',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data: {'name=ming&age=11'}
});
$http({
url: 'example.php',
method: 'post',
// 這里的headers可以寫也可以不寫,默認就是這種形式
headers: {
'Content-Type': 'application/json'
},
data: {name: 'ming', age: 11}
});
(2)當請求為get方式,使用params專門傳遞get方式的數據
$http({
url: 'example.php',
method: 'get',
params: {age: 11},
});
如何處理跨域請求呢
通過get方式傳遞一個事先定義好的函數名,這里有固定的寫法:params: {callback: 'JSON_CALLBACK'}
,JSON_CALLBACK
即為"事先定義好的函數名",JSON_CALLBACK
是一個占位符,AngularJS 也會像jQuery一樣動態創建一個函數,但是動態創建的函數名不固定,先使用一個占位符JSON_CALLBACK
占一個位,當動態函數創建成功后再將這個JSON_CALLBACK
進行替換。后臺接收函數名的時候也用JSON_CALLBACK
$http({
url: ' ',
params: {callback: 'JSON_CALLBACK'},
method: 'jsonp'
}).success(function (info) {
});
使用AngularJS的$http服務的跨域獲取天氣信息
這里用的是百度車聯網API中的天氣查詢 - 車聯網API
注意下面兩種傳遞callback的方法,都是可以的。
<!DOCTYPE html>
<html lang="en" ng-app="App">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<!-- 0A5bc3c4fb543c8f9bc54b77bc155724 -->
<div ng-controller="WeatherController">
<table>
<!-- 視圖 -->
<tr ng-repeat="item in weatherData">
<td>{{item.date}}</td>
<td>{{item.temperature}}</td>
<td>{{item.weather}}</td>
<td>{{item.wind}}</td>
<td></td>
<td></td>
</tr>
</table>
</div>
<script src="../libs/angular.min.js"></script>
<script>
var App = angular.module('App', []);
// 定義控制器并聲明依賴
App.controller('WeatherController', ['$scope', '$http', '$log', function($scope, $http, $log) {
// 請求數據
$http({
method: 'jsonp',
url: 'http://api.map.baidu.com/telematics/v3/weather?callback=JSON_CALLBACK',
params: { // 請求的參數
location: '北京',
output: 'json',
ak: '0A5bc3c4fb543c8f9bc54b77bc155724'
}
})
.success(function (data) {
$log.log(data); // 成功的回調
// 請求回的數據放到模型上
$scope.weatherData = data.results[0].weather_data;
});
}])
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body ng-app="App">
<!-- 0A5bc3c4fb543c8f9bc54b77bc155724 -->
<div ng-controller="WeatherCtrl">
<table>
<tr ng-repeat="weather in weathers">
<td>{{weather.date}}</td>
<td>{{weather.temperature}}</td>
<td>{{weather.weather}}</td>
<td>{{weather.wind}}</td>
<td></td>
<td></td>
</tr>
</table>
</div>
<script src="../libs/angular.min.js"></script>
<script>
var App = angular.module( 'App', [] ) ;
App.controller('WeatherCtrl', ['$scope', '$http', function ($scope, $http) {
$http({
url: 'http://api.map.baidu.com/telematics/v3/weather',
method: 'jsonp',
params: {
callback: 'JSON_CALLBACK',
ak: '0A5bc3c4fb543c8f9bc54b77bc155724',
location: '北京',
output: 'json'
}
}).success(function (info) {
console.log(info);
$scope.weathers = info.results[0].weather_data;
});
}]);
</script>
</body>
</html>
8.2 自定義服務
通過上面例子得知,所謂服務是將一些通用性的功能邏輯進行封裝方便使用,AngularJS允許將自定義服務。
8.2.1 factory方法
<script>
var App = angular.module('App', []);
// 模塊實例App包含若干方法
// 使用factory() 來定義服務
// 第一個參數是服務的名稱
// 第二個參數是一個數組
// 自定義服務是可以在原有的AngularJS內置服務基礎之上來定義
// 自定義的服務其使用和內置服務一樣
App.factory('sayHello', ['$log', function ($log) {
// 必須要有一個返回值
// 其值可以是任意數據類型
// 但是通常對象或函數更有意義
function sayHello(name) {
alert('你好' + name);
}
return {
sayHello: sayHello,
changge: function () {
console.log('我還會唱歌呢');
}
}
}]);
// 需求定義一個可以獲取當前時間的服務并格式化好
App.factory('Time', ['$filter', function ($filter) {
return {
// 傳入格式
now: function (format) {
// 如果沒傳入格式,有個默認格式
if(!format) {
format = 'yyyy-MM-dd';
}
var now = new Date;
// 參考$filter用法
return $filter('date')(now, format);
}
}
}]);
App.controller('DemoCtrl', ['sayHello', 'Time', '$scope', function (sayHello, Time, $scope) {
sayHello('小明');
sayHello.sayHello('小明');
sayHello.changge();
$scope.now = Time.now('yyyy-MM-dd H:m:s');
}]);
</script>
8.2.2 service方法
特點:不需要return
<body>
<div ng-controller="DemoCtrl">
<span>{{now}}</span>
</div>
<script src="./libs/angular.min.js"></script>
<script>
var App = angular.module('App', []);
// 還可以使用service() 來定義服務
// 第一個參數服務的名稱
// 第二個參數還是數組
// 數組是用來解決依賴
App.service('Time', ['$filter', function ($filter) {
// 在此完成當前服務的所有業務邏輯
function Time(format) {
return $filter('date')(new Date, format);
}
// 不需要使用return返回
// this指的就是當前服務
this.now = Time;
}]);
App.controller('DemoCtrl', ['$scope', 'Time', function ($scope, Time) {
$scope.now = Time.now('yyyy-MM-dd H:m:s');
}])
</script>
</body>
8.2.3 value方法定義常量
<body>
<div ng-controller="DemoCtrl">
{{author}}
{{ver}}
</div>
<script src="./libs/angular.min.js"></script>
<script>
var App = angular.module('App', []);
// value定義的本質上一個服務
// 從表現形式上是一個常量
// 第一個參數為服務名稱
// 第二個參數任意[字符串 數值]
App.value('ver', '1.0');
App.value('author', 'xiaoming');
App.controller('DemoCtrl', ['$scope', 'ver', 'author', function ($scope, ver, author) {
console.log(ver);
$scope.ver = ver;
$scope.author = author;
}])
</script>
</body>
9.模塊加載
AngularJS模塊可以在被加載和執行之前對其自身進行配置。我們可以在應用的加載階段配置不同的邏輯。
先來看一下Angular的執行過程。
只需要明白一點,我們可以通過配置塊和運行塊可以影響后面的執行。
9.1 配置塊
通過config方法實現對模塊的配置,AngularJS中的服務大部分都對應一個“provider”,用來執行與對應服務相同的功能或對其進行配置。
比如$log、$http、$location都是內置服務,相對應的“provider”分別是$logProvider、$httpProvider、$locationPorvider。
<body ng-app="App">
<div ng-controller="DemoCtrl">
<h1>{{info|capilize}}</h1>
</div>
<script src="./libs/angular.min.js"></script>
<script>
var App = angular.module('App', []);
// App.config()方法是用來配置內置模塊的
// 參數是一個數組(可以一次性配置多個)
// 其具體配置方式為
// 要想配置某個模塊都需要通過“服務名Provider”
// 例如 想配置$log 就使用 $logProvider進行配置
// 例如 想配置$filter 就使用$filterProvider進行配置
// 例如 想配置$http 就使用$httpProvider進行配置
// 配置$log的功能
App.config(['$logProvider', '$filterProvider', function ($logProvider, $filterProvider) {
// 禁用debug方法
$logProvider.debugEnabled(false);
// $filter 默認只有9個(過濾器)方法
// 這里可以通過配置添加一個首字母大寫功能
// 第一個參數寫過濾器的名稱
$filterProvider.register('capilize', function () {
return function (input) {
return input[0].toUpperCase() + input.slice(1);
}
});
}]);
App.controller('DemoCtrl', ['$log', '$scope', function ($log, $scope) {
$log.log('調試信息');
$log.debug('debug還能用嗎');
$scope.info = 'my name is xiaoming';
}]);
// 總結:在AngularJS執行過程中,可以對某些功能進行配置,使用提供的config方法來現
// 在進行配置時并不是使用原有的服務名稱,而是“服務名稱+Provider”
// 例如 $log 使用 $logProvider
App.config(['$logProvider', function ($logProvider) {
// 通過$logProvider就可以配置$log了
$logProvider.debugEnabled();
}]);
</script>
</body>
9.2 運行塊
服務是模塊形式存在的,對外提供特定功能,一般都是將服務做為依賴注入進去的,然后再進行調用,除了這種方式外我們也可以直接運行相應的服務模塊,AngularJS提供了run方法來實現。
不但如此,run方法還是最先執行的,利用這個特點我們可以將一些需要優先執行的功能通過run方法來運行,比如驗證用戶是否登錄,未登錄則不允許進行任何其它操作。
App.run(['$http', '$rootScope', function ($http, $rootScope) {
$http({
method: 'post',
url: 'example.php'
}).success(function (info) {
// 在根作用域上定義date,相當于在ng-init = '' 中定義
// 如果要在根作用域上定義多個屬性,使用ng-init = '' 會比較麻煩
// 此時就可以用$rootScope.xxx
$rootScope.date = info;
});
}]);
10. 路由
網站在切換各個頁面時,網站主域名后的改變的,就是路由。改變一個路由,就會切換一個頁面。一個應用是由若個視圖組合而成的,根據不同的業務邏輯展示給用戶不同的視圖,路由則是實現這一功能的關鍵。通過監聽路由的改變,可以實現單頁面應用(單頁面應用也是有路由的)。
10.1 單頁面應用(SPA)
SPA(Single Page Application)
單頁面應用就是指將所有的功能通過一個頁面展示。
在PC端也有廣泛的應用,通常情況下使用Ajax異步請求數據,然后實現內容局部刷新,局部刷新的本質是動態生成DOM,新生成的DOM元素并沒有真實存在于文檔中,所以當再次刷新頁面時新添加的DOM元素會“丟失”,通過單頁面應可以很好的解決這個問題。
由于頁面沒變,內容變了??梢允褂肵MLHttpRequest向服務器獲取數據,并將獲取到的數據以DOM操作的方式添加到頁面中去。采用這種方式可以增強用戶體驗,還可以減輕服務器壓力。
10.2 路由
在后端開發中通過URL地址可以實現頁面(視圖)的切換,但是AngularJS是一個純前端MVC框架,在開發單頁面應用時,所有功能都在同一頁面完成,所以無需切換URL地址(即不允許產生跳轉),但Web應用中又經常通過鏈接(a標簽)來更新頁面(視圖),當點擊鏈接時還要阻止其向服務器發起請求,通過錨點(頁內跳轉)可以實現這一點。
實現單頁面應用需要具備:
a、只有一頁面
b、鏈接使用錨點
<body>
<nav>
<a href="#login">登錄</a>
<a href="#register">注冊</a>
</nav>
<div class="content">
</div>
<!-- 點擊登錄出現登錄界面 -->
<!-- 點擊注冊出現注冊界面 -->
<script>
// 根據錨點的變化來決定到底應該顯示哪個界面
// 如何知道錨點變化了?
window.onhashchange = function () {
// 當錨點發生變化后此事件的回調被執行
var hash = location.hash.slice(1);
var xhr = new XMLHttpRequest;
// 將獲取到的錨點的變化值以參數的形式傳給服務端
xhr.open('get', 'spa.php?hash=' + hash);
xhr.send();
xhr.onreadystatechange = function () {
if(xhr.readyState == 4 && xhr.status == 200) {
var content = document.querySelector('.content');
content.innerHTML = xhr.responseText;
}
}
}
</script>
</body>
通過上面的例子發現在單一頁面中可以通過hashchange事件監聽到錨點的變化,進而可以實現為不同的錨點準不同的視圖,單頁面應用就是基于這一原理實現的。
AngularJS對這一實現原理進行了封裝,將錨點的變化封裝成路由(Route),這是與后端路由的根本區別。
在1.2版前路由功能是包含在AngularJS核心代碼當中,之后的版本將路由功能獨立成一個模塊,下載angular-route.js
8.2.1 如何使用路由
(1)引入angular-route.js
(2)實例化模塊(App)時,當成依賴傳進去(模塊名稱叫ngRoute)
(3)配置路由模塊
(4)布局模板。通過ng-view指令布局模板,路由匹配的視圖會被加載渲染到些區域。
<body ng-app="App">
<nav>
<a href="#/login">登錄</a>
<a href="#/register">注冊</a>
<a href="#/list">講師列表</a>
</nav>
<div ng-view></div>
<!-- AngularJS核心文件 -->
<script src="./libs/angular.min.js"></script>
<!-- AngularJS路由插件 -->
<script src="./libs/angular-route.js"></script>
<script>
// 作為依賴傳入
var App = angular.module('App', ['ngRoute']);
// 路由一定要經過config方法配置才能用
App.config(['$routeProvider', function ($routeProvider) {
// 具體使用when方法進行配置
$routeProvider.when('/login', {
// 將界面以字符串形式書寫,不易管理
// template: '<h1>我是登錄界面</h1>'
// 將界面內容放到獨立文件中,方便管理
templateUrl: './views/login.html'
})
.when('/register', {
// 將界面以字符串形式書寫,不易管理
// template: '<h1>我是注冊界面</h1>'
// 將界面內容放到獨立文件中,方便管理
templateUrl: './views/register.html'
})
.when('/list', {
templateUrl: './views/list.tpl',
controller: 'DemoCtrl'
})
.otherwise({
redirectTo: '/login'
});
}]);
App.controller('DemoCtrl', ['$scope', '$routeParams', function ($scope, $routeParams) {
console.log($routeParams);
$scope.list = ['html', 'js', 'css', 'php'];
}]);
</script>
8.2.2 路由參數
(1)提供兩個方法匹配路由,分別是when
和otherwise
,when
方法需要兩個參數,otherwise
方法做為when
方法的補充只需要一個參數,其中when
方法可以被多次調用。
(2)第1個參數是一個字符串,代表當前URL中的hash值。
(3)第2個參數是一個對象,配置當前路由的參數,如視圖、控制器等。
1. template 字符串形式的視圖模板
2. templateUrl 引入外部視圖模板
3. controller 視圖模板所屬的控制器
4. redirectTo 跳轉到其它路由
(4)獲取參數,在控制中注入$routeParams可以獲取傳遞的參數
<script src="./libs/angular.min.js"></script>
<script src="./libs/angular-route.js"></script>
<script>
var App = angular.module('App', ['ngRoute']);
App.config(['$routeProvider', function ($routeProvider) {
// ?后面的內容是參數,并不是路由的一部分
$routeProvider.when('/', {
template: '<h1>你的參數為:{{params.name}}和{{params.age}}</h1>',
controller: 'IndexCtrl'
})
// 使用:來為路由設置參數
// :name , :后跟的是一個形參,在地址欄中輸入實參。
// 而沒加: 的就是固定寫法
// 比如地址欄可以是/list/haoxiang/18/demo/100形式
.when('/list/:name/:age/demo/:page', {
template: '<h1>列表</h1>',
controller: 'ListCtrl'
});
}]);
App.controller('IndexCtrl', ['$scope', '$http', '$routeParams', function ($scope, $http, $routeParams) {
// $routeParams來獲取地址參數,就是?后的那部分
console.log($routeParams);
$scope.params = $routeParams;
}]);
// 列表控制器
App.controller('ListCtrl', ['$scope', '$http', '$routeParams', function ($scope, $http, $routeParams) {
console.log($routeParams);
}]);
</script>
9.其他
9.1 在AngularJS中使用jQuery
在沒有引入jQuery的前提下AngularJS實現了簡版的jQuery Lite,通過angular.element不能選擇元素,但可以將一個DOM元素轉成jQuery對象,如果引提前引入了jQuery則angular.element則完全等于jQuery。
// 原生DOM對象
var box = document.querySelector('.box');
var btn = document.querySelector('button');
// 轉成jQuery對象
box = angular.element(box);
btn = angular.element(btn);