那些年,在AngularJS的路上遇到的坑

使用AngularJS這麼久以來,遇到過一些坑,以前一直沒有以書面的形式整理下,如今好好總結下,以供後面查備。javascript

 

1、angular scope 在ng-if與ng-show下的區別(二者做用域的差異)php

如下代碼爲一個針對應用列表分頁的代碼:css

HTML代碼:html

對應的js代碼爲:java

在頁面中咱們能夠看到效果:ios


能夠看到:在使用ng-if時,點擊分頁時,在ng-if包括的做用域裏,$scope.currentPage的值改變了,可是ng-if所在做用域外面,打印$scope.currentPage的值時,仍然不變,始終是初始化的狀態,而且能夠看到獲取的列表也並無改變。ajax

 

解決方案:將ng-if換爲ng-show,或者ng-model=」currentPage」改成ng-model=」parentObj.currentPage」,下面看看解決以後的效果:chrome

 



能夠看到,選擇分頁時,頁碼相應的改變了,得到列表也正確了。數據庫

 

爲何會出現上述狀況呢?小編在http://camnpr.com/javascript/1888.html和http://stackoverflow.com/questions/21869283/when-to-favor-ng-if-vs-ng-show-ng-hide??這兩個地方找到了答案,究其緣由就是ng-if 指令像其它指令同樣,會建立一個子域(child scope)。後端

 

 

所以,ng-if裏的ng-model的$scope是子域下的,外部做用域沒法捕獲$scope的更新值,而ng-show則是做用於外部做用域的。

 

綜上,ng-if與ng-show的原理是不同的:

 

一、ng-show/ng-hide是經過修改CSS樣式方式控制元素顯示與隱藏,對應的DOM元素會一直存在於當前頁面中,而ng-if根據表達式的值動態的在當前的頁面中添加刪除頁面元素。若是賦值表達式的值爲false,那麼這個元素就會從頁面中刪除,不然會添加一個元素。ng-if建立元素時用的是被它編譯後的代碼,若是ng-if內部的代碼被其它方式修改過,那麼修改只會對本次展示有效,頁面元素從新渲染後修改效果會消失,而ng-show/ng-hide則可以保留dom元素上次修改後的狀態。

 

二、做用域不一樣:當一個元素被ng-if從DOM中刪除時,與其關聯的做用域也會被銷燬。並且當它從新加入DOM中時,則會生成一個新的做用域,而ng-show和ng-hide則不會。

 

2、UI閃爍問題

 

衆所周知,Angular最大的亮點就是數據的雙向綁定。然而,在項目的實際使用過程當中,每每會碰到頁面閃現出沒有被解析的表達式(形如{{app.name}})。是由於在Angular初始化以前,DOM還沒準備就緒,Angular正在計算並替換相應的值。

 

解決辦法:

一、放棄使用{{ }}表達式,改用ng-bind指令;

 二、在元素上添加「ng-clock」,工做原理就是在初始化階段inject了css規則,或者你能夠包含這個css 隱藏規則到你本身的stylesheet。Angular就緒後就會移除這個cloak樣式,讓咱們的應用(或者元素)馬上渲染。

 

3、ng-include指令

 

<div ng-include="views/include/footer.html"></div> 錯在哪裏,記得在第一次使用ng-include的時候,這樣引用文件,始終在頁面上都不顯示footer.html的內容,後來明白ng-include須要的是一個變量,因此咱們能夠改寫成<div ng-include="’views/include/footer.html’"></div> 或定義一個$scope變量,如:$scope.footerHtml="views/include/footer.html"; 頁面引用:<div ng-include="footerHtml"></div>。效果以下圖所示:

 


 

究其緣由是:由於在ng-include中,是把它的參數當作變量來解釋的,它會經過$eval對傳入的值進行計算,而後做爲模板地址去加載。

 

不過,更好的方法是把這些界面片斷(partical)寫成指令,那樣你就不用在多處重複寫路徑了,更重要的是,未來你能夠直接在這裏擴展它的交互邏輯,從界面原型平滑的演化到線上系統。

 

下面舉一個寫成指令的例子:

HTML頁面:頁面中直接使用指令,指令會找到對應的頁面進而進行DOM的渲染。

 

directive指令:

 

 

4、angularJS文件壓縮問題

 

當項目上線時,咱們每每須要將文件壓縮以減小體積來提升頁面的加載速度,普通的javascript壓縮通常都沒什麼問題,可是若是是如下格式編碼的angularJS文件壓縮後,在使用則會報錯:

 

 

解決辦法是:使用Javascript數組方式構造控制器:把要注入的服務放到一個字符串數組(表明依賴的名字)裏,數組最後一個元素是控制器的方法函數:

varBookCtrl = ['$scope', '$http', function($scope, $http) { /* constructor body */}];

 

如如下登錄的controller:


這樣使用數組的方式在壓縮後,就不會出現依賴的錯誤了。

 

 

5、IE11下get請求緩存問題,頁面綁定數據不及時更新的bug

 

在項目開發的過程當中,我曾經發現過一個奇葩的問題,就是在IE11瀏覽器下,當點擊某個button觸發一個函數,從而改變ng-model綁定的某個變量時,明確變量的值已經改變,可是隻有在F12開啓開發者調試工具時,頁面才能及時更新顯示改變後的變量值,一旦F12開發者調試工具關閉,變量值將不更新。可是這一現象在chrome、firefox下確未曾見,究其緣由竟是IE瀏覽器下ajax請求緩存的問題,後來把緩存禁用後,竟恢復正常了。具體解決方法就是:在配置路由的angularJS文件中,添加以下配置代碼便可:

 



 

6、angular中的unsafe問題

 

在項目中,以前遇到過這樣的問題,就是當給一個ios應用上傳安裝包和plist文件後,通常這個ios應用的下載地址就會變爲:

「itms-services:///?action=download-manifest&url=https://dn-touchc-test.qbox.me/test2.plist」這樣的樣子,可是當咱們把這樣的字符串放到<a>標籤的href屬性中時,其會自動在上述字符串以前加上「unsafe:」的字符串:以下圖所示:

 

 

而這個這樣加了「unsafe:」字樣的連接被咱們複製到手機瀏覽器裏進行下載則會報找不到。

解決辦法就是:在angular配置路由的配置文件中加入以下配置代碼:

 

再來看結果:

 

原來angular對href是有安全檢查的,只能生成它認爲安全的連接。AngularJS真是爲咱們的安全操碎了心……而我也更加明白了{{}}並非一個簡單的模板替換,它真的把咱們的html從新編譯了一遍。

 

7、使用ng-repeat或ng-options進行數據循環時,track by的使用

 

在使用ng-repeat進行數據循環時,若是後端數據庫採用的是moogodb的話,當 ng-repeat 的數組被替換時, 它默認並不會從新利用已有的 Dom 元素,而是直接將其所有刪除並從新生成新的數組 Dom 元素,Dom 的頻繁操做是很是不友好的,爲何 ng-repeat 不能利用已有的 dom 元素去更新數據呢?由於你沒有把數組元素的標識屬性告訴它,那麼兩次替換的時候它就沒辦法追蹤了,因此當首次使用ng-repeat渲染出列表數據,再次請求渲染數據的時候,ng-repeat會往數組中每一個元素加上$$hashKey屬性,這個 key 是由 Angular 內部的 nextUid() 方法生成,相似數據庫自增,可是是使用字符串。所以,咱們使用 track by 來避免,例如:



$index表示默認的索引(自動遞增),item.id是數據的主鍵值。

 

8、$apply的使用

 

以前在微信端修改我的設置的頁面時,接口沒有返回用戶的相關信息,只有經過另外一個接口去獲取用戶的具體信息,可是這個過程當中發現拿到用戶的具體信息從新給$scope.userData賦值後,雖然model值改變了,可是頁面並無及時更新,主要緣由是angular沒有檢測到數據變化,因此此時須要咱們手動通知angular數據改變,以下代碼所示:

 這裏使用的是$apply的無參數形式,另外還有一種形式是:

這樣也能夠,$scope.$apply() 會自動觸發 $rootScope.$digest(),從而讓 watchers 被觸發用以更新view。具體能夠參考這篇文章:深刻理解Angular中的$apply()以及$digest()

 

固然,後面的路還很長,遇到的坑也不止這些,我會持續更新的……

相關文章
相關標籤/搜索