使用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等时需要特别注意,相关的查找顺序这里就不细说了。