Tag Archives: AngularJS

[repost ]AngularJS vs. EmberJS vs. Backbone.js

original:http://vschart.com/compare/angularjs/vs/emberjs/vs/backbone-js

Actions

AngularJS EmberJS Backbone.js
Category
Preference 56% votes 22% votes 22% votes
Website angularjs.​org emberjs.​com backbonejs.​org
License
Features
Scaffolding No Yes ?
Full text search Yes ? ?
Architecture
Development principles ?
Design pattern
Multilingual
Multilingual content Yes Yes Conditional
System requirements
Operating system
Programming language
Framework ? ?
Database ? ?
More
Description AngularJS is a toolset for building the framework most suited to your application development EmberJS is a framework for creating ambitious web applications. ?
Tag javascript javascript javascript
Extension/Plug-in Yes Yes Yes
Interpreter No ? ?
Database model ? ?
Unicode Yes Yes Yes
Angle viewing ★★★★☆ ? ?
WYSIWYG-Editor No No Yes
Multiple projects Yes Yes Yes
External pages Yes ? ?
User statistics Yes ? Yes
Template language
Target audience
Difficulty level
Application startup time 0.5 ms ? 0.25 ms
Version number 1.1.4 1.5.0 1.1.0
Adobe Flash Support Yes Yes ?
Object-Relational Mapping (ORM) Yes Yes ?
Machine Code Generation No No No
Release Date 2009 ? ?
Documentation level ★★★★☆ ★★★☆☆ ★★★★☆
Multi tasking Yes ? Yes
RESTful Conditional Yes Yes
Code Generation Conditional Yes ?
Dynamic typing Yes ? ?
Value size max. 20 B ? ?
Programming paradigm ?
Comments Yes ? Yes
Scripting language support
Free to use Yes Yes Yes
Backend
Active Yes Yes Yes
Database Connection Pooling No ? No
Separate Service Layer Yes No ?
Community Driven Good Good Good
Wizard Yes No ?
HTML syntax Yes ? ?
Reliability ★★★★☆ ★★★★☆ ★★★★☆
Layout Structure Template Yes Yes ?
API Good Good Good
Compiled language Conditional ? No
XML Aware No ? ?
Client/Server code reuse ?
Cloud platform support ? ?
Admin Generator No ? ?
Library file size 200 kB 64 kB 5.6 kB
Browser support ?
API comprehensibility ★★★★☆ ? ?
Jobs Oportunities ★★★★☆ ★★★★☆ ★★★☆☆
Implementation flexibility 22% votes 44% votes 33% votes
Out-of-the-box functionality 44% votes 28% votes 28% votes
Annotation Support No ? ?
Backup functionality Good ? ?
Query Cache Yes ? ?
Realtime Yes Yes Yes
Community feedback ★★★★☆ ★★★☆☆ ★★★★☆
Ease of use ★★★★☆ ★★★☆☆ ★★★☆☆
Malicious Injection Prevention No ? ?
Openshift Paas Support Yes ? ?
Copy, Cut and Paste Support Yes ? Yes
Free for commercial use Yes Yes Yes
Read preferences Yes ? Yes
Easy of Use Yes ? Yes
Uncompressed Size 493 kB 980 kB 59 kB
Open Source Yes Yes Yes
Value-to-Object Transformation Yes ? ?
Type inference Yes ? ?
Websocket Support Yes Yes ?
Ressource File Processing No ? ?
Lambda Expressions Yes Yes ?
Properties Yes Yes ?
Partial Classes No ? ?
Closures Yes Yes Yes
actors 17ᵗʰ February 2014 ? ?
Perfomance ★★★★☆ ★★★☆☆ ★★★★☆
I like it Yes Yes Yes
Asyncronous JS loading Yes Yes ?
JavaScript Library included Yes Yes ?
Debug Mode Yes Yes Yes
Static Typing No ? No
Cloud-enabled Yes ? ?
Click & Edit in Place / WYSIWYG No ? No
API Test Control No Yes ?
Autocomplete Code Yes ? No
Dependency Injection Yes Yes ?
Personalization Yes ? ?
Supported from Google Yes ? No
Makes you angry No Yes Yes
Easy to Learn Yes No Yes
Extensibility Yes ? ?
Unit Testing Yes Yes ?
Ajax Yes Yes Yes
User management No ? ?
Mobile ready Yes Yes ?
Operating system server ?
Package Manager Yes Yes No
Easy Setup Yes No Yes
Jquery Yes Yes ?
jQuery Support Yes Yes ?
Jetty Yes ? ?
ACL Yes ? ?
Full-Stack No ? No
Clients synchronization No ? ?
Component Oriented Yes Yes ?
Internationalization Yes ? ?
Heroku Support Yes ? ?
Framework specific vocabulary Yes ? ?
Easy to Config Yes Yes ?
Self Server No ? ?
Will get you a job Yes ? ?
Is reliable and secure Yes ? ?
Scalability Yes Yes ?
Supported by Microsoft No ? ?
Official IDE Support No ? ?
Can function without AJAX Yes ? ?
HTML Binding Yes ? ?
Сасай лалка Yes ? ?
wtf mode Yes ? ?
Will make super models fall in love with you No ? ?
MVC Yes ? ?
24/7 support Yes ? ?
Gay Prevention Yes ? ?
Developed for Human Yes Yes ?
piece of crap Yes ? ?
Yes ? ?
Inheritance Yes ? ?
IDE method invoke validation No ? ?
IDE field use validation No ? ?
Bullshit Yes ? ?
Please PB Yes ? ?
dog-vomit style of code Yes ? ?
Revision control ? No Yes
Supported VCS ? ?
Creation Date ? April 2011 13ᵗʰ October 2010
Sexiness ? 75 ?
Makes developer create a slew of inefficient Javascript ? No ?
Multi-user system ? ? Yes
Price rating ? ? ★★★★☆
Duck typing ? ? No
Useless ? ? No
JVM ? ? No
Optional Static Type ? ? No
+ Add row + + +
AngularJS EmberJS Backbone.js

[repost ]AngularJS 开发者最常犯的 10 个错误

original:http://developer.51cto.com/art/201410/453664.htm

AngularJS是如今最受欢迎的JS框架之一,简化开发过程是它的目标之一,这使得它非常适合于元型较小的apps的开发,但也扩展到具有全部特征的客户端应用的开发

介绍

AngularJS是如今最受欢迎的JS框架之一,简化开发过程是它的目标之一,这使得它非常适合于元型较小的apps的开发,但也扩展到具有全部特征的客户端应用的开发。易于开发、较多的特征及较好的效果导致了较多的应用,伴随而来的是一些陷阱。本文列举了AngularJS的一些共同的易于也问题的地方,尤其是在开发一个app的时候。

1. MVC目录结构

AngularJS是一个缺乏较好的term的MVC框架,其models不像backbone.js中那样做为一个框架来定义,但其结构模式仍匹配的较好。当在一个MVC框架中作业时,基于文件类型将文件组合在一起是其共同的要求:

  1. templates/
  2.     _login.html
  3.     _feed.html
  4. app/
  5.     app.js
  6.     controllers/
  7.         LoginController.js
  8.         FeedController.js
  9.     directives/
  10.         FeedEntryDirective.js
  11.     services/
  12.         LoginService.js
  13.         FeedService.js
  14.     filters/
  15.         CapatalizeFilter.js

这样的布局, 尤其是对那些有 Rails 背景的人来说, 看起来挺合理. 可是当 app 变得越来越庞大的时候, 这样的布局结构会导致每次都会打开一堆文件夹. 无论你是用 Sublime, Visual Studio, 还是 Vim with Nerd Tree, 每次都要花上很多时间滑动滚动条浏览这个目录树来查找文件.

如果我们根据每个文件隶属的功能模块来对文件分组, 而不是根据它隶属的层:

  1. app/
  2.     app.js
  3.     Feed/
  4.         _feed.html
  5.         FeedController.js
  6.         FeedEntryDirective.js
  7.         FeedService.js
  8.     Login/
  9.         _login.html
  10.         LoginController.js
  11.         LoginService.js
  12.     Shared/
  13.         CapatalizeFilter.js

那么查找某个功能模块的文件就要容易得多, 自然可以提高开发的速度. 也许把 html 文件跟 js 文件放在混合放在一起做法不是每个人都能认同. 但是起码它省下宝贵的时间.

2、模块分组

一开始就将主模块中所有子模块展示出来是通常的做法。但是开始做一个小应用还好,但是做大了就不好管理了。

  1. var app = angular.module(‘app’,[]);app.service(‘MyService’function(){
  2.     //service code});app.controller(‘MyCtrl’, function($scope, MyService){
  3.     //controller code});

一个比较好的办法是将相似类型的子模块分组:

  1. var services = angular.module(‘services’,[]);services.service(‘MyService’function(){
  2.     //service code});var controllers = angular.module(‘controllers’,[‘services’]);controllers.controller(‘MyCtrl’, function($scope, MyService){
  3.     //controller code});var app = angular.module(‘app’,[‘controllers’, ‘services’]);

这个方法与上面那个方法效果差不多,但是也不很大。运用要分组的思想将使工作更容易。

  1. var sharedServicesModule = angular.module(‘sharedServices’,[]);
  2. sharedServices.service(‘NetworkService’function($http){});
  3. var loginModule = angular.module(‘login’,[‘sharedServices’]);
  4. loginModule.service(‘loginService’function(NetworkService){});
  5. loginModule.controller(‘loginCtrl’function($scope, loginService){});
  6. var app = angular.module(‘app’, [‘sharedServices’‘login’]);

当创建一个大的应用时,所有模块可能不会放在一页里,但是将模块根据类型进行分组将使模块的重用能力更强。

3 依赖注入

依赖注入是AngularJS最棒的模式之一。它使测试变得更加方便,也让它所依赖的对象变的更加清楚明白。AngularJS 对于注入是非常灵活的。一个最简单的方式只需要为模块将依赖的名字传入函数中:

  1. var app = angular.module(‘app’,[]);app.controller(‘MainCtrl’function($scope, $timeout){
  2.     $timeout(function(){
  3.         console.log($scope);
  4.     }, 1000);});

这里,很清楚的是MainCtrl依赖于$scope和$timeout。

直到你准备投入生产并压缩你的代码。使用UglifyJS,上面的例子会变成:

  1. var app=angular.module(“app”,[]);
  2. app.controller(“MainCtrl”,function(e,t){t(function(){console.log(e)},1e3)})

现在AngularJS怎么知道MainCtrl依赖什么?AngularJS提供了一个非常简单的解决方案:把依赖作为一个字符串数组传递,而数组的最后一个元素是一个把所有依赖作为参数的函数。

  1. app.controller(‘MainCtrl’, [‘$scope’‘$timeout’function($scope, $timeout){
  2.     $timeout(function(){
  3.         console.log($scope);
  4.     }, 1000);}]);

接下来在压缩的代码中AngularJS也可以知道如何找到依赖:

  1. app.controller(“MainCtrl”,[“$scope”,“$timeout”,function(e,t){t(function(){console.log(e)},1e3)}])

3.1 全局依赖

通常在写AngularJS应用时会有一个对象作为依赖绑定到全局作用域中。这意味着它在任何AngularJS的代码中都可用,但这打破了依赖注入模型同时带来一些问题,特别是在测试中。

AngularJS把这些全局变量封装到模块中,这样它们可以像标准AngularJS模块一样被注入。

Underscore.js是很棒的库,它把Javascript代码简化成了函数模式,并且它可以被转化成一个模块:

  1. var underscore = angular.module(‘underscore’, []);underscore.factory(‘_’function() {
  2.   return window._; //Underscore must already be loaded on the page});var app = angular.module(‘app’, [‘underscore’]);app.controller(‘MainCtrl’, [‘$scope’, ‘_’, function($scope, _) {
  3.     init = function() {
  4.           _.keys($scope);
  5.       }
  6.       init();}]);

它允许应用继续用AngularJS依赖注入的风格,也让underscore在测试的时候被交换出来。
这或许看上去不重要,像是一个无关紧要的工作,但如果你的代码正在使用use strict(应该使用),那么这就变得有必要了。

4 控制器膨胀

控制器是AngularJS应用中的肉和番茄。它很简单,特别是开始的时候,在控制器中放入过多的逻辑。控制器不应该做任何DOM操作或者有DOM选择器,这应该由使用ngModel的指令(directives)做的事。同样地,业务逻辑应该在服务(services)中,而不是 控制器。

数据也应该被存在服务(services)中,除非它已经和$scope关联。服务(services)是留存于整个应用生命周期的个体,同时控制器在应用各阶段间都是暂态的。如果数据被存在控制器中,那么当它被重新实例化的时候,就需要从其他地方抓取。即使数据被存储在localStorage中,获取数据也要比从Javascript变量中获取要慢几个数量级。

AngularJS在遵从简单责任原则(SRP)时工作地最好。如果控制器是视图和模型的协调者,那么它拥有的逻辑应该被最小化。这将使得测试变的更加简单。

5 Service 和 Factory的区别

几乎每一个刚接触AngularJS的开发者,都会对这两个东西产生困惑。 虽然它们(几乎)实现了同样的效果,但真的不是语法糖。

这里是它们在 AngularJS 源码中的定义:

  1. function factory(name, factoryFn) { return provider(name, { $get: factoryFn }); }
  2. function service(name, constructor) {
  3.     return factory(name, [‘$injector’function($injector) {
  4.       return $injector.instantiate(constructor);
  5.     }]);
  6.   }

从源码上看显然 service 函数只是调用 factory 函数,然后 factory 函数再调用 provider 函数。事实上,value、constant和decorator 也是 AngularJS 提供的对 provider 的封装,但对它们使用场景不会有这种困惑,并且文档描述也非常清晰。

那么Service 仅仅是单纯的调用了一次 factory 函数吗? 重点在 $injector.instantiate 中; 在这个函数里service会接收一个由$injector 使用new关键字去实例化的一个构造器对象。(原文:with in this function $injector creates a new instance of the service’s constructor function.)

下面是完成同样功能的一个service和一个factory。

  1. var app = angular.module(‘app’,[]);
  2. app.service(‘helloWorldService’function(){
  3.     this.hello = function() {
  4.         return “Hello World”;
  5.     };});
  6. app.factory(‘helloWorldFactory’function(){
  7.     return {
  8.         hello: function() {
  9.             return “Hello World”;
  10.         }
  11.     }});

当 helloWorldService 或者 helloWorldFactory中的任何一个注入到controller里面, 他们都有一个返回字符串”Hello World”的名称为 hello方法。 这个service 的构造函数只在声明时被实例化一次,并且在这个 factory 对象每次被注入时各种互相引用, 但这个 factory还是只是被实例化了一次。 所有的 providers 都是单例的。

既然都完成同样的功能,为什么会有这两种格式存在?factory比service略微更灵活一些,因为它们可以使用new关键字返回函数(原文:Factories offer slightly more flexibility than services because they can return functions which can then be new’d)。 在其他地方,从面向对象编程的工厂模式来说。 一个factory可以是一个用于创建其他对象的对象。

  1. app.factory(‘helloFactory’function() {
  2.     return function(name) {
  3.         this.name = name;
  4.         this.hello = function() {
  5.             return “Hello “ + this.name;
  6.         };
  7.     };
  8. });

这里有一个使用了前面提到的那个service和两个factory的controller 的例子。需要注意的是 helloFactory 返回的是一个函数,变量name的值是在对象使用new关键字的时候设置。

  1. app.controller(‘helloCtrl’function($scope, helloWorldService, helloWorldFactory, helloFactory) {
  2.     init = function() {
  3.       helloWorldService.hello(); //’Hello World’
  4.       helloWorldFactory.hello(); //’Hello World’
  5.       new helloFactory(‘Readers’).hello() //’Hello Readers’
  6.     }
  7.     init();
  8. });

在刚入门时候最好只使用services.

Factory更加适用于当你在设计一个需要私有方法的类的时候使用:

  1. app.factory(‘privateFactory’function(){
  2.     var privateFunc = function(name) {
  3.         return name.split(“”).reverse().join(“”); //reverses the name
  4.     };
  5.     return {
  6.         hello: function(name){
  7.           return “Hello “ + privateFunc(name);
  8.         }
  9.     };});

在这个例子中privateFactory含有一个不能被外部访问的私有privateFunc函数。这种使用方式services也可以实现,但是使用Factory代码结构显得更加清晰。

6 不会使用 Batarang

Batarang 是用于开发和调试 AngularJS 应用的一个优秀的chrome浏览器插件。

Batarang 提供了模型浏览,可以查看Angular内部哪些模型已经绑定到作用域(scopes )。可以用于需要在运行时查看指令中的隔离作用域(isolate scopes)绑定的值。

Batarang 还提供了依赖关系图。 对于引入一个未测试的代码库, 这个工具可以快速确定哪些services应该得到更多的关注。

最后, Batarang提供了性能分析。 AngularJS 虽然是高性能开箱即用, 但是随着应用自定义指令和复杂的业务逻辑的增长,有时候会感到页面不够流畅。使用 Batarang 的性能分析工具可以很方便的查看哪些functions 在digest 周期中占用了更多的时间。这个工具还可以显示出整个监控树(full watch tree),当页面有太多的监控器(watch)时,这个功能就显得有用了。

7 太多的watchers

正如上文中提到的,在外部AngularJS是很不错的。因为在一个循环消化中需要进行dirty检查,一旦watcher的数目超过2,000,循环会出现很明显的问题。(2,000仅是一个参考数,在1.3版本中AngularJS对循环消化有更为严谨的控制,关于这个Aaron Graye有较为详细的叙述)

这个IIFE(快速响应函数)可输出当前本页中的watcher的数目,只需将其复制到console即可查看详情。IIFE的来源跟Jared关于StackOverflow的回答是类似的。

  1. (function () {
  2.     var root = $(document.getElementsByTagName(‘body’));
  3.     var watchers = [];
  4.     var f = function (element) {
  5.         if (element.data().hasOwnProperty(‘$scope’)) {
  6.             angular.forEach(element.data().$scope.$$watchers, function (watcher) {
  7.                 watchers.push(watcher);
  8.             });
  9.         }
  10.         angular.forEach(element.children(), function (childElement) {
  11.             f($(childElement));
  12.         });
  13.     };
  14.     f(root);
  15.     console.log(watchers.length);})();

使用这个,可以从Batarang的效率方面来决定watcher及watch tree的数目,可以看到在哪些地方顾在或哪些地方没有改变的数据有一个watch。

当有数据没有变化时,但在Angular中又想让它成为模板,可以考虑使用bindonce.Bindonce在Angular中仅是一个可能使用模板的指令,但没有增加watch的数目。

8 审视$scope

Javascript的基于原型的继承和基于类的继承在一些细微的方面是不同的。通常这不是问题,但是差别往往会在使用$scope时出现。在AngularJS中每一个$scope都从它的父$scope继承过来,最高层是$rootScope。($scope在指令中表现的有些不同,指令中的隔离作用域仅继承那些显式声明的属性。)

从父级那里分享数据对于原型继承来说并不重要。不过如果不小心的话,会遮蔽父级$scope的属性。

我们想在导航栏上呈现一个用户名,然后进入登陆表单。

  1. <div ng-controller=“navCtrl”>
  2.    <span>{{user}}</span>
  3.    <div ng-controller=“loginCtrl”>
  4.         <span>{{user}}</span>
  5.         <input ng-model=“user”></input>
  6.    </div></div>

考你下:当用户在设置了ngModel的文本框中输入了值,哪个模板会被更新?是navCtrl,loginCtrl还是两者?

如果你选loginCtrl,那么你可能对原型继承的机理比较了解了。当寻找字面值时,原型链并没有被涉及。如果navCtrl要被更新的话,那么查找原型链是必要的。当一个值时对象的时候就会发生这些。(记住在Javascript中,函数、数组合对象都算作对象)

所以想要获得期望的效果就需要在navCtrl上创建一个对象可以被loginCtrl引用。

  1. <div ng-controller=“navCtrl”>
  2.    <span>{{user.name}}</span>
  3.    <div ng-controller=“loginCtrl”>
  4.         <span>{{user.name}}</span>
  5.         <input ng-model=“user.name”></input>
  6.    </div></div>

现在既然user是一个对象了,原型链会被考虑进去,navCtrl的模板和$scope也会随着loginCtrl更新。

这可能看上去像一个设计好的例子,但当涉及到像ngRepeat那样会创建子$scope的时候问题就会出现。

9 手工测试

虽然测试驱动开发可能不是每一个开发者都喜欢的开发方式,不过每次开发者去检查他们的代码是否工作或开始砸东西时,他们正在做手工测试。

没有理由不去测试一个AngularJS应用。AngularJS从一开始就是被设计地易于测试的。依赖注入和ngMock模块就是证据。核心团队开发了一些工具来讲测试带到另一个级别。

9.1 Protractor

单元测试是一组测试集的基本元素,但随着应用复杂性的提高,集成测试会引出更多实际问题。幸运地是AngularJS核心团队提供了必要的工具。

“我们构建了Protractor,一个端对端的测试运行工具,模拟用户交互,帮助你验证你的Angular应用的运行状况。”

Protractor使用Jasmine测试框架来定义测试。Protractor为不同的页面交互提供一套健壮的API。

有其他的端对端工具,不过Protractor有着自己的优势,它知道怎么和AngularJS的代码一起运行,特别是面临$digest循环的时候。

9.2 Karma

一旦使用Protractor写好了集成测试,测试需要被运行起来。等待测试运行特别是集成测试,会让开发者感到沮丧。AngularJS核心团队也感到了这个痛苦并开发了Karma

Karma是一个Javascript测试运行工具,可以帮助你关闭反馈循环。Karma可以在特定的文件被修改时运行测试,它也可以在不同的浏览器上并行测试。不同的设备可以指向Karma服务器来覆盖实际场景。

10 jQuery的使用

jQuery 是个很不错的类库. 它将跨平台开发标准化. 在现代网页开发中具有很重要的地位. 虽然 jQuery 拥有许多强大的功能. 但是他的设计理念却与 AngularJS 大相径庭.

AngularJS 是用来开发应用框架的; jQuery 则是一个用来简化 HTML 文档对象遍历和操作, 事件处理, 动画以及 Ajax 使用的类库而已. 这是它们俩在本质上的区别. AngularJS 侧重点在于应用的架构, 而非仅仅是补充 HTML 网页的功能.

如文档所述 AngularJS 可以让你根据应用的需要对 HTML 进一步扩展. 所以, 如果想要深入的了解 AngularJS 应用开发, 就不应该再继续抱着 jQuery 的大腿. jQuery 只会把程序员的思维方式限制在现有的 HTML 标准里头.

DOM操作应该出现在指令中,但这并不意味着一定要使用jQuery包装集。在使用jQuery前要考虑到一些功能AngularJS已经提供了。指令建立于相互之间,并可以创建有用的工具。

总有一天,使用jQuery库是必要的,不过从一开始就引入它无疑是一个错误。

总结

AngularJS是一个很不错的框架,并且和它的社区一起发展着。符合习惯的AngularJS仍旧是一个正在发展的概念,但希望以上这些对于规划一个AngularJS应用时会出现的陷阱希望可以被避免。

【编辑推荐】

[repost ]使用 AngularJS 开发一个大规模的单页应用(SPA)

original:http://developer.51cto.com/art/201409/450653.htm

下载源代码

介绍

(SPA)这样一个名字里面蕴含着什么呢? 如果你是经典的Seinfeld电视秀的粉丝,那么你一定知道Donna Chang这个名字。Jerry跟Donna见面,Donna其实不是华人,但是却因在谈论其对中国的固有印象比如在针灸上的兴趣,以及偶然的一次单词发音带上了点儿中文口音,她将自己末尾的名字缩成了Chang Donna 在电话上同George的母亲交谈,(通过引用孔子)给她提了些建议。当George向自己的父母介绍Donna是,George的母亲意识到Donna并不是华人,因此并没有接受Donna的建议.

单页面引用 (SPA), 被定义成一个目的在于提供一种接近桌面应用程序的流畅用户体验单web页面应用程序,或者说网站. 在一个SPA中, 所有必需的代码 – HTML, JavaScript, 以及 CSS – 都是在单页面加载的时候获取,或者相关的资源被动态的加载并按需添加到页面中, 这常常是在响应用户动作的时候发生的. 尽管现代的Web技术(比如那些在HTML5中引入的技术)提供了应用程序中各自独立的逻辑页面相互感知和导航的能力,页面却不会在过程中重新加载任何端点,或者将控制转到另外一个页面. 同单页面应用程序的交互常常设计到同位于后台的web服务器的动态交互.

那么拿这项技术同 ASP.NET 的母版页Master Pages相比呢? 诚然 ASP.NET 的母版页让你可以为自己应用程序里的页面创建一个一直的布局。一个单独的母版页就可以定义好你想要在整个应用程序中的所有页面(或者一组页面)上应用的外观和标准动作. 然后你就可以再来创建你想要展示的内容各自独立页面. 当用户发起对内容页面的请求时,它们会将来自母版页的布局和来自内容页面的内容混合到一起,产生输出.

当你深入研究SPA和ASP.NET母版页实现这两者之间的不同时,你就开始会意识到它们之间相同的地方多于不同的地方——那就是SPA可以看做是一个简单的装着内容页面的外壳页面,就像是一个母版页, 只是SPA中的外壳页面不能像母版页那样根据每一个新的页面请求来重新装载和执行.

也许“单页面应用”是个不幸运的名字(像唐娜`程一样),让你相信这个技术不适合开发需要拓展到企业级,可能 包含上百页面以及数千用户的Web应用。

本文的目标是基于单页面应用程序开发出拥有数百页的内容,包括认证,授权,会话状态等功能,可以支持上千个用户的企业级应用。

AngularJS – 概述

本文的样例包含的功能有创建/跟新用户账号,创建/更新客户和产品。而且,它还允许用户针对所有信息执行查询,创建和跟新销售订单。为了实现这些功能,该样例将会基于AngularJS来开发。 AngularJS 是一个由Google和AngularJS社区的开发人员维护的开源的Web应用框架。

AngularJS仅需HTML,CSS和JavaScript就可在客户端创建单页面应用。它的目标是是开发和测试更容易,增强MVC Web应用的性能。

这个库读取HTML中包含的其他定制的标签属性;然后服从这个定制的属性的指令,把页面的I/O结合到有标准JavaScript变量生成的模块中。这些JavaScript标准变量的值可以手动设置,或者从静态或动态的JSON数据源中获取。

AngularJS使用入门 – 外壳页面,模块和路由

你首先要做的一件事情就是讲AngularJS框架下载到你的项目中,你可以从 https://angularjs.org 获得框架. 本文的示例程序是使用MS Visual Studio Web Express 2013 Edition开发的,因此我是使用如下的命令从一个Nuget包安装AngularJS的:

Install-Package AngularJS -Version 1.2.21

Nuget包管理控制台上. 为了保持简单和灵活性,我创建了一个空的 Visual Studio web 应用程序项目,并将Microsoft Web API 2库选进了核心引用. 这个应用程序将使用Web API 2 库来实现 RESTful API 的服务器端请求.

现在当你要使用AngularJS创建一个SPA应用程序是,首先要做的两件事情就是设置一个外壳页面,以及用于获取内容页面的路由表. 开始的时候,外壳页面只需要一个队AngularJS JavaScript库的引用,还有一个ng-view,来告诉AngularJS内容页面需要在外壳页面的那个地方被渲染.

  1. <!DOCTYPE html>
  2. <html lang=“en”>
  3. <head>
  4. <title>AngularJS Shell Page example</title>
  5. </head>
  6. <body>
  7. <div>
  8. <ul>
  9. <li><a href=“#Customers/AddNewCustomer”>Add New Customer</a></li>
  10. <li><a href=“#Customers/CustomerInquiry”>Show Customers</a></li>
  11. </ul>
  12. </div>
  13. <!– ng-view directive to tell AngularJS where to inject content pages –>
  14. <div ng-view></div>
  15. <script src=“http://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js”></script>
  16. <script src=“app.js”></script>
  17. </body>
  18. </html>

在上面的外壳页面示例中,几个链接呗映射到了AngularJS的路由。div标签上的ng-view指令是一个能将选定路由的被渲染内容页面包含到外壳页面来补充AngularJS的$route服务的指令. 每次当目前的路由变化时,包含的视图也会根据$route服务的配置随之改变. 比如,当用户选择了 “Add New Customer” 链接,AngularJS 就会在ng-view所在的div里面渲染用于添加一个新顾客的内容 . 被渲染的内容是一个HTML片段.

下来的app.js文件同样也被外壳页面引用了。这个文件里的JavaScript将会为应用程序创建AngularJS模块。此外,应用程序所有的路由配置也会在这个文件中定义。你可以把一个AngularJS模块想象成封装你应用程序不同部分的容器。大多数的应用程序都会有一个主方法,用来初始化应用程序的不同部分,并将它们联系起来。AngularJS应用程序却没有一个主方法,而是让模块声明性的指定应用程序如何启动和配置. 本文的示例程序将只会有一个AngularJS模块,虽然应用程序中存在几个明显不同的部分(顾客,产品,订单和用户).

现在,app.js的主要目的就是如下所示,用来设置AngularJS的路由。AngularJS的$routeProvider服务会接受  when() 方法,它将为一个Uri匹配一个模式. 当发现一次匹配时,独立页面的HTML内容会跟随相关内容的控制器文件一同被加载到外壳页面中. 控制器文件就简单的只是一个JavaScript文件,它将获得带有某个特定路由请求内容的引用.

  1. //Define an angular module for our app
  2. var sampleApp = angular.module(&apos;sampleApp&apos;, []);
  3. //Define Routing for the application
  4. sampleApp.config([&apos;$routeProvider&apos;,
  5.     function($routeProvider) {
  6.         $routeProvider.
  7.             when(&apos;/Customers/AddNewCustomer&apos;, {
  8.                 templateUrl: &apos;Customers/AddNewCustomer.html&apos;,
  9.                 controller: &apos;AddNewCustomerController&apos;
  10.             }).
  11.             when(&apos;/Customers/CustomerInquiry&apos;, {
  12.                 templateUrl: &apos;Customers/CustomerInquiry.html&apos;,
  13.                 controller: &apos;CustomerInquiryController&apos;
  14.             }).
  15.             otherwise({
  16.                 redirectTo: &apos;/Customers/AddNewCustomer&apos;
  17.             });
  18. }]);

AngularJS 的控制器

AngularJS 控制器无非就是一个原生的JavaScript函数,只是被绑定到了一个特定的范围而已。控制器用来将逻辑添加到你的视图。视图就是HTML页面。这些页面只是做简单的数据展示工作,我们会使用双向数据绑定来将数据绑定到这些HTML页面上. 将模型(也就是数据)同数据粘合起来基本山就是控制器的职责了.

  1. <div ng-controller=“customerController”>
  2. <input ng-model=“FirstName” type=“text” style=“width: 300px” />
  3. <input ng-model=“LastName” type=“text” style=“width: 300px” />
  4. <div>
  5. <button class=“btn btn-primary btn-large” ng-click=“createCustomer()”/>Create</button>

对于上面的AddCustomer模板,ng-controller指令将会引用JavaScript函数customerController,这个控制会执行所有的数据绑定以及针对该视图的JavaScript函数.

  1. function customerController($scope)
  2. {
  3.     $scope.FirstName = “William”;
  4.     $scope.LastName = “Gates”;
  5.     $scope.createCustomer = function () {
  6.         var customer = $scope.createCustomerObject();
  7.         customerService.createCustomer(customer,
  8.                         $scope.createCustomerCompleted,
  9.                         $scope.createCustomerError);
  10.     }
  11. }

开箱即用 – 可扩展性问题

当我为本文开发这个实力程序时,首当其冲的两个扩展性问题在应用单页面应用程序时变得明显起来。其实一个开箱即用,AngularJS需要应用程序的外壳页面中所有的JavaScript文件和控制器在启动中伴随应用程序的启动被引入和下载. 对于一个大型的应用程序而言,可能会有上百个JavaScript文件,这样情况看上去就会不怎么理想。我遇到的另外一个问题就是AngularJS的路由表。我找到的所有示例都有针对所有内容的所有路由的硬编码。而我想要的确不是一个在路由表里包含上百项路由记录的方案.

英文原文:Developing a Large Scale Application with a Single Page Application (SPA) using AngularJS

译文链接:http://www.oschina.net/translate/developing-a-large-scale-application-with-a-single

【编辑推荐】

[repost ]How do I “think in AngularJS” if I have a jQuery background?

original:http://stackoverflow.com/questions/14994391/how-do-i-think-in-angularjs-if-i-have-a-jquery-background

1. Don’t design your page, and then change it with DOMmanipulations

In jQuery, you design a page, and then you make it dynamic. This is because jQuery was designed for augmentation and has grown incredibly from that simple premise.

But in AngularJS, you must start from the ground up with your architecture in mind. Instead of starting by thinking “I have this piece of the DOM and I want to make it do X”, you have to start with what you want to accomplish, then go about designing your application, and then finally go about designing your view.

2. Don’t augment jQuery with AngularJS

Similarly, don’t start with the idea that jQuery does X, Y, and Z, so I’ll just add AngularJS on top of that for models and controllers. This is really tempting when you’re just starting out, which is why I always recommend that new AngularJS developers don’t use jQuery at all, at least until they get used to doing things the “Angular Way”.

I’ve seen many developers here and on the mailing list create these elaborate solutions with jQuery plugins of 150 or 200 lines of code that they then glue into AngularJS with a collection of callbacks and$applys that are confusing and convoluted; but they eventually get it working! The problem is that inmost cases that jQuery plugin could be rewritten in AngularJS in a fraction of the code, where suddenly everything becomes comprehensible and straightforward.

The bottom line is this: when solutioning, first “think in AngularJS”; if you can’t think of a solution, ask the community; if after all of that there is no easy solution, then feel free to reach for the jQuery. But don’t let jQuery become a crutch or you’ll never master AngularJS.

3. Always think in terms of architecture

First know that single-page applications are applications. They’re not webpages. So we need to think like a server-side developer in addition to thinking like a client-side developer. We have to think about how to divide our application into individual, extensible, testable components.

So then how do you do that? How do you “think in AngularJS”? Here are some general principles, contrasted with jQuery.

The view is the “official record”

In jQuery, we programmatically change the view. We could have a dropdown menu defined as a ul like so:

<ul class="main-menu">
    <li class="active">
        <a href="#/home">Home</a>
    </li>
    <li>
        <a href="#/menu1">Menu 1</a>
        <ul>
            <li><a href="#/sm1">Submenu 1</a></li>
            <li><a href="#/sm2">Submenu 2</a></li>
            <li><a href="#/sm3">Submenu 3</a></li>
        </ul>
    </li>
    <li>
        <a href="#/home">Menu 2</a>
    </li>
</ul>

In jQuery, in our application logic, we would activate it with something like:

$('.main-menu').dropdownMenu();

When we just look at the view, it’s not immediately obvious that there is any functionality here. For small applications, that’s fine. But for non-trivial applications, things quickly get confusing and hard to maintain.

In AngularJS, though, the view is the official record of view-based functionality. Our ul declaration would look like this instead:

<ul class="main-menu" dropdown-menu>
    ...
</ul>

These two do the same thing, but in the AngularJS version anyone looking at the template knows what’s supposed to happen. Whenever a new member of the development team comes on board, she can look at this and then know that there is a directive called dropdownMenu operating on it; she doesn’t need to intuit the right answer or sift through any code. The view told us what was supposed to happen. Much cleaner.

Developers new to AngularJS often ask a question like: how do I find all links of a specific kind and add a directive onto them. The developer is always flabbergasted when we reply: you don’t. But the reason you don’t do that is that this is like half-jQuery, half-AngularJS, and no good. The problem here is that the developer is trying to “do jQuery” in the context of AngularJS. That’s never going to work well. The view isthe official record. Outside of a directive (more on this below), you never, ever, never change the DOM. And directives are applied in the view, so intent is clear.

Remember: don’t design, and then mark up. You must architect, and then design.

Data binding

This is by far one of the most awesome features of AngularJS and cuts out a lot of the need to do the kinds of DOM manipulations I mentioned in the previous section. AngularJS will automatically update your view so you don’t have to! In jQuery, we respond to events and then update content. Something like:

$.ajax({
  url: '/myEndpoint.json',
  success: function ( data, status ) {
    $('ul#log').append('<li>Data Received!</li>');
  }
});

For a view that looks like this:

<ul class="messages" id="log">
</ul>

Apart from mixing concerns, we also have the same problems of signifying intent that I mentioned before. But more importantly, we had to manually reference and update a DOM node. And if we want to delete a log entry, we have to code against the DOM for that too. How do we test the logic apart from the DOM? And what if we want to change the presentation?

This a little messy and a trifle frail. But in AngularJS, we can do this:

$http( '/myEndpoint.json' ).then( function ( response ) {
    $scope.log.push( { msg: 'Data Received!' } );
});

And our view can look like this:

<ul class="messages">
    <li ng-repeat="entry in log">{{ entry.msg }}</li>
</ul>

But for that matter, our view could look like this:

<div class="messages">
    <div class="alert" ng-repeat="entry in log">
        {{ entry.msg }}
    </div>
</div>

And now instead of using an unordered list, we’re using Bootstrap alert boxes. And we never had to change the controller code! But more importantly, no matter where or how the log gets updated, the view will change too. Automatically. Neat!

Though I didn’t show it here, the data binding is two-way. So those log messages could also be editable in the view just by doing this: <input ng-model="entry.msg" />. And there was much rejoicing.

Distinct model layer

In jQuery, the DOM is kind of like the model. But in AngularJS, we have a separate model layer that we can manage in any way we want, completely independently from the view. This helps for the above data binding, maintains separation of concerns, and introduces far greater testability. Other answers mentioned this point, so I’ll just leave it at that.

Separation of concerns

And all of the above tie into this over-arching theme: keep your concerns separate. Your view acts as the official record of what is supposed to happen (for the most part); your model represents your data; you have a service layer to perform reusable tasks; you do DOM manipulation and augment your view with directives; and you glue it all together with controllers. This was also mentioned in other answers, and the only thing I would add pertains to testability, which I discuss in another section below.

Dependency injection

To help us out with separation of concerns is dependency injection (DI). If you come from a server-side language (from Java to PHP) you’re probably familiar with this concept already, but if you’re a client-side guy coming from jQuery, this concept can seem anything from silly to superfluous to hipster. But it’s not. :-)

From a broad perspective, DI means that you can declare components very freely and then from any other component, just ask for an instance of it and it will be granted. You don’t have to know about loading order, or file locations, or anything like that. The power may not immediately be visible, but I’ll provide just one (common) example: testing.

Let’s say in our application, we require a service that implements server-side storage through a RESTAPI and, depending on application state, local storage as well. When running tests on our controllers, we don’t want to have to communicate with the server – we’re testing the controller, after all. We can just add a mock service of the same name as our original component, and the injector will ensure that our controller gets the fake one automatically – our controller doesn’t and needn’t know the difference.

Speaking of testing…

4. Test-driven development – always

This is really part of section 3 on architecture, but it’s so important that I’m putting it as its own top-level section.

Out of all of the many jQuery plugins you’ve seen, used, or written, how many of them had an accompanying test suite? Not very many because jQuery isn’t very amenable to that. But AngularJS is.

In jQuery, the only way to test is often to create the component independently with a sample/demo page against which our tests can perform DOM manipulation. So then we have to develop a component separately and then integrate it into our application. How inconvenient! So much of the time, when developing with jQuery, we opt for iterative instead of test-driven development. And who could blame us?

But because we have separation of concerns, we can do test-driven development iteratively in AngularJS! For example, let’s say we want a super-simple directive to indicate in our menu what our current route is. We can declare what we want in our view:

<a href="/hello" when-active>Hello</a>

Okay, now we can write a test:

it( 'should add "active" when the route changes', inject(function() {
    var elm = $compile( '<a href="/hello" when-active>Hello</a>' )( $scope );

    $location.path('/not-matching');
    expect( elm.hasClass('active') ).toBeFalsey();

    $location.path( '/hello' );
    expect( elm.hasClass('active') ).toBeTruthy();
}));

We run our test and confirm that it fails. So now we can write our directive:

.directive( 'whenActive', function ( $location ) {
    return {
        scope: true,
        link: function ( scope, element, attrs ) {
            scope.$on( '$routeChangeSuccess', function () {
                if ( $location.path() == element.attr( 'href' ) ) {
                    element.addClass( 'active' );
                }
                else {
                    element.removeClass( 'active' );
                }
            });
        }
    };
});

Our test now passes and our menu performs as requested. Our development is both iterative and test-driven. Wicked-cool.

5. Conceptually, directives are not packaged jQuery

You’ll often hear “only do DOM manipulation in a directive”. This is a necessity. Treat it with due deference!

But let’s dive a little deeper…

Some directives just decorate what’s already in the view (think ngClass) and therefore sometimes do DOM manipulation straight away and then are basically done. But if a directive is like a “widget” and has a template, it should also respect separation of concerns. That is, the template too should remain largely independent from its implementation in the link and controller functions.

AngularJS comes with an entire set of tools to make this very easy; with ngClass we can dynamically update the class; ngBind allows two-way data binding; ngShow and ngHide programmatically show or hide an element; and many more – including the ones we write ourselves. In other words, we can do all kinds of awesomeness without DOM manipulation. The less DOM manipulation, the easier directives are to test, the easier they are to style, the easier they are to change in the future, and the more re-usable and distributable they are.

I see lots of developers new to AngularJS using directives as the place to throw a bunch of jQuery. In other words, they think “since I can’t do DOM manipulation in the controller, I’ll take that code put it in a directive”. While that certainly is much better, it’s often still wrong.

Think of the logger we programmed in section 3. Even if we put that in a directive, we still want to do it the “Angular Way”. It still doesn’t take any DOM manipulation! There are lots of times when DOM manipulation is necessary, but it’s a lot rarer than you think! Before doing DOM manipulation anywherein your application, ask yourself if you really need to. There might be a better way.

Here’s a quick example that shows the pattern I see most frequently. We want a toggleable button. (Note: this example is a little contrived and a skosh verbose to represent more complicated cases that are solved in exactly the same way.)

.directive( 'myDirective', function () {
    return {
        template: '<a>Toggle me!</a>',
        link: function ( scope, element, attrs ) {
            var on = false;

            $(element).click( function () {
                if ( on ) {
                    $(element).removeClass( 'active' );
                }
                else {
                    $(element).addClass( 'active' );
                }

                on = !on;
            });
        }
    };
});

There are a few things wrong with this. First, jQuery was never necessary. There’s nothing we did here that needed jQuery at all! Second, even if we already have jQuery on our page, there’s no reason to use it here; we can simply use angular.element and our component will still work when dropped into a project that doesn’t have jQuery. Third, even assuming jQuery was required for this directive to work, jqLite (angular.element) will always use jQuery if it was loaded! So we needn’t use the $ – we can just use angular.element. Fourth, closely related to the third, is that jqLite elements needn’t be wrapped in $ – the element that is passed to the link function would already be a jQuery element! And fifth, which we’ve mentioned in previous sections, why are we mixing template stuff into our logic?

This directive can be rewritten (even for very complicated cases!) much more simply like so:

.directive( 'myDirective', function () {
    return {
        scope: true,
        template: '<a ng-class="{active: on}" ng-click="toggle()">Toggle me!</a>',
        link: function ( scope, element, attrs ) {
            scope.on = false;

            scope.toggle = function () {
                scope.on = !scope.on;
            };
        }
    };
});

Again, the template stuff is in the template, so you (or your users) can easily swap it out for one that meets any style necessary, and the logic never had to be touched. Reusability – boom!

And there are still all those other benefits, like testing – it’s easy! No matter what’s in the template, the directive’s internal API is never touched, so refactoring is easy. You can change the template as much as you want without touching the directive. And no matter what you change, your tests still pass.

w00t!

So if directives aren’t just collections of jQuery-like functions, what are they? Directives are actuallyextensions of HTML. If HTML doesn’t do something you need it to do, you write a directive to do it for you, and then use it just as if it was part of HTML.

Put another way, if AngularJS doesn’t do something out of the box, think how the team would accomplish it to fit right in with ngClickngClass, et al.

Summary

Don’t even use jQuery. Don’t even include it. It will hold you back. And when you come to a problem that you think you know how to solve in jQuery already, before you reach for the $, try to think about how to do it within the confines the AngularJS. If you don’t know, ask! 19 times out of 20, the best way to do it doesn’t need jQuery and to try to solve it with jQuery results in more work for you.

———————->

Imperative → declarative

In jQuery, selectors are used to find DOM elements and then bind/register event handlers to them. When an event triggers, that (imperative) code executes to update/change the DOM.

In AngularJS, you want to think about views rather than DOM elements. Views are (declarative) HTML that contain AngularJS directives. Directives set up the event handlers behind the scenes for us and give us dynamic databinding. Selectors are rarely used, so the need for IDs (and some types of classes) is greatly diminished. Views are tied to models (via scopes). Views are a projection of the model. Events change models (that is, data, scope properties), and the views that project those models update “automatically.”

In AngularJS, think about models, rather than jQuery-selected DOM elements that hold your data. Think about views as projections of those models, rather than registering callbacks to manipulate what the user sees.

Separation of concerns

jQuery employs unobtrusive JavaScript – behavior (JavaScript) is separated from the structure (HTML).

AngularJS uses controllers and directives (each of which can have their own controller, and/or compile and linking functions) to remove behavior from the view/structure (HTML). Angular also has servicesand filters to help separate/organize your application.

See also http://stackoverflow.com/a/14346528/215945

Application design

One approach to designing an AngularJS application:

  1. Think about your models. Create services or your own JavaScript objects for those models.
  2. Think about how you want to present your models — your views. Create HTML templates for each view, using the necessary directives to get dynamic databinding.
  3. Attach a controller to each view (using ng-view and routing, or ng-controller). Have the controller find/get only whatever model data the view needs to do its job. Make controllers as thin as possible.

Prototypal inheritance

You can do a lot with jQuery without knowing about how JavaScript prototypal inheritance works. When developing AngularJS applications, you will avoid some common pitfalls if you have a good understanding of JavaScript inheritance. Recommended reading: What are the nuances of scope prototypal / prototypical inheritance in AngularJS?

—————->

Can you describe the paradigm shift that is necessary?

Imperative vs Declarative

With jQuery you tell the DOM what needs to happen, step by step. With AngularJS you describe what results you want but not how to do it. More on this here. Also, check out Mark Rajcok’s answer.

How do I architect and design client-side web apps differently?

AngularJS is an entire client-side framework that uses the MVC pattern (check out their graphical representation). It greatly focuses on separation of concerns.

What is the biggest difference? What should I stop doing/using; what should I start doing/using instead?

jQuery is a library

AngularJS is a beautiful client-side framework, highly testable, that combines tons of cool stuff such as MVC, dependency injection, data binding and much more.

It focuses on separation of concerns and testing (unit testing and end-to-end testing), which facilitates test-driven development.

The best way to start is going through their awesome tutorial. You can go through the steps in a couple of hours; however, in case you want to master the concepts behind the scenes, they include a myriad of reference for further reading.

Are there any server-side considerations/restrictions?

You may use it on existing applications where you are already using pure jQuery. However, if you want to fully take advantage of the AngularJS features you may consider coding the server side using a RESTfulapproach.

Doing so will allow you to leverage their resource factory, which creates an abstraction of your server side RESTful API and makes server-side calls (get, save, delete, etc.) incredibly easy.

[repost ]Picking a Javascript framework isn’t about preference. It’s about what best fits your project.

original:http://readwrite.com/2014/02/06/angular-backbone-ember-best-javascript-framework-for-you

You’ve got the perfect idea for a JavaScript-based application or website. Now all you have to do is pick the right open source framework.

If you’re reading this, I’m assuming that you’re a beginner or novice developer, or else you’re hiring somebody else to develop your idea for you. But while you’ve probably heard the words Angular, Ember, and Backbone, you might not know what they are, or why they help web development.

Developers use JavaScript for lots of different Web applications because it can really make the client side—that is, the user’s side—look and work beautifully. But while it may not look old, the JavaScript language has been around for nearly 20 years, and it has the variety and vastness to prove it. If you keep adding more and more (see: redundant) code to make it work in multiple browsers and use cases, it can quickly become a big, confusing mess.

Enter JavaScript frameworks. Frameworks like Angular, Backbone, and Ember bring structure to your JavaScript code and keep it organized. They’re all open source, so they’re constantly being improved by the community. They also save you time because they’re each built on top of JQuery, a powerful library that makes some of JavaScript’s tricker operations easier to perform and more readable.

Here’s an example of how referencing JQuery might enhance the coding process: Instead of writing the tedious lines of code to generate a table, you could insert a reference to a JavaScript library that generates the table with just one line of code. Even better, these shortcuts don’t just help you work faster, they make your code work in multiple environments, too.

However, picking your JavaScript framework isn’t like picking between three different-colored T-shirts. In fact, it’s more like choosing between three completely different articles of clothing: Each piece helps to cover you, but they all function and look very differently. Carrying the metaphor even further, you wouldn’t wear a bathing suit in a cold climate, or a winter coat at the beach. Likewise, different JavaScript frameworks are better for different types of applications.

Here is a rundown of each of the three hottest frameworks, and what they’re best for:

AngularJS

Initially released in 2009, AngularJS is the oldest of the three frameworks. Probably as a result, it also has the largest community.

In 2013, Angular had the fourth-largest number of contributors and third-largest number of stars (kind of like Facebook “Likes”)on GitHub. On Built With AngularJS, you can check out all of the applications currently being developed with Angular.

Some of the most well-known companies that credit AngularJS as the JavaScript framework include Google and Nike. Beginning in August 2013, General Motors cars featureapplications that were built in Angular. You’ll also notice a lot of news sites using AngularJS on their front pages, like theGuardian, the Huffington Post, and MSNBC.

According to Igor Minar, lead developer on AngularJS at Google, it’s more about Angular’s adaptability than anything to do with the news.

“I don’t think that Angular is more suitable for news sites than for other sites and apps. But there definitely is a bunch of them,” Minar said. “I think it’s just that these are high-visibility sites maintained by companies in highly-competitive market which means they keep their technology stack fresh in order to be efficient at making changes and providing great user experience.”

Why are sites that use Angular good at making quick changes? Probably because Angular, more aggressively than any other framework, nudges developers to create easily testable code and test it often. Though some developers might find this guidance annoying, it pays off in catching little coding errors before they have a chance to become big ones.

Another peculiarity of Angular is that it has a lot of its own terms and jargon. Minar thinks this is because the framework includes some features no other JavaScript solution does.

“Some terms we use commonly are specific to Angular and might come across as jargon or strange,” he said. “The good news is that the web standards are catching up and giving ‘official’ names to some of these concepts.”

If you code with Angular, you’re coding on Angular’s rigid terms, but Google Trendspoints to that not being such a bad thing. You’ll have to use Angular’s jargon and it might take time to make your code more testable, but the result is adaptability later on.

Backbone.js

Backbone came out in June 2010, and its community is nearly as large as Angular’s.

Many popular applications use the Backbone framework, including Twitter, Foursquare, and LinkedIn Mobile. Also worth noting is that a number of music apps were built with Backbone, including Soundcloud, Pitchfork, and Pandora.

However, there’s something about Backbone that’s very, very small compared to other frameworks—and that’s its download size. Compressed and minified, AngularJS is about 36K; the Ember starter kit is even bigger, at 69K. But Backbone, compared to its contemporaries, is downright puny, at just 6.4K.

Backbone’s diminutive size is typically its biggest selling point, since it only depends on one JavaScript library instead of several. As a result, Backbone is extremely lightweight, which means it’s good for building fast and responsive applications—but it’s most effective if those web applications are themselves small and single-page, or better yet, only part of a page, like many of the music apps using Backbone right now.

Here’s another characteristic that makes Backbone special: Its framework is remarkably hands-off, or as some developers put it, Backbone doesn’t “hold your hand.” This means experienced JavaScript developers can quickly get started, but less experienced developers might find themselves writing a lot of “boilerplate,” or repetitive code.

According to Backbone creator Jeremy Ashkenas, concerns about needless boilerplate coding are “a silly marketing campaign.”

“If you’re writing a lot of ‘boilerplate’ code in Backbone, then you don’t know how to use it,” Ashkenas said. “In general, in programming, if you’re writing the same thing over and over again—you write a function to do it automatically for you.”

If you’re having trouble, however, Backbone has an especially active community rife with free tutorials for getting started with the framework. Plenty of developers have taken to GitHub to upload useful examples and how-tos that take the place of other frameworks’ hand-holding.

If you’re working on a single-page application or widget—and you’re comfortable with being a self-starter—Backbone is likely the lightweight framework for you.

Ember.js

Ember is the newest kid on the block, but it’s already making waves. Initially released in 2011, Ember just hit version 1.0 last year. It also recently become Code School’s latest course, and given that Code School already offers courses for Angular and Backbone, it’s likely the newest course will grow to become equally popular.

LivingSocial, Groupon, Zendesk, Discourse, and Square are some of the most well-known applications that have adopted Ember. Ember creators Tom Dale and Yehuda Katz say it’s easy to see when a site is using Ember because of its loading speed.

“They feel like normal websites, they’re just far faster than what you’re used to,” Dale said. “It’s because all the rendering happens in the browser. It may look like a regular website, but under the hood, it’s architected like an iOS or Android app that isn’t being rendered by the server.”

At 69K minified and zipped, Ember is the largest framework of the three, but Katz points out that often medium-sized jpegs are just as large.

“The reason I feel confident that the features we’re baking in are things you need anyway is because I frequently look at the compiled size of Ember apps alongside other apps in the wild, and they’re all roughly the same size,” said Katz, implying that developers who use other frameworks often download additional libraries and tools during the building process.

Ember’s larger library size partly explains why it’s the largest download of the three Javascript frameworks, but another reason is because Ember comes with a lot of built-in support for standard code features. If you’ve ever tried to click the “back” button on a website only to get no response, you know all about what happens when JavaScript applications break. Ember’s support features are there to keep small but annoying errors like those from happening.

Ember’s library size and support network are its two greatest strengths, but if you’re only trying to create a small widget or single-page app, it might be overkill for you. If you’re working on a multipage, navigational, long-term project, Ember might be your pick.

When developers discuss these three frameworks online among their contemporaries, the discussion often devolves into one of personal preference. But from a non-developer perspective, it’s clear that different applications—and different needs—make each framework shine its brightest.

[repost ]10 Reasons Web Developers Should Learn AngularJS

original:http://www.wintellect.com/blogs/jlikness/10-reasons-web-developers-should-learn-angularjs

There is no doubt that AngularJS – the self-proclaimed “superheroic JavaScript framework” – is gaining traction. I’ll refer to it frequently as just “Angular” in this post. I’ve had the privilege on working on an enterprise web application with a large team (almost 10 developers, soon growing to over 20) using Angular for over half of a year now. What’s even more interesting is that we started with a more traditional MVC/SPA approach using pure JavaScript and KnockoutJS before we switched over to using the power-packed combination of TypeScript and Angular. It’s important to note that we added comprehensive testing using Jasmine but overall the team agrees the combination of technologies has increased our quality and efficiency: we are seeing far fewer bugs and delivering features far more quickly.

If you are familiar with Angular, this post may give you some ideas to think about you hadn’t encountered before. If you know Angular and are trying to justify its adoption at your company or on your project, this post can provide you with some background information that may help. If you have no idea what Angular is, read on because I’ll share why it’s so powerful and then point you to resources that will get you up to speed, quickly.

I can only assume other organizations are seeing positive results after adopting Angular. According to Google Trends the popularity of AngularJS (blue) compared to KnockoutJS (red) and “Single Page Applications” (yellow) is exploding.

angulartrends

One of the first single-track AngularJS conferences, ng-conf, sold out hundreds of tickets in just a few minutes.

This post isn’t intended to bash KnockoutJS or Ember or Backbone or any of the other popular frameworks that you may already be using and are familiar with. Instead, I’d like to focus on why I believe AngularJS is gaining so much momentum so quickly and is something anyone who works on web applications should take very seriously and at least learn more about to decide if it’s the right tool to put in your box.

1. AngularJS Gives XAML Developers a Place to Go on the Web

I make this bullet a little “tongue-in-cheek” because the majority of developers using Angular probably haven’t touched XAML with a 10 foot pole. That’s OK, the reasons why XAML became popular in the Microsoft world through WPF, Silverlight, and now Windows Store app development are important to look at because they translate quite well to Angular. If you’re not familiar with XAML, it is a declarative language based on XML used to instantiate object graphs and set values. You can define various types of objects with properties and literally mark up a set that will get created. The most common types of objects to create are user interface elements such as panels and controls that create a display. XAML makes it easy to layout complex UIs that may change over time. XAML supports inheritance (properties defined as children of parents can pick up values set higher in the tree) and bubbles events similar to the HTML DOM.

Another interesting component of XAML is the support for data-binding. This allows there to exist a declared relationship between the presentation layer and your data without creating hard dependencies between components. The XAML layer understands there is a contract – i.e. “I expect a name to be published” and the imperative code simply exposes a property without any knowledge of how it will be rendered. This enables any number of testing scenarios, decouples the UI from underlying logic in a way that allows your design to be volatile without having to refactor tons of code, and enables a truly parallel workflow between designers and developers.

 

This may sound like lip-service but I’ve been on many projects and have seen it in action. I recall two specific examples. One was aproject with Microsoft that we had to finish in around 4 months. We estimated a solid 4 months of hands-on development and a separate design team required about 4 months of design before all was said and done – they went from wireframes to comps to interactive mock-ups and motion study and other terms that make me thankful I can let the designers do that while I focus on code. Of course if we followed the traditional, sequential approach, we would have missed our deadline and waited 8 months (4 months of design followed by 4 months of coding). XAML allowed us to work in parallel, by agreeing upon an interface for a screen – “These are the elements we’ll expose.” The developers worked on grabbing the data to make those properties available and wrote all of the tests around them, and the designers took the elements and manipulated, animated, and moved them around until they reached the desired design. It all came together brilliantly in the end.

The other real world example was a pilot program with a cable company. We were building a Silverlight-based version of their interactive guide. The only problem was that they didn’t have the APIs ready yet. We were able to design the system based on a domain model that mapped what the user would experience – listings, times, etc. – then fill those domain objects with the APIs once they were defined and available. Again, it enabled a parallel workflow that greatly improved our efficiency and the flexibility of the design.

I see these same principles reflected in the Angular framework. It enables a separation of concerns that allows a true parallel workflow between various components including the markup for the UI itself and the underlying logic that fetches and processes data.

2. AngularJS Gets Rid of Ritual and Ceremony

egyptian-gods

Picture Credit: Piotr Siedlecki

Have you ever created a text property on a model that you want to bind to your UI? How is that done in various frameworks? In Angular, this will work without any issues and immediately reflect what you type in the span:

<input data-ng-model=’synchronizeThis’/><span>{{synchronizeThis}}</span>

Of course you’ll seldom have the luxury of building an app that simple, but it illustrates how easy and straightforward data-binding can be in the Angular world. There is very little ritual or ceremony involved with standing up a model that participates in data-binding. You don’t have to derive from an existing object or explicitly declare your properties and dependencies – for the most part, you can just pass something you already have to Angular and it just works. That’s very powerful. If you’re curious how it works, Angular uses dirty tracking.

Although I understand some other frameworks have gotten better with this, moving away from our existing framework where we had to explicitly map everything over to an interim object to data-bind to Angular was like a breath of fresh air … things just started coming together more quickly and I felt like was duplicating less code (who wants to define a contact table, then a contact domain object on the server, then a contact JSON object that then has to be passed to a contact client-side model just to, ah, display details about a contact?)

3. AngularJS Handles Dependencies

Dependency injection is something Angular does quite well. I’ll admit I was skeptical we even needed something like that on the client, but I was used to the key scenario being the dynamic loading of modules. Oh, wait – what did you say? That’s right, with libraries like RequireJS you can dynamically load JavaScript if and when you need it. Where dependency injection really shines however is two scenarios: testing and Single Page Applications.

For testing, Angular allows you to divide your app into logical modules that can have dependencies on each other but are initialized separately. This lets you take a very tactical approach to your tests by bringing in only the modules you are interested in. Then, because dependencies are injected, you can take an existing service like Angular’s $HTTP service and swap it out with the$httpBackend mock for testing. This enables true unit testing that doesn’t rely on services to be stood up or browser UI to render, while also embracing the ability to create end-to-end tests as well.

Single Page Applications use dynamic loading to present a very “native application” feel from a web-based app. People like to shout the SPA acronym like it’s something new but we’ve been building those style apps from the days of Atlas and Ajax. It is ironic to think that Ajax today is really what drives SPA despite the fact that there is seldom any XML involved anymore as it is all JSON. What you’ll find is these apps can grow quickly with lots of dependencies on various services and modules. Angular makes it easy to organize these and grab them as needed without worrying about things like, “What namespace does it live in?” or “Did I already spin up an instance?” Instead, you just tell Angular what you need and Angular goes and gets it for you and manages the lifetime of the objects for you (so, for example, you’re not running around with 100 copies of the same simple service that fetches that contact information).

4. AngularJS Allows Developers to Express UI Declaratively and Reduce Side Effects

There are many advantages to a declarative UI. I mentioned several when I discussed XAML earlier in this post, but HTML is in the same boat. Having a structured UI makes it easier to understand and manipulate. Designers who aren’t necessarily programmers can learn markup far easier than they can programming. Using jQuery you end up having to know a lot about the structure of your documents. This creates two issues: first, the result is a lot of unstable code working as “glue” that is tightly coupled to changes in the UI, and second, you end up with plenty “magic” because it’s not evident from looking at the markup just what the UI will do. In other words, you may have a lot of behaviors and animations that are wired up “behind the scenes” so it’s not apparent from looking at the form tags that any validation or transitions are taking place.

By declaring your UI and placing markup directly in HTML, you keep the presentation logic in one place and separated from the imperative logic. Once you understand the extended markup that Angular provides, code snippets like the one above make it clear where data is being bound and what it is being bound to. The addition of tools like directives and filters makes it even more clear what the intent of the UI is, but also how the information is being shaped because the shaping is done right there in the markup rather in some isolated code.

Maintaining large systems – whether large software projects or mid-sized projects with large teams – is about reducing side effects. A side effect is when you change something with unexpected or even catastrophic results. If your jQuery depends on an id to latch onto an element and a designer changes it, you lose that binding. If you are explicitly populating options in a dropdown and the designer (or the customer, or you) decides to switch to a third party component, the code breaks. A declarative UI reduces these side effects by declaring the bindings at the source, removing the need for hidden code that glues the behaviors to the UI, and allowing data-binding to decouple the dependency on the idea (i.e. “a list”) from the presentation of the idea (i.e. a dropdown vs. a bulleted list).

5. AngularJS Embraces ‘DD … Er, Testing

man-1378643715O0oIt doesn’t matter if you embrace Test-Driven Development, Behavior-Driven Development, or any of the driven-development methodologies, Angular embraces this approach to building your application. I don’t want to use this post to get into all of the advantages and reasons why you should test (I’m actually amazed that in 2013 people still question the value) but I’ve recently taken far more of a traditional “test-first” approach and it’s helped. I believe that on our project, the introduction of Jasmine and the tests we included were responsible for reducing defects by up to 4x. Maybe it’s less (or it could be more) but there was a significant drop-off. This isn’t just because of Angular – it’s a combination of the requirements, good acceptance criteria, understanding how to write tests correctly and then having the framework to run them – but it certainly was easier to build those tests. (Photo credit:George Hodan).

If you want to see what this looks like, take a look at my 6502 emulator and then browse the source code. Aside from some initial plumbing, the app was written with a pure test-first approach. That means when I want to add an op code, I write tests for the op code then I turn around and implement it. When I want to extend the compiler, I write a test for the desire outcome of compilation that fails, then I refactor the compiler to ensure the test passes. That approach saved me time and served to both change the way I structured and thought about the application, but also to document it – you can look at the specs yourself and understand what the code is supposed to do. The ability to mock dependencies and inject them in Angular is very important and as you can see from the example, you can test everything from UI behaviors down to your business logic.

6. AngularJS Enables Massively Parallel Development.

One of the biggest issues we encountered early in the project was developers stepping on each other’s toes. Part of this is just a discipline and even with raw JavaScript you can follow patterns that make it more modular, but Angular just took it to another level. That’s not to say it completely eliminates dependencies, but it certainly makes them easier to manage. As a specific case in point, there is a massive grid in the application that is used to drive several key operations. In a traditional JavaScript application it could have been a merge nightmare to scale this across a large team. With Angular, however, it was straightforward to break down the various actions into their own services and sub-controllers that developers could independently test and code without crashing into each other as often.

Obviously for larger projects, this is key. It’s not just about the technology from the perspective of how it enables something on the client, but actually how it enables a workflow and process that empowers your company to scale the team.

7, AngularJS Enables a Design <—> Development Workflow.

OK, who am I kidding, right? You can get this with HTML and CSS and other fun technologies. The reason this gets its own bullet, however, is because of just how well this works with Angular. The designer can add markup without completely breaking an application because it depends on a certain id or structure to locate an element and perform tasks. Instead, rearranging portions of code is as easy as moving elements around and the corresponding code that does the binding and filtering moves with it. Although I haven’t yet seen a savvy environment where the developers share a “design contract” with the UI/UX team, I don’t doubt it’s far off – essentially the teams agree on the elements that will be displayed, then design goes and lays it out however they want while development wires in the $scope with their controllers and other logic, and the two pieces just come together in the end. That’s how we did it with XAML and there is nothing preventing you from doing the same with Angular.

If you’re a Microsoft developer and have worked with Blend … wouldn’t it be cool to see an IDE that understands Angular and could provide the UI to set up bindings and design-time data? The ability is there, it just needs to be built, and with the popularity I’m seeing I don’t doubt that will take long.

8. AngularJS Gives Developers Controls.

One of the most common complaints I heard about moving to MVC was “what do we do with all of those controls?” The early perception was that controls don’t work/wouldn’t function in the non-ASP.NET space but web developers who use other platforms know that’s just not the case. There are a variety of ways to embrace reusable code on the web, from the concept of jQuery pluginsto third-party control vendors like one of my favorites, KendoUI.

Angular enables a new scenario known as a “directive” that allows you to create new HTML elements and attributes. In the earlier example, the directive for the “data-ng-model” allowed data-binding to take place. In my emulator, I use a directive to create two new tags: a “console” tag that writes the console messages and a “display” tag that uses SVG to render the pixels for the emulator (OK, by this time if you’ve checked it out I realize it’s more like a simulator). This gives developers their controls – and more importantly, control over the controls.

Our project has evolved with literally dozens of directives and they all participate in previous points:

  • Directives are testable
  • Directives can be worked on in parallel
  • Directives enable a declarative way to extend the UI, rather than using code to wire up new constructs
  • Directives reduce ritual and ceremony
  • Directives participate in dependency injection

Remember how I mentioned the huge grid that is central to the project? We happen to use a lot of grids (as does almost every enterprise web application ever written). We use the KendoUI variant, and there are several steps you must take to initialize the grid. For our purposes, many of the configuration options are consistent across grids, so why make developers type all of the code? Instead, we enable them to drop a new element (directive), tag it with a few attributes (directives), and they are up and running.

9. AngularJS Helps Developers Manage State.

I hesitate to add this point because savvy and experienced web developers understand the concept of what HTTP is and how to manage their application state. It’s the “illusion” of state that was perpetuated by ASP.NET that confuses developers when they shift to MVC. I once read on a rather popular forum a self-proclaimed architect declare that MVC was an inferior approach to web design because he had to “build his own state management.” What? That just demonstrates a complete lack of understanding of how the web works. If you rely on a 15K view state for your application to work, you’re doing it wrong.

I’m referring more to client state and how you manage properties, permissions, and other common cross-cutting concerns across your app in the browser. Angular not only handles dependency injection, but it also manages the lifetime of your components for you. That means you can approach code in a very different way. Here’s a quick example to explain what I mean:

One of the portions of the application involved a complex search. It is a traditional pattern: enter your search criteria, click “search” and see a grid with the results, then click on a row to see details. The initial implementation involved two pages: first, a detailed criteria page, then a grid page with a pane that would slide in from the right to reveal the details of the currently selected row. Later in the project, this was refactored to a dialog for the search criteria that would overlay the grid itself, then a separate full screen page for the details.

In a traditional web application this would involve rewriting a bit of logic. I’d likely have some calls that would get detail information and expect to pass them on the same page to a panel for the detail, then suddenly have to refactor that to pass a detail id to a separate page and have that page make the call, etc. If you’ve developed for the web for any amount of time you’ve had to suffer through some rewrites that felt like they were a bit much for just moving things around. There are multiple pieces of “state” to manage, including the selection criteria and the identifier for the detailed record being shown.

In Angular, this was a breeze. I created a controller for the search dialog, a controller for the grid, and a controller for the detail page. A parent controller kept track of the search criteria and current detail. This meant that switching from one approach to the other really meant just reorganizing my markup. I moved the details to a new page, switched the criteria to a dialog, and the only real code I had to write was a new function to invoke the dialog when requested. All of the other logic – fetching and filtering the data and displaying it – remained the same. It was a fast refactoring. This is because my controllers weren’t concerned with how the pages were organized or flowed – they simply focused on obtaining the information and exposing it through the scope. The organization was a concern of routing and we used Angular’s routing mechanisms to “reroute” to the new approach while preserving the same controllers and logic behind the UI. Even the markup for the search criteria remained the same – it just changed from a template that was used as a full page to a template that was used within a dialog.

Of course, this type of refactoring was possible due to the fact the application is a hybrid Single Page Application (SPA).

10. AngularJS Supports Single Page Applications.

In case you missed it, this point continues the last one. Single Page Applications are becoming more popular for a good reason. They fill a very specific need. More functionality is being moved to the web, and the browser is finally realizing its potential as a distributed computing node. By design, SPA applications are far more responsive (even though some of that is perception). They can provide an experience that feels almost like a native app in the web. By rendering on the client they cut down load on the server as well as reduce network traffic – instead of sending a full page of markup, you can send a payload of data and turn it into markup at the client.

In our experience, large apps make more sense to build as hybrid SPA apps. By hybrid I mean instead of treating the entire application as a single page application, you divide it into logical units of work or paths (“activities”) through the system and implement each of those as a SPA. You end up with certain areas that result in a full page refresh, but the key interactions take place in a series of different SPA modules. For example, administration might be one “mini” SPA app while configuration is another. Angular provides all of the necessary infrastructure from routing (being able to take a URL and map it to dynamically loaded pages), templates, to journaling (deep linking and allowing users to use the built-in browser controls to navigate even though the pages are not refreshing) needed to stand up a functional SPA application, and is quite adept at enabling you to share all of the bits and pieces across individual areas or “mini-SPA” sections to give the user the experience of being in a single application.

Conclusion

Whether you already knew about Angular and just wanted to see what my points were, or if you’re getting exposed for the first time, I have an easy “next step” for you to learn more. I recorded a video that lasts just over an hour covering all of the fundamentals you need to get started with writing Angular applications today. Although the video is on our “on demand training” site, I have a code you can use to get free access to both the video and the rest of the courses we have on WintellectNOW. Just head over to myFundamentals of AngularJS video and use the code LIKNESS-13 to get your free access.

[repost ]AngularJS 最佳实践

original:http://www.lovelucy.info/angularjs-best-practices.html

AngularJS Logo

AngularJS 是一个 Web 应用框架,它实现了前端的 MVC 架构,能让开发人员很方便地实现业务逻辑。

举个栗子,要做到下面的效果,以前可能需要写一连串的 JavaScript 代码绑定 N 多事件。而使用 AngularJS 框架,一句 JavaScript 都不用写就能实现了,神奇吧?查看演示

angularjs-demo

    <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>
    <div data-ng-app>
        单价: <input type="number" min=0 ng-model="price" ng-init="price = 299">
        <br>
        数量: <input type="number" min=0 ng-model="quantity" ng-init="quantity = 1">
        <br>
        总价: {{ quantity * price }}
    </div>

这得益于 AngularJS 中的双向数据绑定特性(Two Way Data-Binding),将 Model 和 View 自动关联了起来,在更复杂的业务场景下,还有代码分离的好处,将 DOM 操作和应用逻辑解耦,非常实用。

 

 

不过没有银弹,和其他框架一样,AngularJS 也有它的局限。CRUD 类型的操作是它所擅长的,想想看以前写过的管理后台,几乎大部分都是从数据库中读取数据,然后呈现在页面上,进行各种增删改查。AngularJS 约定了一套规范(约定优于配置),于是你可以很便捷地操作数据。而在其他方面,例如开发复杂的 Web 游戏,AngularJS 则是无用武之地了。

一、AngularJS 中的精美特性

双向绑定

上面的例子已经说明了,我们可以像 PHP Smarty 模板一样在 HTML 中写表达式,用 {{ 和 }} 包起来。在 AngularJS 里,View 和 Model 是在 Controller 里面绑定的,所以无论你在 View 的表单中修改了内容,还是在 Controller 里通过代码修改了 Model 值,两边都会即时发生变化,同步更新。因为 AngularJS 会监控 (watch) Model 对象的变化,随时反映到 View 中。

Filter

Filter 类似 Unix 里面的 | 管道概念,AngularJS 把它搬到了前端。还是举个例子,你们感受一下——

<div>{{ 9999 | number }}</div>
<div>{{ 9999+1 | number:2 }}</div>
<div>{{ 9*9 | currency }}</div>
<div>{{ 'Hello World' | uppercase }}</div>

输出结果:

9,999
10,000.00
$81.00
HELLO WORLD

二、AngularJS 中的一些“坑”

由于过去写 JavaScript 的习惯使然,人们很容易掉进一些 AngularJS 的陷阱里。下面的内容假设你已经了解前端 MVC 概念,并对 AngularJS 有了一定经验,初学者读起来可能比较艰深晦涩。

DOM 操作

避免使用 jQuery 来操作 DOM,包括增加元素节点,移除元素节点,获取元素内容,隐藏或显示元素。你应该使用 directives 来实现这些动作,有必要的话你还要编写自己的 directives。

如果你感到很难改变习惯,那么考虑从你的网页中移除 jQuery 吧。真的,AngularJS 中的 $http 服务非常强大,基本可以替代 jQuery 的 ajax 函数,而且 AngularJS 内嵌了 jQLite —— 它内部实现的一个 jQuery 子集,包含了常用的 jQuery DOM 操作方法,事件绑定等等。但这并不是说用了AngularJS 就不能用 jQuery 了。如果你的网页有载入 jQuery 那么 AngularJS 会优先采用你的 jQuery,否则它会 fall back 到 jQLite。

需要自己编写 directives 的情况通常是当你使用了第三方的 jQuery 插件。因为插件在 AngularJS 之外对表单值进行更改,并不能即时反应到 Model 中。例如我们用得比较多的 jQueryUI datepicker 插件,当你选中一个日期后,插件会将日期字符串填到 input 输入框中。View 改变了,却并没有更新 Model,因为$('.datepicker').datepicker(); 这段代码不属于 AngularJS 的管理范围。我们需要编写一个directive 来让 DOM 的改变即时更新到 Model 里。

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 中引入这个 direcitve

<input type="text" datepicker ng-model="myObject.myDateValue" />

说白了 directive 就是在 HTML 里写自定义的标签属性,达到插件的作用。这种声明式的语法扩展了 HTML。

需要说明的是,有一个 AngularUI 项目提供了大量的 directive 给我们使用,包括 Bootstrap 框架中的插件以及基于 jQuery 的其他很热门的 UI 组件。我之前说过 AngularJS 的社区很活跃嘛,生态系统健全。

ngOption 中的 value

这是个大坑。如果你去查看 ngOption 生成的 <select> 中的 <option> 的选项值(每个 <option value="xxx"> 的 value 部分),那绝对是枉费心机。因为这里的值永远都会是 AngularJS 内部元素的索引,并不是你所指定的表单选项值。

还是要转变观念,AngularJS 已经不再用表单进行数据交互了,而是用 Model。使用 $http 来提交 Model,在 php 中则使用 file_get_contents('php://input') 来获取前端提交的数据。

{{ }} 的问题

在页面初始化的时候,用户可能会看到 {{ }},然后闪烁一下才出现真正的内容。
解决办法:

  1. 使用 ng-cloak directive 来隐藏它
  2. 使用 ng-bind 替代 {{ }}

将界面与业务逻辑分离

Controller 不应该直接引用 DOM,而应该控制 view 的行为。例如“如果用户操作了 X,应该发生什么事情”,“我从哪里可以获得 X?”

Service 在大部分情况下也不应该直接引用 DOM,它应该是一个单例(singletons),独立于界面,与 view 的逻辑无关。它的角色只是“做 X 操作”。

DOM 操作应该放在 directives 里面。

尽量复用已有功能

你所写的功能很可能 AngularJS 已经实现了,有一些代码是可以抽象出来复用的,使用更 Angular 的方式。总之就是很多 jQuery 的繁琐代码可以被替代。

1. ng-repeat

ng-repeat 很有用。当 Ajax 从服务器获得数据后,我们经常使用 jQuery (比如上面讲过的例子) 向某些 HTML 容器节点中添加更多的元素,这在 AngularJS 里是不好的做法。有了 ng-repeat 一切就变得非常简单了。在你的 $scope 中定义一个数组 (model) 来保存从服务器拉取的数据,然后使用 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>

显示结果

I have 2 friends. They are:
  [1] John who is 25 years old.
  [2] Mary who is 28 years old.

2. ng-show

ng-show 也很有用。使用 jQuery 来根据条件控制界面元素的显示隐藏,这很常见。但是 Angular 有更好的方式来做到这一点。ng-show (以及 ng-hide) 可以根据布尔表达式来决定隐藏和显示。在 $scope 中定义一个变量:

<div ng-show="!loggedIn">
    点击 <a href="#/login">这里</a> 登录
</div>

类似的内置 directives 还有 ng-disabled, ng-switch 等等,用于条件控制,语法简洁,都很强大。

3. 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,很实用。

$watch 和 $apply

AngularJS 的双向数据绑定是最令人兴奋的特性了,然而它也不是全能的魔法,在某些情况下你需要做一些小小的修正。

当你使用 ng-model, ng-repeat 等等来绑定一个元素的值时, AngularJS 为那个值创建了一个 $watch,只要这个值在 AngularJS 的范围内有任何改变,所有的地方都会同步更新。而你在写自定义的 directive 时,你需要定义你自己的 $watch 来实现这种自动同步。

有时候你在代码中改变了 model 的值,view 却没有更新,这在自定义事件绑定中经常遇到。这时你就需要手动调用 scope.$apply() 来触发界面更新。上面 datepicker 的例子已经说明了这一点。第三方插件可能会有 call back,我们也可以把回调函数写成匿名函数作为参数传入$apply()中。

将 ng-repeat 和其他 directives 结合起来

ng-repeat 很有用,不过它和 DOM 绑定了,很难在同一个元素上使用其他 directives (比如 ng-show, ng-controller 等等)。

如果你想对整个循环使用某个 directive,你可以在 repeat 外再包一层父元素把 directive 写在那儿;如果你想对循环内部的每一个元素使用某个 directive,那么把它放到 ng-repeat 的一个子节点上即可。

Scope

Scope 在 templates 模板中应该是 read-only 的,而在 controller 里应该是 write-only 的。Scope 的目的是引用 model,而不是成为 model。model 就是我们定义的 JavaScript 对象。

$rootScope 是可以用的,不过很可能被滥用

Scopes 在 AngularJS 中形成一定的层级关系,树状结构必然有一个根节点。通常我们用不到它,因为几乎每个 view 都有一个 controller 以及相对应的自己的 scope。

但偶尔有一些数据我们希望全局应用在整个 app 中,这时我们可以将数据注入 $rootScope。因为其他 scope 都会继承 root scope,所以那些注入的数据对于 ng-show 这类 directive 都是可用的,就像是在本地 $scope 中的变量一样。

当然,全局变量是邪恶的,你必须很小心地使用 $rootScope。特别是不要用于代码,而仅仅用于注入数据。如果你非常希望在 $rootScope 写一个函数,那最好把它写到 service 里,这样只有用到的时候它才会被注入,测试起来也方便些。

相反,如果一个函数的功能仅仅是存储和返回一些数据,就不要把它创建成一个 service。

三、AngularJS 项目的目录结构

怎样组织代码文件和目录?这恐怕是初学者一开始就会遇到的问题。AngularJS 应用开发的官方入门项目angular-seed,其文件结构是这样的:

  • css/
  • img/
  • js/
    • app.js
    • controllers.js
    • directives.js
    • filters.js
    • services.js
  • lib/
  • partials/

这种结构对于一个简单的单页 app 来说是可行的,只是一旦代码中存在多个 Controller 或者 Service,就很难找到想要寻找的对象了。我们可以对文件按照业务逻辑进行拆分,就像下面这样:

  • controllers/
    • LoginController.js
    • RegistrationController.js
    • ProductDetailController.js
    • SearchResultsController.js
  • directives.js
  • filters.js
  • models/
    • CartModel.js
    • ProductModel.js
    • SearchResultsModel.js
    • UserModel.js
  • services/
    • CartService.js
    • UserService.js
    • ProductService.js

这种结构把不同的业务功能拆分为独立的文件,条理清晰,但是仍有一定的局限性。最大的问题是一个业务功能的代码分布在controllers, models, servers 三个不同目录下,要从中挑出正确的文件,建立起代码关联,还是有些麻烦。按照功能进行模块化划分目录结构,应该要更为合理一些:

  • cart/
    • CartModel.js
    • CartService.js
  • common/
    • directives.js
    • filters.js
  • product/
    • search/
      • SearchResultsController.js
      • SearchResultsModel.js
    • ProductDetailController.js
    • ProductModel.js
    • ProductService.js
  • user/
    • LoginController.js
    • RegistrationController.js
    • UserModel.js
    • UserService.js

这样也是适合 RequireJS 等模块加载器的自然直观的代码组织方式。

参考链接: