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


使用AngularJS這么久以來,遇到過一些坑,之前一直沒有以書面的形式整理下,現在好好總結下,以供后面查備。

 

一、angular scope 在ng-if與ng-show下的區別(兩者作用域的差別)

以下代碼為一個針對應用列表分頁的代碼:

HTML代碼:

對應的js代碼為:

在頁面中我們可以看到效果:


可以看到:在使用ng-if時,點擊分頁時,在ng-if包括的作用域里,$scope.currentPage的值改變了,但是ng-if所在作用域外面,打印$scope.currentPage的值時,仍然不變,始終是初始化的狀態,並且可以看到獲取的列表也並沒有改變。

 

解決方案:將ng-if換為ng-show,或者ng-model=”currentPage”改為ng-model=”parentObj.currentPage”,下面看看解決之后的效果:

 



可以看到,選擇分頁時,頁碼相應的改變了,獲得列表也正確了。

 

為什么會出現上述情況呢?小編在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的原理是不一樣的:

 

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

 

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

 

二、UI閃爍問題

 

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

 

解決辦法:

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

 2、在元素上添加“ng-clock”,工作原理就是在初始化階段inject了css規則,或者你可以包含這個css 隱藏規則到你自己的stylesheet。Angular就緒后就會移除這個cloak樣式,讓我們的應用(或者元素)立刻渲染。

 

三、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指令:

 

 

四、angularJS文件壓縮問題

 

當項目上線時,我們往往需要將文件壓縮以減少體積來提高頁面的加載速度,普通的javascript壓縮一般都沒什么問題,但是如果是以下格式編碼的angularJS文件壓縮后,在使用則會報錯:

 

 

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

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

 

如以下登陸的controller:


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

 

 

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

 

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

 



 

六、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重新編譯了一遍。

 

七、使用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是數據的主鍵值。

 

八、$apply的使用

 

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

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

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

 

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

 

 





 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM