JavaScript開發教程之Angularjs留存問題(二)

JavaScript開發教程之Angularjs留存問題,Angularjs留存系列主要是用來記錄一些自己在學習其他文章時覺得有必要留存一下的fortune!

PS:Angularjs留存系列主要是用來記錄一些自己在學習其他文章時覺得有必要留存一下的fortune!

文章可能會很長(默認每篇17個小的知識點),但是我們可以使用Ctrl + F 來查找啦!

1、AngularJS中,子作用域一般都會通過JavaScript原型繼承機制繼承其父作用域的屬性和方法。但有一個例外:在directive中使用scope: { … },這種方式創建的作用域是一個獨立的"Isolate"作用域,它也有父作用域,但父作用域不在其原型鏈上,不會對父作用域進行原型繼承。這種方式定義作用域通常用於構造可復用的directive組件。

作用域的原型繼承是非常簡單普遍的,甚至你不必關心它的運作。直到你在子作用域中向父作用域的原始類型屬性使用雙向數據綁定2-way data binding,比如Form表單的ng-model為父作用域中的屬性,且為原始類型,輸入數據後,它不會如你期望的那樣運行——AngularJS不會把輸入數據寫到你期望的父作用域屬性中去,而是直接在子作用域創建同名屬性並寫入數據。這個行為符合JavaScript原型繼承機制的行為。AngularJS新手通常沒有認識到ng-repeat、ng-switch、ng-view和ng-include都會創建子作用域, 所以經常出問題。 (見示例)

避免這個問題的最佳實踐是在ng-model中總使用.,(至於為什麼隻要仔細體會JavaScript原型繼承機制,我相信你就可以明白!!!)。。。參見文章always have a '.' in your ng-models。

比如:

<input type="text" ng-model="someObj.prop1">

優於:

<input type="text" ng-model="prop1">

如果你一定要直接使用原始類型,要註意兩點:

在子作用域中使用$parent.parentScopeProperty,這樣可以直接修改父作用域的屬性。在父作用域中定義函數,子作用域通過原型繼承調用函數把值傳遞給父作用域(這種方式極少使用)。 2、AngularJS存在四種作用域:(這篇文章講得很好)
普通的帶原型繼承的作用域 –ng-include,ng-switch,ng-controller, directive withscope: true;普通的帶原型繼承的,並且有賦值行為的作用域 –ng-repeat,ng-repeat為每一個迭代項創建一個普通的有原型繼承的子作用域,但同時在子作用域中創建新屬性存儲迭代項;“Isolate”作用域 — directive withscope: {…}, 該作用域沒有原型繼承,但可以通過'=', '@', 和 '&'與父作用域通信。“transcluded”作用域 — directive withtransclude: true,它也是普通的帶原型繼承的作用域,但它與“Isolate”作用域是相鄰的好基友。 3、一招制敵 – 玩轉 AngularJS 指令的 Scope (作用域)
每當一個指令被創建的時候,都會有這樣一個選擇,是繼承自己的父作用域(一般是外部的Controller提供的作用域或者根作用域($rootScope)),還是創建一個新的自己的作用域,當然AngularJS為我們指令的scope參數提供瞭三種選擇,分別是:false,true,{};默認情況下是false。
(1)、當我們將scope設置為true的時候,我們就新創建瞭一個作用域,隻不過這個作用域是繼承瞭我們的父作用域;我覺得可以這樣理解,我們新創建的作用域是一個新的作用域,隻不過在初始化的時候,用瞭父作用域的屬性和方法去填充我們這個新的作用域。它和父作用域不是同一個作用域。
(2)、當我們將scope設置為false的時候,我們創建的指令和父作用域(其實是同一個作用域)共享同一個model模型,所以在指令中修改模型數據,它會反映到父作用域的模型中。

這是一篇對指令的Scope講得很透徹的文章,也有相應的Demo參考! 強烈推薦對Directive的Scope有疑惑的同學去閱讀學習下!

4、理解Angular中的$apply()以及$digest() 什麼時候手動調用$apply()方法?

如果AngularJS總是將我們的代碼wrap到一個function中並傳入$apply(),以此來開始一輪$digest循環,那麼什麼時候才需要我們手動地調用$apply()方法呢?實際上,AngularJS對此有著非常明確的要求,就是它隻負責對發生於AngularJS上下文環境中的變更會做出自動地響應(即,在$apply()方法中發生的對於models的更改)。AngularJS的built-in指令就是這樣做的,所以任何的model變更都會被反映到view中。但是,如果你在AngularJS上下文之外的任何地方修改瞭model,那麼你就需要通過手動調用$apply()來通知AngularJS。
你總是應該使用接受一個function作為參數的$apply()方法。這是因為當你傳入一個function到$apply()中的時候,這個function會被包裝到一個try…catch塊中,所以一旦有異常發生,該異常會被$exceptionHandler service處理。
$digest循環會運行多少次?

答案是$digest循環不會隻運行一次。在當前的一次循環結束後,它會再執行一次循環用來檢查是否有models發生瞭變化。這就是臟檢查(Dirty Checking),它用來處理在listener函數被執行時可能引起的model變化。因此,$digest循環會持續運行直到model不再發生變化,或者$digest循環的次數達到瞭10次。因此,盡可能地不要在listener函數中修改model。

Note: $digest循環最少也會運行兩次,即使在listener函數中並沒有改變任何model。正如上面討論的那樣,它會多運行一次來確保models沒有變化。

5、AngularJS 之 Factory vs Service vs Provider

用Factory就是創建一個對象,為它添加屬性,然後把這個對象返回出來。你把 service 傳進 controller 之後,在 controller 裡這個對象裡的屬性就可以通過 factory 使用瞭。

FactoryExample1

2)Service是用"new"關鍵字實例化的。因此,你應該給"this"添加屬性,然後 service 返回"this"。你把 service 傳進 controller 之後,在controller裡 "this" 上的屬性就可以通過 service 來使用瞭。

ServiceExample2

3)Providers是唯一一種你可以傳進 .config() 函數的 service。當你想要在 service 對象啟用之前,先進行模塊范圍的配置,那就應該用 provider。

ProviderExample3

什麼時候適合使用factory()方法

在service裡面當我們僅僅需要的是一個方法和數據的集合且不需要處理復雜的邏輯的時候,factory()是一個非常不錯的選擇。
註意:需要使用.config()來配置service的時候不能使用factory()方法

什麼時候適合使用service()方法

service()方法很適合使用在功能控制比較多的service裡面
註意:需要使用.config()來配置service的時候不能使用service()方法

什麼時候適合使用provider()方法

當我們希望在應用開始前對service進行配置的時候就需要使用到provider()。比如,我們需要配置services在不同的部署環境裡面(開發,演示,生產)使用不同的後端處理的時候就可以使用到瞭
當我們打算發佈開源provider()也是首選創建service的方法,這樣就可以使用配置的方式來配置services而不是將配置數據硬編碼寫到代碼裡面。

6、作用域也隻不過是在彼此間進行瞭原型繼承。所有可以用在 JavaScript 對象上的原型繼承的規則,都可以同等的用在 作用域 的原型鏈繼承上去。畢竟Scopes作用域就是 JavaScript 對象嘛。

在子級作用域中去改變父級作用域上面的屬性有幾種方法。第一種,我們就直接通過$parent屬性來引用父級作用域,但我們要看到,這是一個非常不可靠的解決方案。麻煩之處就在於,ng-model指令所使用的表達式非常嚴重的依賴於整個DOM結構。比如就在<input>標簽上面的哪裡插入另一個可創建作用域的指令,那$parent就會指向一個完全不同的作用域瞭。

就經驗來講,盡量避免使用$parent屬性,因為它強制的把 AngularJS 表達式和你的模板所創建的 DOM 結構捆綁在瞭一起。這樣一來,HTML結構的一個小小的改動,都可能會讓整個應用崩潰。

另一個解決方案就是,不要直接把屬性綁定到 作用域上,而是綁到一個對象上面,如下所示:

<body ng-app ng-init="thing = {name : 'World'}"> 
    <h1>Hello, {{thing.name}}</h1>
    <p ng-controller="HelloCtrl">
        Say hello to: <input type="text" ng-model="thing.name">
        <h2>Hello, {{thing.name}}!</h2> 
    </p>
</body>

這個方案會更可靠,因為他並沒有假設 DOM 樹的結構是什麼樣子。

避免直接把數據綁定到 作用域的屬性上。應優先選擇把數據雙向綁定到對象的屬性上(然後再把對象掛到 scope 上)。
就經驗而言,在給ng-model指令的表達式中,你應該有一個點(例如,ng-model="thing.name")。

7、給你一個承諾 – 玩轉 AngularJS 的 Promise

Promise是一種異步方式處理值(或者非值)的方法,promise是對象,代表瞭一個函數最終可能的返回值或者拋出的異常。 要在AngularJS中使用Promise,要使用AngularJS的內置服務$q。 我們可以先使用$q的defer()方法創建一個deferred對象,然後通過deferred對象的promise屬性,將這個對象變成一個promise對象;這個deferred對象還提供瞭三個方法,分別是resolve(),reject(),notify()。

Test1

我們先通過一個同步的例子來創建一個promise對象。

HTML代碼:

<p ng-app="MyApp">
    <p ng-controller="MyController">
        <label for="flag">成功
        <input id="flag" type="checkbox" ng-model="flag" /><br/>
        </label>
        <hr/>
        <button ng-click="handle()">點擊我</button>
    </p>
</p>

JS代碼:

angular.module("MyApp", [])
.controller("MyController", ["$scope", "$q", function ($scope, $q) {
            $scope.flag = true;
            $scope.handle = function () {
            var deferred = $q.defer();
            var promise = deferred.promise;

            promise.then(function (result) {
                alert("Success: " + result);
            }, function (error) {
                alert("Fail: " + error);
            });

            if ($scope.flag) {
                deferred.resolve("you are lucky!");
            } else {
                deferred.reject("sorry, it lost!");
            }
        }
}]);

我們來詳細的分析一下上面的代碼,我們在html頁面上添加瞭一個checkbox,一個button目的是為瞭當我們選中checkbox和不選中checkbox時,點擊下面的按鈕會彈出不同的內容。

var deferred = $q.defer()這段代碼創建瞭一個deferred對象,我們然後利用var promise = deferred.promise創建瞭一個promise對象。

我們給給promise的then方法傳遞瞭兩個處理函數,分別處理當promise被執行的時候以及promise被拒絕的時候所要進行的操作。

下面的一個if(){}else{}語句塊,包含執行和拒絕deferred promise,如果$scope.flag為true,那麼我們就會執行deferred promise,然後我們給promise傳遞一個值,也可能是一個對象,表明promise執行的結果。如果$scope.flag為false,那麼我們就會拒絕deferred promise,然後我們給promise傳遞一個值,也可能是一個對象,表明promise被拒絕的原因。

現在回過頭來看看,promise的then方法,如果promise被執行,那麼它的參數中的第一個函數的result就代表瞭"you are lucky!"

我們暫時用的是同步的模式,為的是能夠說明問題,後面將會使用異步的方法。

到這裡我們可以瞭解一下$q的defer()方法創建的對象具有哪些方法

resolve(value):用來執行deferred promise,value可以為字符串,對象等。
reject(value):用來拒絕deferred promise,value可以為字符串,對象等。
notify(value):獲取deferred promise的執行狀態,然後使用這個函數來傳遞它。
then(successFunc, errorFunc, notifyFunc):無論promise是成功瞭還是失敗瞭,當結果可用之後,then都會立刻異步調用successFunc,或者'errorFunc',在promise被執行或者拒絕之前,notifyFunc可能會被調用0到多次,以提供過程狀態的提示。
catch(errorFunc)
finally(callback) 8、AngularJS表單基礎

首先,HTML原生的form表單是不能嵌套的,而Angular封裝之後的form就可以嵌套。

其次,Angular為form擴展瞭自動校驗、防止重復提交等功能。

有人說,如果我想要使用原生的HTML表單,應該怎麼做呢?

Angular提供瞭一個ng-pristine指令,把這個指令寫在form標簽中,Angular就知道你想使用原生的form標簽瞭。

Angular為表單內置瞭4中CSS樣式。

ng-valid 校驗合法狀態
ng-invalid 校驗非法狀態
ng-pristine 如果要使用原生的form,需要設置這個值
ng-dirty     表單處於臟數據狀態

Angular在對表單進行自動校驗的時候會校驗Model上的屬性,如果不設置ng-model,則Angular無法知道myForm.$invalid這個值是否為真。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *