使用AngualrJs開發移動App已經快半年了,逐漸積累了很是多AngularJS的問題,特別是對於用慣了Jquery的開發人員,轉到AngularJS仍是需要克服很是多問題的。不像Jquery那樣側重DOM操做,AngularJS是以視圖模型和雙向綁定爲核心的。
javascript
如下的內容若是你已經瞭解前端 MVC 概念,並對 AngularJS 有了必定經驗,剛開始學習的人讀起來可能比較艱深晦澀。本文的總結會涉及部分在移動設備上特有的問題。
php
避免使用 jQuery 來操做 DOM,包含添加元素節點,移除元素節點,獲取元素內容,隱藏或顯示元素。你應該使用 directives 來實現這些動做,有必要的話你還要編寫本身的 directives。css
在站點Web前端開發中,假設你感到很是難改變習慣,那麼考慮從你的網頁中移除 jQuery 吧。真的,AngularJS 中的 $http
服務很是強大,基本可以替代 jQuery 的 ajax 函數,而且 AngularJS 內嵌了 jQLite —— 它內部實現的一個 jQuery 子集,包括了常用的 jQuery DOM 操做方法,事件綁定等等。但這並不是說用了AngularJS 就不能用 jQuery 了。假設你的網頁有加載 jQuery 那麼 AngularJS 會優先採用你的 jQuery,不然它會 fall back 到 jQLite。html
假設是移動App或移動Web開發,建議不要引入Jquery了,假設實在需要jquery的某些功能,引入Zepto.js吧。只是請相信我,用了AngularJS,你不會需要Jquery的!前端
需要本身編寫 directives 的狀況通常是當你使用了第三方的 jQuery 插件。因爲插件在 AngularJS 以外對錶單值進行更改,並不能即時反應到 Model 中。好比咱們用得比較多的 jQueryUI datepicker 插件,當你選中一個日期後,插件會將日期字符串填到 input 輸入框中。View 改變了,卻並無更新 Model,因爲$('.datepicker').datepicker();
這段代碼不屬於 AngularJS 的管理範圍。咱們需要編寫一個directive 來讓 DOM 的改變即時更新到 Model 裏。java
var directives = angular.module('directives', []); directives.directive('datepicker', function() { return function(scope, element, attrs) { element.datepicker({ inline: true, dateFormat: 'dd.mm.yy', onSelect: function(dateText) { var modelPath = $(this).attr('ng-model'); putObject(modelPath, scope, dateText); scope.$apply(); } }); } });
而後在 HTML 中引入這個 directivejquery
<input type="text" datepicker ng-model="myObject.myDateValue" />Directive 就是在 HTML 裏寫本身定義的標籤屬性,達到插件的做用,有效補充了HTML的功能。這樣的聲明式的語法擴展了 HTML。建議項目中通用的功能和頁面組件,都封裝成Directive,方便使用和代碼維護。
需要說明的是,有一個 AngularUI 項目提供了大量的 directive 給咱們使用,包含 Bootstrap 框架中的插件以及基於 jQuery 的其它很是熱門的 UI 組件。 AngularJS 的社區現在很是活躍,生態系統健全。git
這是個大坑。假設你去查看 ngOption 生成的 <select>
中的 <option>
的選項值(每個 <option value="xxx">
的 value 部分),那絕對是枉費心機。因爲這裏的值永遠都會 是 AngularJS 內部元素的索引,並不是你所指定的表單選項值。github
仍是要轉變觀念,AngularJS 已經再也不用表單進行數據交互了,而是用 Model。使用 $http 來提交 Model,在 php 中則使用 file_get_contents('php://input')
來獲取前端提交的數據。ajax
AngularJS有些版本號,當輸入框設爲 Input type='number'時,在移動設備上ng-change方法會失效。
在頁面初始化的時候,用戶可能會看到 {{ }},而後閃爍一下才出現真正的內容。
解決的方法:
Controller 不該該直接引用 DOM,而應該控制 view 的行爲。好比「假設用戶操做了 X,應該發生什麼事情」,「我從哪裏可以得到 X?」
Service 在大部分狀況下也不該該直接引用 DOM,它應該是一個單例(singletons),獨立於界面,與 view 的邏輯無關。它的角色僅僅是「作 X 操做」。
DOM 操做應該放在 directives 裏面。
你所寫的功能很是可能 AngularJS 已經實現了,有一些代碼是可以抽象出來複用的,使用更 Angular 的方式。總之就是很是多 jQuery 的繁瑣代碼可以被替代。
ng-repeat
ng-repeat 很是實用。當 Ajax 從server得到數據後,咱們經常使用 jQuery (比方上面講過的樣例) 向某些 HTML 容器節點中加入不少其它的元素,這在 AngularJS 裏是很差的作法。有了 ng-repeat 一切就變得很是easy了。在你的 $scope 中定義一個數組 (model) 來保存從server拉取的數據,而後使用 ng-repeat 將它與 DOM 綁定就能夠。如下的樣例初始化定義了 friends 這個 model
<div ng-init="friends = [{name:'John', age:25}, {name:'Mary', age:28}]"> I have {{friends.length}} friends. They are: <ul> <li ng-repeat="friend in friends"> [{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old. </li> </ul> </div>
ng-show
ng-show 也很是實用。使用 jQuery 來依據條件控制界面元素的顯示隱藏,這非常常見。但是 Angular 有更好的方式來作到這一點。ng-show (以及 ng-hide) 可以依據布爾表達式來決定隱藏和顯示。
對於數組或字符串,可以用strXXXX.length控制顯示,不然在移動設備上會不正常。
相似的內置 directives 還有 ng-disabled, ng-switch 等等,用於條件控制,語法簡潔,都很是強大。
ng-class
ng-class 用於條件性地給元素加入 class,曾經咱們也經常用 jQuery 來實現。Angular 中的 ng-class 固然更好用了,樣例:
<div ng-class="{ errorClass: isError, warningClass: isWarning, okClass: !isError && !isWarning }">...</div>
在這裏 ng-class 接受一個 object 對象,key 爲 CSS class 名,值爲 $scope 變量控制的條件表達式,其它相似的內置 directives 還有 ng-class-even 和 ng-class-odd,很是有用。
使用ng-show和ng-if都實現控制頁面元素顯示的功能,但2者是不一樣的,ng-if會動態建立DOM,ng-show僅僅是切換已有DOM的顯示,至關於設置style="display:none",假設使用before和after等css僞類控制顯示效果,可能會出現故障,需要依據狀況合理使用ng-show和ng-if。
AngularJS 的雙向數據綁定是最使人興奮的特性了,然而它也不是全能的魔法,在某些狀況下你需要作一些小小的修正。
當你使用 ng-model, ng-repeat 等等來綁定一個元素的值時, AngularJS 爲那個值建立了一個 $watch,僅僅要這個值在 AngularJS 的範圍內有不論什麼改變,所有的地方都會同步更新。而你在寫本身定義的 directive 時,你需要定義你本身的 $watch 來實現這樣的本身主動同步。
有時候你在代碼中改變了 model 的值,view 卻沒有更新,這在本身定義事件綁定中經常遇到。這時你就需要手動調用 scope.$apply() 來觸發界面更新。上面 datepicker 的樣例已經說明了這一點。第三方插件可能會有 call back,咱們也可以把回調函數寫成匿名函數做爲參數傳入$apply()中。
ng-repeat 很是實用,只是它和 DOM 綁定了,很是難在同一個元素上使用其它 directives (比方 ng-show, ng-controller 等等)。
假設你想對整個循環使用某個 directive,你可以在 repeat 外再包一層父元素把 directive 寫在那兒;假設你想對循環內部的每一個元素使用某個 directive,那麼把它放到 ng-repeat 的一個子節點上就能夠。
Scope 在 templates 模板中應該是 read-only 的,而在 controller 裏應該是 write-only 的。Scope 的目的是引用 model,而不是成爲 model。model 就是咱們定義的 JavaScript 對象。
Scopes 在 AngularJS 中造成必定的層級關係,樹狀結構一定有一個根節點。一般咱們用不到它,因爲差點兒每個 view 都有一個 controller 以及相相應的本身的 scope。
但偶爾有一些數據咱們但願全局應用在整個 app 中,這時咱們可以將數據注入 $rootScope。因爲其它 scope 都會繼承 root scope,因此那些注入的數據對於 ng-show 這類 directive 都是可用的,就像是在本地 $scope 中的變量同樣。
固然,全局變量是邪惡的,你必須很是當心地使用 $rootScope。特別是不要用於代碼,而只用於注入數據。假設你很是但願在 $rootScope 寫一個函數,那最好把它寫到 service 裏,這樣只實用到的時候它纔會被注入,測試起來也方便些。
相反,假設一個函數的功能不過存儲和返回一些數據,就不要把它建立成一個 service。
對於剛開始學習的人,這個也是個大坑啊。做用域變量的繼承是基於javascript原型繼承機制的,在使用涉及到做用域的指令如ng-template,ion-modal等時需要特別注意,相關的查找順序這裏就不細說了。