angular framework 知識點 開發實踐
angularJS 主要 須要掌握下面幾個基本點 進行平常的開發是沒有問題的
第一個:理解 angularjs 中的數據綁定
第二個:理解angularjs 的模塊和模塊的加載機制
第三個:知道 angularjs中的做用域 $scope
第四個:理解 angularjs 中的控制器
第五個:熟悉 angularjs中的過濾器,表達式
第六個:理解angularjs中的指令包括 內置指令 和自定義的指令中的10來個參數的含義
第七個:理解angularjs中的視圖和路由
第八個:理解angularjs中的依賴注入
第九個:理解angularjs中的服務 包括熟悉一些內置的服務和 自定義的服務
第十個:理解angularjs中的事件
若是想用angularjs進行開發至少都須要掌握上面的10點 仍是有點多的 ,angularjs 後面還有一些本地化 安全性,測試,緩存一些技術 ,若是是初學者我其實並不建議一上來就學習 angularjs 能夠先看一些簡單的MVC框架 例如 backbone.js
其實 angularjs我的理解是把java的那套東東搬到前端來了,之後這種angularjs的項目應該是作一些內部系統比較多,由於和java同樣方便搭積木。
下面咱們就來分別說一說angularjs中的這10點
第一點: 理解 angularjs 中的數據綁定
數據綁定這個東西 最先我接觸到的是從asp.net開始的那個時候的asp.net 網頁每一個VIEW 其實都至關於一個模板,當時這樣作就是但願 控制器邏輯和頁面分離開來,這樣不會顯得整個工程雜亂無章 ,到後來 jquery的興起,也有一些很優秀的前端模板開始出現 例如jquery-templ 這些基於jquery的模板插件,若是你作過一些 後端的前端開發的化應該很快能夠理解 angularjs 中的數據綁定
HTML (VIEW) JS(controller)
- <div ng-controller="MyController">
- <span style="white-space:pre"> </span><h1>來自controller scope My:{{name}}</h1>
- </div>
- <h1>來自 rootscope {{name}}</h1>
- testapp.controller('MyController',function($scope, $parse){
- $scope.name = "ARI";
- });
運行後的效果
其實這裏就是一個簡單的 數據綁定 當我隨意修改 controller 中name 的值的時候 相應的視圖中的 顯示也會變化
第二個:理解angularjs 的模塊和模塊的加載機制
熟悉java的同窗 都知道 java 中有不少的 包 不一樣的包 有着不一樣的功能,由於javascript中的一些很差的東西 ,也不叫很差,就是在通常的場景下有些不方便的東西例如全局命名空間,angularjs 中的模塊 能夠保持命名空間的清潔,能夠從多個地方複用咱們的模塊代碼
- <span style="color:#330033;">angular.module('myApp', []);
- 這個方法接受兩個參數,第一個是名字name,這裏 name = myAPP 第二個參數是一個數組,這個裏面存放的是其餘模塊的名字myApp這個模塊 依賴這些模塊</span>
那麼問題來了 這些模塊應該怎麼加載呢,他們之間順序變化有問題嗎? 這裏就涉及到angularjs 中的模塊加載機制
咱們先看一個小例子理解一下
- <span style="color:#330033;">function SomeClass(greeter) {
- this.greeter = greeter;
- }
- SomeClass.prototype.greetName = function(name) {
- this.greeter.greet(name);
- };</span>
SomeClass可以在運行時訪問到內部的greeter,但它並不關心如何得到對greeter的引用。
爲了得到對greeter實例的引用,SomeClass的建立者會負責構造其依賴關係並傳遞進去。
angularjs 經過一個 $injector 的一個組件 來去實現各類狀況下模塊的依賴和注入
下面咱們來簡單看一下 angularjs 的 $injector api
第一個方法 annotate()
annotate()方法的返回值是一個由服務名稱組成的數組,這些服務會在實例化時被注入到目
標函數中。annotate()方法能夠幫助$injector判斷哪些服務會在函數被調用時注入進去。
annotate()方法能夠接受一個參
參數fn能夠是一個函數,也能夠是一個數組。annotate()方法返回一個數組,數組元素的
值是在調用時被注入到目標函數中的服務的名稱。
- <span style="color:#330033;">var injector = angular.injector(['ng', 'myApp']);
- injector.annotate(function($q, greeter) {});
第二個 get() 方法
get()方法返回一個服務的實例,能夠接受一個參數
參數name是想要獲取的實例的名稱
get()根據名稱返回服務的一個實例
第三個方法 has()
has()方法返回一個布爾值,在$injector可以從本身的註冊列表中找到對應的服務時返回
true,不然返回false。它能接受一個參數:
參數name是咱們想在注入器的註冊列表中查詢的服務名稱
第四個方法 instantiate()
instantiate()方法能夠建立某個JavaScript類型的實例。它會經過new操做符調用構造函數,
並將全部參數都傳遞給構造函數。它能夠接受兩個參數:
type(函數) locals(對象,可選)
instantiate()方法返回Type的一個新實例
第五個方法 invoke()
invoke()方法會調用方法並從$injector中添加方法參數。
invoke()方法接受三個參數。
fn(function)
這個函數就是要調用的函數。這個函數的參數由函數聲明設置。
self (object-可選)
self參數容許咱們設置調用方法的this參數。
locals (object-可選)
這個可選參數提供另外一種方式在函數被調用時傳遞參數名給該函數。
invoke()方法返回fn函數返回的值
上面介紹的 都是文檔裏面描述的 你們只要瞭解一下就好了
第三點 angularjs 中的做用域
首先應用的做用域是和應用的數據模型相關聯的,同時做用域也是表達式執行的上下文。$scope
對象是定義應用業務邏輯、控制器方法和視圖屬性的地方
做用域是視圖和控制器之間的膠水。在應用將視圖渲染並呈獻給用戶以前,視圖中的模板會
和做用域進行鏈接,而後應用會對DOM進行設置以便將屬性變化通知給AngularJS
做用域是應用狀態的基礎。基於動態綁定,咱們能夠依賴視圖在修改數據時馬上更新$scope,
也能夠依賴$scope在其發生變化時馬上從新渲染視圖
AngularJS將$scope設計成和DOM相似的結構,所以$scope能夠進行嵌套,也就是說咱們可
以引用父級$scope中的屬性
做用域有如下的基本功能:
提供觀察者以監視數據模型的變化;
能夠將數據模型的變化通知給整個應用,甚至是系統外的組件;
能夠進行嵌套,隔離業務功能和數據;
給表達式提供運算時所需的執行環境。
$scope 的生命週期:
建立
在建立控制器或指令時,AngularJS會用$injector建立一個新的做用域,並在這個新建的控
制器或指令運行時將做用域傳遞進去。
連接
當Angular開始運行時,全部的$scope對象都會附加或者連接到視圖中。全部建立$scope對
象的函數也會將自身附加到視圖中。這些做用域將會註冊當Angular應用上下文中發生變化時需
要運行的函數。
更新
當事件循環運行時,它一般執行在頂層$scope對象上(被稱做$rootScope),每一個子做用域
都執行本身的髒值檢測。每一個監控函數都會檢查變化。若是檢測到任意變化,$scope對象就會觸
髮指定的回調函數。
銷燬
當一個$scope在視圖中再也不須要時,這個做用域將會清理和銷燬本身。
儘管永遠不會須要清理做用域(由於Angular會爲你處理),可是知道是誰建立了這個做用域
仍是有用的,由於你可使用這個$scope上叫作$destory()的方法來清理這個做用域。
第四個:理解 angularjs 中的控制器
控制器在AngularJS中的做用是加強視圖
- <span style="color:#330033;">var app = angular.module('app', []);
- app.controller('FirstController', function($scope) {
- $scope.message = "hello";
- });</span>
設計良好的應用會將複雜的邏輯放到指令和服務中。經過使用指令和服務,咱們能夠將控制
器重構成一個輕量且更易維護的形式:
第五個:熟悉 angularjs中的過濾器,表達式
表達式在AngularJS應用中被普遍使用,所以深刻理解AngularJS如何使用並運算表達式是非
常重要的。
前面已經見過使用表達式的示例。用{{ }}符號將一個變量綁定到$scope上的寫法本質上就
是一個表達式:{{ expression }}。當用$watch進行監聽時,AngularJS會對錶達式或函數進行
運算。
表達式和eval(javascript)很是類似,可是因爲表達式由AngularJS來處理,它們有如下顯
著不一樣的特性:
全部的表達式都在其所屬的做用域內部執行,並有訪問本地$scope的權限;
若是表達式發生了TypeError和ReferenceError並不會拋出異常;
不容許使用任何流程控制功能(條件控制,例如if/eles);
能夠接受過濾器和過濾器鏈。
對錶達式進行的任何操做,都會在其所屬的做用域內部執行,所以能夠在表達式內部調用那
些限制在此做用域內的變量,並進行循環、函數調用、將變量應用到數學表達式中等操做。
過濾器用來格式化須要展現給用戶的數據。AngularJS有不少實用的內置過濾器,同時也提
供了方便的途徑能夠本身建立過濾器。
過濾器本質上是一個會把咱們輸入的內容看成參數傳入進去的函數。上面這個例子中,咱們
在調用過濾器時簡單地把input看成字符串來處理。
- <span style="color:#330033;">angular.module('myApp.filters', [])
- .filter('capitalize', function() {
- return function(input) {
- if (input) {
- return input[0].toUpperCase() + input.slice(1);
- }
- });</span>
第六個:理解angularjs中的指令
我的認爲 要搞清楚指令 把這個 弄透就完事了
第六個:理解angularjs中的指令
- <span style="color:#330033;">angular.module('myApp', [])
- .directive('myDirective', function() {
- return {
- restrict: String,
- priority: Number,
- terminal: Boolean,
- template: String or Template Function:
- function(tElement, tAttrs) (...},
- templateUrl: String,
- replace: Boolean or String,
- scope: Boolean or Object,
- transclude: Boolean,
- controller: String or
- function(scope, element, attrs, transclude, otherInjectables) { ... },
- controllerAs: String,
- require: String,
- link: function(scope, iElement, iAttrs) { ... },
- compile:
- function(tElement, tAttrs, transclude) {
- return {
- pre: function(scope, iElement, iAttrs, controller) { ... },
- post: function(scope, iElement, iAttrs, controller) { ... }
- }
- return function postLink(...) { ... }
- }
- };
- });</span>
固然 還忘記了一點 angularjs 中還有許多內置的指令 這些也必須瞭解一下
點
這裏 能夠查看詳細的 內置 指令文檔 裏面講的很清楚
很久沒更新了 來 咱們繼續!
下面咱們來一一介紹一下
第一個 :restrict
他限制directive爲指定的聲明方式 能夠經過四種方式進行聲明
E: element A:attribute C:class M: anotationjavascript
默認是經過 屬性的方式
priority : 優先級html
當有多個directive定義在同一個DOM元素時,有時須要明確它們的執行順序。這屬性用於在directive的compile function調用以前進行排序。若是優先級相同,則執行順序是不肯定的(經初步試驗,優先級高的先執行,同級時按照相似棧的「後綁定先執行」。前端
terminal :java
若是設置爲」true」,則表示當前的priority將會成爲最後一組執行的directive。任何directive與當前的優先級相同的話,他們依然會執行,但順序是不肯定的(雖然順序不肯定,但基本上與priority的順序一致。當前優先級執行完畢後,更低優先級的將不會再執行)。jquery
template:git
若是replace 爲true,則將模版內容替換當前的HTML元素,並將原來元素的屬性、class一併遷移;若是爲false,則將模版元素看成當前元素的子元素處理。angularjs
一個能夠接受兩個參數的函數,參數爲tElement和tAttrs,並返回一個表明模板的字符串。tElement和tAttrs中的t表明template,是相對於instance的。github
- <span style="color:#330033;"> angular.module('app',[])
- .directive('myDirective', function () {
- return {
- restrict: 'E',
- template: '<a href="http://www.baidu.com">百度</a>'
- };
- })</span>
- <span style="color:#330033;"> HtmlCode:
- <my-directive></my-directive></span>
- <span style="color:#330033;">angular.module('app',[])
- .directive('myDirective', function () {
- return {
- restrict: 'EAC',
- template: function (elem, attr) {
- return "<a href='" + attr.value + "'>" + attr.text + "</a>";
- }
- };
- })
- </span>
templateUrl:express
與template基本一致,但模版經過指定的url進行加載。由於模版加載是異步的,因此compilation、linking都會暫停,等待加載完畢後再執行。後端
replace:
若是設置爲true,那麼模版將會替換當前元素,而不是做爲子元素添加到當前元素中。
scope:
scope參數是可選的,能夠被設置爲true或一個對象。默認值是false。
scope:false 此時,directive沒有獨立的scope對象,link函數中引用的scope對象來自於當前節點的默認controller
scope:true 此時,directive擁有獨立的scope對象,此scope是由父scope對象繼承而來,能夠訪問父scope中的全部屬性,此時其實是經過繼承連接(prototype鏈)訪問的父scope中的屬性,要注意的是,當給此scope繼承而來的屬性名稱賦值的時候,子scope中會相應創建一個本地屬性,此時改變的是本scope的變量屬性,父scope中的屬性是不會改變的。
下面 咱們來講說 directive 與scope 的 隔離交互
AngularJS 的 directive 默認能共享父 scope 中定義的屬性,例如在模版中直接使用父 scope 中的對象和屬性。一般使用這種直接共享的方式能夠實現一些簡單的 directive 功能。當你須要建立一個可重複使用的 directive,只是偶爾須要訪問或者修改父 scope 的數據,就須要使用隔離 scope。當使用隔離 scope 的時候,directive 會建立一個沒有依賴父 scope 的 scope,並提供一些訪問父 scope 的方式。
當你想要寫一個可重複使用的 directive,不能再依賴父 scope,這時候就須要使用隔離 scope 代替。共享 scope 能夠直接共享父 scope,而隔離 scope 沒法共享父scope。下圖解釋共享 scope 和隔離 scope 的區別:

使用共享 scope 的時候,能夠直接從父 scope 中共享屬性。所以下面示例能夠將那麼屬性的值輸出出來。使用的是父 scope 中定義的值。
- <span style="color:#330033;">app.controller("myController", function ($scope) {
- $scope.name = "hello world";
- }).directive("shareDirective", function () {
- return {
- template: 'Say:{{name}}'
- }
- });</span>
- <span style="color:#330033;"><div ng-controller="myController">
- <div share-directive=""></div>
- </div></span>
結果輸出 hello world
使用隔離 scope 的時候,沒法從父 scope 中共享屬性。所以下面示例沒法輸出父 scope 中定義的 name 屬性值。
- <span style="color:#330033;">app.controller("myController", function ($scope) {
- $scope.name = "hello world";
- }).directive("isolatedDirective", function () {
- return {
- scope: {},
- template: 'Say:{{name}}'
- }
- });</span>
- <span style="color:#330033;"><div ng-controller="myController">
- <div isolated-directive=""></div>
- </div></span>
輸出結果 say:
從上面看出共享 scope 容許從父 scope 滲入到 directive 中,而隔離 scope 不能,在隔離 scope 下,給 directive 創造了一堵牆,使得父 scope 沒法滲入到 directive 中。
在 Directive 中建立隔離 scope 很簡單,只須要定義一個 scope 屬性便可,這樣,這個 directive 的 scope 將會建立一個新的 scope,若是多個 directive 定義在同一個元素上,只會建立一個新的 scope。
- <span style="color:#330033;">angular.module('app').controller("myController", function ($scope) {
- $scope.user = {
- id:1,
- name:"hello world"
- };
- }).directive('isolatedScope', function () {
- return {
- scope: {},
- template: 'Name: {{user.name}} Street: {{user.addr}}'
- };
- });</span>
如今 scope 是隔離的,user 對象將沒法從父 scope 中訪問,所以,在 directive 渲染的時候 user 對象的佔位將不會輸出內容。
directive 在使用隔離 scope 的時候,提供了三種方法同隔離以外的地方交互。這三種分別是
@ 綁定一個局部 scope 屬性到當前 dom 節點的屬性值。結果老是一個字符串,由於 dom 屬性是字符串。
& 提供一種方式執行一個表達式在父 scope 的上下文中。若是沒有指定 attr 名稱,則屬性名稱爲相同的本地名稱。
= 經過 directive 的 attr 屬性的值在局部 scope 的屬性和父 scope 屬性名之間創建雙向綁定。
以下示例:directive 聲明未隔離 scope 類型,而且使用@綁定 name 屬性,在 directive 中使用 name 屬性綁定父 scope 中的屬性。當改變父 scope 中屬性的值的時候,directive 會同步更新值,當改變 directive 的 scope 的屬性值時,父 scope 沒法同步更新值。
- <span style="color:#330033;"> app.controller("myController", function ($scope) {
- $scope.name = "hello world";
- }).directive("isolatedDirective", function () {
- return {
- scope: {
- name: "@"
- },
- template: 'Say:{{name}} <br>改變隔離scope的name:<input type="buttom" value="" ng-model="name" class="ng-pristine ng-valid">'
- }
- })</span>
- <span style="color:#330033;"><div ng-controller="myController">
- <div class="result">
- <div>父scope:
- <div>Say:{{name}}<br>改變父scope的name:<input type="text" value="" ng-model="name"/></div>
- </div>
- <div>隔離scope:
- <div isolated-directive name="{{name}}"></div>
- </div>
- <div>隔離scope(不使用{{name}}):
- <div isolated-directive name="name"></div>
- </div>
- </div></span>
= 局部 scope 屬性
= 經過 directive 的 attr 屬性的值在局部 scope 的屬性和父 scope 屬性名之間創建雙向綁定。
意思是,當你想要一個雙向綁定的屬性的時候,你可使用=來引入外部屬性。不管是改變父 scope 仍是隔離 scope 裏的屬性,父 scope 和隔離 scope 都會同時更新屬性值,由於它們是雙向綁定的關係。
- <span style="color:#330033;"> app.controller("myController", function ($scope) {
- $scope.user = {
- name: 'hello',
- id: 1
- };
- }).directive("isolatedDirective", function () {
- return {
- scope: {
- user: "="
- },
- template: 'Say:{{user.name}} <br>改變隔離scope的name:<input type="buttom" value="" ng-model="user.name"/>'
- }
- })</span>
- <span style="color:#330033;"><div ng-controller="myController">
- <div>父scope:
- <div>Say:{{user.name}}<br>改變父scope的name:<input type="text" value="" ng-model="user.name"/></div>
- </div>
- <div>隔離scope:
- <div isolated-directive user="user"></div>
- </div>
- <div>隔離scope(使用{{name}}):
- <div isolated-directive user="{{user}}"></div>
- </div>
- </div></span>
& 局部 scope 屬性
& 方式提供一種途經是 directive 能在父 scope 的上下文中執行一個表達式。此表達式能夠是一個 function。
好比當你寫了一個 directive,當用戶點擊按鈕時,directive 想要通知 controller,controller 沒法知道 directive 中發生了什麼,也許你能夠經過使用 angular 中的 event 廣播來作到,可是必需要在 controller 中增長一個事件監聽方法。
最好的方法就是讓 directive 能夠經過一個父 scope 中的 function,當 directive 中有什麼動做須要更新到父 scope 中的時候,能夠在父 scope 上下文中執行一段代碼或者一個函數。
- <span style="color:#330033;"> app.controller("myController", function ($scope) {
- $scope.value = "hello world";
- $scope.click = function () {
- $scope.value = Math.random();
- };
- }).directive("isolatedDirective", function () {
- return {
- scope: {
- action: "&"
- },
- template: '<input type="button" value="在directive中執行父scope定義的方法" ng-click="action()"/>'
- }
- })</span>
- <span style="color:#330033;"> <div ng-controller="myController">
- <div>父scope:
- <div>Say:{{value}}</div>
- </div>
- <div>隔離scope:
- <div isolated-directive action="click()"></div>
- </div>
- </div></span>
Transclusion(嵌入)
Transclusion是讓咱們的指令包含任意內容的方法。咱們能夠延時提取並在正確的scope下編譯這些嵌入的內容,最終將它們放入指令模板中指定的位置。 若是你在指令定義中設置 transclude:true,一個新的嵌入的scope會被建立,它原型繼承子父scope。 若是你想要你的指令使用隔離的scope,可是它所包含的內容可以在父scope中執行,transclusion也能夠幫忙。
有時候我咱們要嵌入指令元素自己,而不只僅是它的內容。在這種狀況下,咱們須要使用 transclude:’element’。它和 transclude:true 不一樣,它將標記了 ng-transclude 指令的元素一塊兒包含到了指令模板中。使用transclusion,你的link函數會得到一個名叫 transclude 的連接函數,這個函數綁定了正確的指令scope,而且傳入了另外一個擁有被嵌入DOM元素拷貝的函數。你能夠在這個 transclude 函數中執行好比修改元素拷貝或者將它添加到DOM上等操做。
transclude:true
- <span style="color:#330033;"><body ng-app="myApp">
- <div class="AAA">
- <hero name="superman">Stuff inside the custom directive</hero>
- </div>
- </body></span>
- <span style="color:#330033;">angular.module('myApp', []).directive('hero', function () {
- return {
- restrict: 'E',
- transclude: true,
- scope: { name:'@' },
- template: '<div>' +
- '<div>{{name}}</div><br>' +
- '<div ng-transclude></div>' +
- '</div>'
- };
- });</span>
通過指令編譯之後

指令 中的 controller controllerAs require
controller參數能夠是一個字符串或一個函數。當設置爲字符串時,會以字符串的值爲名字,來查找註冊在應用中的控制器的構造函數:
- <span style="color:#330033;">angular.module('myApp', [])
- .directive('myDirective', function() {
- restrict: 'A',
- controller: 'SomeController'
- })
- angular.module('myApp')
- .controller('SomeController', function($scope, $element, $attrs, $transclude) {
- });</span>
能夠在指令內部經過匿名構造函數的方式來定義一個內聯的控制器:
- <span style="color:#330033;">angular.module('myApp',[])
- .directive('myDirective', function() {
- restrict: 'A',
- controller:
- function($scope, $element, $attrs, $transclude) {
- }
- });</span>
咱們能夠將任意能夠被注入的AngularJS服務傳遞給控制器。例如,若是咱們想要將$log服
務傳入控制器,只需簡單地將它注入到控制器中,即可以在指令中使用它了。
控制器中也有一些特殊的服務能夠被注入到指令當中。這些服務有:
1. $scope
與指令元素相關聯的當前做用域。
2. $element
當前指令對應的元素。
3. $attrs
由當前元素的屬性組成的對象。例如,下面的元素:
<div id="aDiv"class="box"></div>
具備以下的屬性對象:
{
id: "aDiv",
class: "box"
}
4. $transclude
嵌入連接函數會與對應的嵌入做用域進行預綁定。
transclude連接函數是實際被執行用來克隆元素和操做DOM的函數。
例如,咱們想要經過指令來添加一個超連接標籤。能夠在控制器內的$transclude函數中實現:
- <span style="color:#330033;">angular.module('myApp')
- .directive('link', function() {
- return {
- restrict: 'EA',
- transclude: true,
- controller:
- function($scope, $element, $transclude, $log) {
- $transclude(function(clone) {
- var a = angular.element('<a>');
- a.attr('href', clone.text());
- a.text(clone.text());
- $log.info("Created new a tag in link directive");
- $element.append(a);
- });
- }
- };
- });</span>
指令的控制器和link函數能夠進行互換。控制器主要是用來提供可在指令間複用的行爲,但連接函數只能在當前內部指令中定義行爲,且沒法在指令間複用
因爲指令能夠require其餘指令所使用的控制器,所以控制器常被用來放置在多個指令間共享的動做。
若是咱們但願將當前指令的API暴露給其餘指令使用,可使用controller參數,不然可使用link來構造當前指令元素的功能性。若是咱們使用了scope.$watch()或者想要與DOM元素作實時的交互,使用連接會是更好的選擇。
技術上講,$scope會在DOM元素被實際渲染以前傳入到控制器中。在某些狀況下,例如使用了嵌入,控制器中的做用域所反映的做用域可能與咱們所指望的不同,這種狀況下,$scope對象沒法保證能夠被正常更新。
controller,link,compile有什麼不一樣
- <span style="color:#330033;">
- phonecatDirectives.directive('exampleDirective', function() {
- return {
- restrict: 'E',
- template: '<p>Hello {{number}}!</p>',
- controller: function($scope, $element){
- $scope.number = $scope.number + "22222 ";
- },
-
- link: function(scope, el, attr) {
- scope.number = scope.number + "33333 ";
- },
- compile: function(element, attributes) {
- return {
- pre: function preLink(scope, element, attributes) {
- scope.number = scope.number + "44444 ";
- },
- post: function postLink(scope, element, attributes) {
- scope.number = scope.number + "55555 ";
- }
- };
- }
- }
- });
-
- dtControllers.controller('directive2',['$scope',
- function($scope) {
- $scope.number = '1111 ';
- }
- ]);
-
- <body ng-app="phonecatApp">
- <div ng-controller="directive2">
- <example-directive></example-directive>
- </div>
- </body></span>
運行結果:
- <span style="color:#330033;">Hello 1111 22222 44444 55555 ! </span>
由結果能夠看出來,controller先運行,compile後運行,link不運行(link就是compile中的postLink)。將上例中的compile註釋掉運行結果:
- <span style="color:#330033;">Hello 1111 22222 33333 ! </span>
由結果能夠看出來,controller先運行,link後運行,link和compile不兼容。compile改變dom,link事件的觸發和綁定
controllerAs
controllerAs參數用來設置控制器的別名,能夠以此爲名來發布控制器,而且做用域能夠訪問controllerAs。這樣就能夠在視圖中引用控制器,甚至無需注入$scope。
例如,建立一個MainController,而後不要注入$scope,以下所示:
- <span style="color:#330033;">angular.module('myApp')
- .controller('MainController', function() {
- this.name = "Ari";
- });</span>
如今,在HTML中無需引用做用域就可使用MainController。
- <span style="color:#330033;"><div ng-appng-controller="MainControllerasmain">
- <input type="text" ng-model="main.name" />
- <span>{{ main.name }}</span>
- </div></span>
這個參數看起來好像沒什麼大用,但它給了咱們能夠在路由和指令中建立匿名控制器的強大能力。這種能力能夠將動態的對象建立成爲控制器,而且這個對象是隔離的、易於測試的。
例如,能夠在指令中建立匿名控制器,以下所示:
- <span style="color:#330033;">angular.module('myApp')
- .directive('myDirective', function() {
- return {
- restrict: 'A',
- template: '<h4>{{ myController.msg }}</h4>',
- controllerAs: 'myController',
- controller: function() {
- this.msg = "Hello World"
- }
- };
- });</span>
require
require參數能夠被設置爲字符串或數組,字符串表明另一個指令的名字。require會將控制器注入到其值所指定的指令中,並做爲當前指令的連接函數的第四個參數。
字符串或數組元素的值是會在當前指令的做用域中使用的指令名稱。
scope會影響指令做用域的指向,是一個隔離做用域,一個有依賴的做用域或者徹底沒有做用域。
在任何狀況下,AngularJS編譯器在查找子控制器時都會參考當前指令的模板。
require參數的值能夠用下面的前綴進行修飾,這會改變查找控制器時的行爲:
?
若是在當前指令中沒有找到所須要的控制器,會將null做爲傳給link函數的第四個參數。
^
若是添加了^前綴,指令會在上游的指令鏈中查找require參數所指定的控制器。
?^
將前面兩個選項的行爲組合起來,咱們可選擇地加載須要的指令並在父指令鏈中進行查找。
沒有前綴
若是沒有前綴,指令將會在自身所提供的控制器中進行查找,若是沒有找到任何控制器(或具備指定名字的指令)就拋出一個錯誤。
下面這個例子可以很好的解釋
- <span style="color:#330033;">var app = angular.modeule('myapp',[]);
-
- app.directive('common',function(){
- return {
- ...
- controller: function($scope){
- this.method1 = function(){
- };
- this.method2 = function(){
- };
- },
- ...
- }
- });
-
- app.directive('d1',function(){
- return {
- ...
- require: '?^common',
- link: function(scope,elem,attrs,common){
- scope.method1 = common.method1;
- ..
- },
- ...
- }
- });</span>
ngModal
ngModel是一個用法特殊的指令,它提供更底層的API來處理控制器內的數據。當咱們在指令中使用ngModel時可以訪問一個特殊的API,這個API用來處理數據綁定、驗證、CSS更新等不實際操做DOM的事情。
ngModel控制器會隨ngModel被一直注入到指令中,其中包含了一些方法。
- <span style="color:#330033;">angular.module('myApp')
- .directive('myDirective',function(){
- return {
- require: '?ngModel',
- link: function(scope, ele, attrs, ngModel) {
- if (!ngModel) return;
- }
- };
- });</span>
若是不設置require選項,ngModelController就不會被注入到指令中。
注意,這個指令沒有隔離做用域。若是給這個指令設置隔離做用域,將致使內部ngModel沒法更新外部ngModel的對應值:AngularJS會在本地做用域之外查詢值。
爲了設置做用域中的視圖值,須要調用ngModel.$setViewValue()函數。ngModel.$setViewValue()函數能夠接受一個參數。
value(字符串):value參數是咱們想要賦值給ngModel實例的實際值。這個方法會更新控制器上本地的$viewValue,而後將值傳遞給每個$parser函數(包括驗證器)。
$setViewValue()方法適合於在自定義指令中監聽自定義事件(好比使用具備回調函數的jQuery插件),咱們會但願在回調時設置$viewValue並執行digest循環。
- <span style="color:#330033;">angular.module('myApp')
- .directive('myDirective', function() {
- return {
- require: '?ngModel',
- link: function(scope, ele, attrs, ngModel) {
- if (!ngModel) return;
- $(function() {
- ele.datepicker({
- onSelect: function(date) {
- scope.$apply(function() {
- ngModel.$setViewValue(date);
- });
- }
- });
- });
- }
- };
- });</span>
看到這裏 可能你們仍是不明白 what fuck this
首先讓咱們在控制檯輸出ngmodel這個參數看看
- <span style="color:#330033;"><!DOCTYPE html>
- <html lang="en" ng-app="app">
- <head>
- <meta charset="UTF-8">
- <title>Document</title>
- <script src="angular.js" charset="utf-8"></script>
- </head>
- <body ng-controller='ctrl'>
- <input type="text" test ng-model=_val>
- <script>
- var app = angular.module('app',[]);
- app.controller('ctrl',function ($scope){
- $scope._val = "leifengshushu";
- })
- app.directive('test',function(){
- return{
- restrict: 'AE',
- require: 'ngModel',
- link: function (scope,iElem,iAttr,ngmodel){
- console.log(ngmodel)
- }
- }
- })
- </script>
- </body>
- </html></span>
這個對象包含不少屬性和方法
$viewValue爲視圖值,即顯示在視圖(頁面)的實際值(就是上面例子中input輸入框的值)
$modelValue爲模型值,即賦給ng-model的值(與控制器綁定的值)
二者不必定相等,由於$viewValue同步到$modelValue要通過一系列的操做。
雖然大多數狀況下二者是相等的(例如上面的例子)
$parsers爲一個執行它裏面每個元素(每個元素都是一個函數)的數組,主要是用來作驗證和轉換值的過程,ngModel從DOM讀取的值會被傳入到其中的函數
它會依次執行每個函數,把每個函數執行的結果傳個下一個函數,而最後一個函數執行的值將會傳到model中,咱們能夠將函數push進去,那樣它就會執行。
$formatters也是一個執行它裏面每個元素(每個元素都是一個函數)的數組,主要用來對值進行格式化和轉換,以便在綁定了這個值的控件中顯示。
當數據的模型值發生變化的時候,裏面的函數會被一一執行,一樣咱們就能夠將函數push進去,讓它執行
$viewChangeListeners的值也是一個由函數組成的數組,當視圖的值發生變化的時候裏面的函數會被一一調用,
實現跟$watch相似的功能。
$render函數負責將模型值同步到視圖上, 若是模型值被改變,須要同步視圖的值。
$setViewValue用於設置視圖值(上面的例子就是將input的value值賦值給$viewValue);
還有一些屬性 在這裏就不詳解 請你們 百度 ngmodal
下面咱們來講說 $apply() 和 $digest();
$apply()和$digest()在AngularJS中是兩個核心概念,可是有時候它們又讓人困惑。而爲了瞭解AngularJS的工做方式,首先須要瞭解$apply()和$digest()是如何工做的。這篇文章旨在解釋$apply()和$digest()是什麼,以及在平常的編碼中如何應用它們。
探索$apply()和$digest()
AngularJS提供了一個很是酷的特性叫作雙向數據綁定(Two-way Data Binding),這個特性大大簡化了咱們的代碼編寫方式。數據綁定意味着當View中有任何數據發生了變化,那麼這個變化也會自動地反饋到scope的數據上,也即意味着scope模型會自動地更新。相似地,當scope模型發生變化時,view中的數據也會更新到最新的值。那麼AngularJS是如何作到這一點的呢?當你寫下表達式如{{ aModel }}時,AngularJS在幕後會爲你在scope模型上設置一個watcher,它用來在數據發生變化的時候更新view。這裏的watcher和你會在AngularJS中設置的watcher是同樣的:
- <span style="color:#330033;">$scope.$watch('aModel', function(newValue, oldValue) {
-
- });</span>
傳入到$watch()中的第二個參數是一個回調函數,該函數在aModel的值發生變化的時候會被調用。當aModel發生變化的時候,這個回調函數會被調用來更新view這一點不難理解,可是,還存在一個很重要的問題!AngularJS是如何知道何時要調用這個回調函數呢?換句話說,AngularJS是如何知曉aModel發生了變化,才調用了對應的回調函數呢?它會週期性的運行一個函數來檢查scope模型中的數據是否發生了變化嗎?好吧,這就是$digest循環的用武之地了。
在$digest循環中,watchers會被觸發。當一個watcher被觸發時,AngularJS會檢測scope模型,如何它發生了變化那麼關聯到該watcher的回調函數就會被調用。那麼,下一個問題就是$digest循環是在何時以各類方式開始的?
在調用了$scope.$digest()後,$digest循環就開始了。假設你在一個ng-click指令對應的handler函數中更改了scope中的一條數據,此時AngularJS會自動地經過調用$digest()來觸發一輪$digest循環。當$digest循環開始後,它會觸發每一個watcher。這些watchers會檢查scope中的當前model值是否和上一次計算獲得的model值不一樣。若是不一樣,那麼對應的回調函數會被執行。調用該函數的結果,就是view中的表達式內容(譯註:諸如{{ aModel }})會被更新。除了ng-click指令,還有一些其它的built-in指令以及服務來讓你更改models(好比ng-model,$timeout等)和自動觸發一次$digest循環。
目前爲止還不錯!可是,有一個小問題。在上面的例子中,AngularJS並不直接調用$digest(),而是調用$scope.$apply(),後者會調用$rootScope.$digest()。所以,一輪$digest循環在$rootScope開始,隨後會訪問到全部的children scope中的watchers。
如今,假設你將ng-click指令關聯到了一個button上,並傳入了一個function名到ng-click上。當該button被點擊時,AngularJS會將此function包裝到一個wrapping function中,而後傳入到$scope.$apply()。所以,你的function會正常被執行,修改models(若是須要的話),此時一輪$digest循環也會被觸發,用來確保view也會被更新。
Note: $scope.$apply()會自動地調用$rootScope.$digest()。$apply()方法有兩種形式。第一種會接受一個function做爲參數,執行該function而且觸發一輪$digest循環。第二種會不接受任何參數,只是觸發一輪$digest循環。咱們立刻會看到爲何第一種形式更好。
何時手動調用$apply()方法?
若是AngularJS老是將咱們的代碼wrap到一個function中並傳入$apply(),以此來開始一輪$digest循環,那麼何時才須要咱們手動地調用$apply()方法呢?實際上,AngularJS對此有着很是明確的要求,就是它只負責對發生於AngularJS上下文環境中的變動會作出自動地響應(即,在$apply()方法中發生的對於models的更改)。AngularJS的built-in指令就是這樣作的,因此任何的model變動都會被反映到view中。可是,若是你在AngularJS上下文以外的任何地方修改了model,那麼你就須要經過手動調用$apply()來通知AngularJS。這就像告訴AngularJS,你修改了一些models,但願AngularJS幫你觸發watchers來作出正確的響應。
好比,若是你使用了JavaScript中的setTimeout()來更新一個scope model,那麼AngularJS就沒有辦法知道你更改了什麼。這種狀況下,調用$apply()就是你的責任了,經過調用它來觸發一輪$digest循環。相似地,若是你有一個指令用來設置一個DOM事件listener而且在該listener中修改了一些models,那麼你也須要經過手動調用$apply()來確保變動會被正確的反映到view中
讓咱們來看一個例子。加入你有一個頁面,一旦該頁面加載完畢了,你但願在兩秒鐘以後顯示一條信息。你的實現多是下面這個樣子的:
- <span style="color:#330033;"><body ng-app="myApp">
- <div ng-controller="MessageController">
- Delayed Message: {{message}}
- </div>
- </body></span>
- <span style="color:#330033;"> angular.module('myApp',[]).controller('MessageController', function($scope) {
-
- $scope.getMessage = function() {
- setTimeout(function() {
- $scope.message = 'Fetched after 3 seconds';
- console.log('message:'+$scope.message);
- }, 2000);
- }
-
- $scope.getMessage();
-
- }); </span>
經過運行這個例子,你會看到過了兩秒鐘以後,控制檯確實會顯示出已經更新的model,然而,view並無更新。緣由也許你已經知道了,就是咱們忘了調用$apply()方法。所以,咱們須要修改getMessage(),以下所示:
- <span style="color:#330033;">angular.module('myApp',[]).controller('MessageController', function($scope) {
-
- $scope.getMessage = function() {
- setTimeout(function() {
- $scope.$apply(function() {
-
- $scope.message = 'Fetched after 3 seconds';
- console.log('message:' + $scope.message);
- });
- }, 2000);
- }
-
- $scope.getMessage();
-
- }); </span>
$digest循環會運行多少次?
當一個$digest循環運行時,watchers會被執行來檢查scope中的models是否發生了變化。若是發生了變化,那麼相應的listener函數就會被執行。這涉及到一個重要的問題。若是listener函數自己會修改一個scope model呢?AngularJS會怎麼處理這種狀況?
答案是$digest循環不會只運行一次。在當前的一次循環結束後,它會再執行一次循環用來檢查是否有models發生了變化。這就是髒檢查(Dirty Checking),它用來處理在listener函數被執行時可能引發的model變化。所以,$digest循環會持續運行直到model再也不發生變化,或者$digest循環的次數達到了10次。所以,儘量地不要在listener函數中修改model。
service
服務提供了一種能在應用的整個生命週期內保持數據的方法,它可以在控制器之間進行通訊,而且能保證數據的一致性。
服務是一個單例對象,在每一個應用中只會被實例化一次(被$injector實例化),而且是延遲加載的(須要時纔會被建立)。
服務提供了把與特定功能相關聯的方法集中在一塊兒的接口。
註冊一個服務
使用angular.module的factoryAPI建立服務,是最多見也是最靈活的方式:
- angular.module('myApp.services', [])
- .factory('githubService', function() {
- var serviceInstance = {};
-
- return serviceInstance;
- });
- var Person = function($http) {
- this.getName = function() {
- return $http({ method: 'GET', url: '/api/user'});
- };
- };
- angular.service('personService', Person);
- angular.module('myApp')
- .factory('myService', function() {
- return {
- 'username': 'auser'
- };
- })
- .provider('myService', {
- $get: function() {
- return {
- 'username': 'auser'
- };
- }
- });
- angular.module('myApp.services', [])
- .factory('githubService', function($http) {
- var githubUrl = 'https://api.github.com';
- var runUserRequest = function(username, path) {
- return $http({
- method: 'JSONP',
- url: githubUrl + '/users/' +
- username + '/' +
- path + '?callback=JSON_CALLBACK'
- });
- };
- return {
- events: function(username) {
- return runUserRequest(username, 'events');
- }
- };
- });
能夠在控制器、指令、過濾器或另一個服務中經過依賴聲明的方式來使用服務。
將服務的名字看成參數傳遞給控制器函數,能夠將服務注入到控制器中。當服務成爲了某個控制器的依賴,就能夠在控制器中調用任何定義在這個服務對象上的方法。
- angular.module('myApp', ['myApp.services'])
- .controller('ServiceController', function($scope, githubService) {
- $scope.events = githubService.events('auser');
- });
constant('name','value')
- angular.module('myApp') .constant('apiKey','123123123')
- angular.module('myApp')
- .controller('MyController', function($scope, apiKey) {
- $scope.apiKey = apiKey;
- });
value('name','value')
- angular.module('myApp')
- .value('apiKey','123123123');
value()方法和constant()方法之間最主要的區別是,常量能夠注入到配置函數中,而值不行。一般狀況下,能夠經過value()來註冊服務對象或函數,用constant()來配置數據。
- angular.module('myApp', [])
- .constant('apiKey', '123123123')
- .config(function(apiKey) {
- })
- .value('FBid','231231231')
- .config(function(FBid) {
- });
下面咱們來講說他們的區別 (provider,value,constant,service,factory,decorator)
provider是幹啥的?
provider能夠爲應用提供通用的服務,形式能夠是常量,也能夠是對象。
好比咱們在controller裏經常使用的$http就是AngularJS框架提供的provider
- myApp.controller(‘MainController', function($scope, $http) {
- $http.get(…)
- }
在上面的代碼裏,就能夠直接使用$http包好的各類功能了
下面咱們本身定義一個provider
- $provide.provider('age', {
- start: 10,
- $get: function() {
- return this.start + 2;
- }
- });
- $provide.provider('age', function($filterProvider){
- this.start = 10;
- this.$get = function() {
- return this.start + 2;
- };
- });
- app.controller('MainCtrl', function($scope, age) {
- $scope.age = age;
- });
provider的基本原則就是經過實現$get方法來在應用中注入單例,使用的時候拿到的age就是$get執行後的結果。 上面例子中的兩種定義方法均可以
下面咱們本身定義 factory
- $provide.provider('myDate', {
- $get: function() {
- return new Date();
- }
- });
- $provide.factory('myDate', function(){
- return new Date();
- });
- app.controller('MainCtrl', function($scope, myDate) {
- $scope.myDate = myDate;
- });
下面咱們去定義service
- $provide.provider('myDate', {
- $get: function() {
- return new Date();
- }
- });
- $provide.factory('myDate', function(){
- return new Date();
- });
- $provide.service('myDate', Date);
value 和 constant ,value能夠被修改 而constant 不能被修改,value 不能在 config 裏面注入可是constant 能夠
angularjs 與服務器的 交互