AngularJs基础教程

来自ling
跳转至: 导航搜索

目录

0. 目录


1. 前言

AngularJS是为了克服HTML在构建应用上的不足而设计的。HTML是一门很好的为静态文本展示设计的声明式语言,但要构建WEB应用的话它就显得乏力了,所以AngularJS做了一些工作来来解决静态网页技术在构建动态应用上的不足。

AngularJS的初衷是为了简化web APP开发,精髓是简单。但是国内外有很多AngularJS的教程,都着重讲AngularJS的强大之处,从而增加了AngularJS学习的难度,本教程试图用通俗的语言讲解最为基础最为实用的内容,简化学习进程、降低学习难度是本教程的初衷。

本系列教程以翻译Chris SmithAngualr Basics为梗概,融合博主自己的理解,为大家提供一个简单明了的学习教程。

本文为系列教程第7篇模块,翻译自Modules

  1. 引言-Introduction
  2. 基础-Basics
  3. 控制器-Controllers
  4. 作用域-Scopes
  5. 集合-Collections
  6. 模块-Modules
  7. 依赖注入-Dependency Injection
  8. 服务-Services
  9. 过滤器-Filters
  10. 指令-Directives
  11. 指令作用域-Directive Scopes
  12. 路由-Routing
  13. 通信-HTTP
  14. 结论

2.正文

总的来说,模块(modules)的目的是通过定义公共APIs、限制行为(功能)和数据(属性和变量)的可视化实现关注点分离(separation of concerns,soc)。大部分的编程平台内置了对模块化的支持,以致于我们会觉得理所当然而无所察觉。但是客户端javascript没有支持,对附加的解决方案(例如CommonJSAMD)的好处和坏处也进行了诸多争论。

需要注意的是,Angular的模块化系统不是模块化加载(它不从文件或internet加载源码),Angular提供的是内置的、私有的模块化方案工作原理类似于其内置的、私有的依赖注入方案,两个系统提供了令人印象深刻的通用基础设施。尽管使用模块化功能是每个开发者的需要掌握的内在能力,但问题是,为什么我们需要?

尽管模块化和依赖注入通常是必要命题,但是其实Angular允许我们不使用模块实现很多事情。截止目前,我们已经完成了一系列通过构造函数(在全局作用域定义和控制器一样名称的函数)不用模块的案例。通过一点简单的设置,ng-controller指令在全局范围和模块化系统中查找控制器函数,允许我们使用简化的方法使用Angular,但仅仅是一定程度上。

2.1 为什么使用模块化-Why use Angular modules?

为了我们可以在Angular基础特性之后走的更远,我们需要精通它的模块化系统。相对于JS对象的管理复杂性问题来说,我们应该使用简单的方法解决问题,寻找您自己的最佳实践,因此本书不想详细介绍模块化和依赖注入的本质特性,只介绍跟Angular使用密切相关的模块和依赖注入的内容。

因此,让我们审视下必须使用Angular模块的重要特性:

  • 特殊组件-Specialized components,在定义您自己的控制器、指令、过滤器和动画的时候,我们必须使用模块系统(控制器可能例外,前面已经说过)
  • 依赖注入-Dependency injection,尽管服务是普通的JS对象和函数, 但使用模块系统创建的服务可以简单地被随着依赖性注入,进行依赖注入。
  • 外部模块-External modules,Angular拥有非常吸引人的开发社区,海量免费的官方插件、第三方插件,如果您要在您的应用中使用这些库,我们就不得不用到模块系统。
  • 加载时配置-Load-time configuration ,Angular的模块系统提供了访问内部配置、插件配置的功能,我们将在最后一章配置自定义HTTP头的案例中演示后面一个特性。
  • 测试-Testing, Angular令人心动的测试功能基于依赖注入实现,同样离不开模块系统

下一章我们讲服务,主要演示如何为自定义JS组件定义提供支持。本章讲解如何定义控制器加载外部模块,同时为您讲解模块系统的基础。

2.2 创建应用模块-Creating the application module

一个Angular应用通常起始于一个根模块,在我们的案例中这个模块名字叫做app,注意,这个名字虽说常见,很多时候就是简单起见,未必是最好的选择。

<code>/* module.js */
angular.module('app', []);</code>

在上面的案例中,需要特别注意module的第二个参数,尽管它看起来只是一个空的数组,但是它是非常重要的,如果您忽略它,angular.module的行为将产生根本改变。考虑到API的合理性设计, 当你指定第二个参数调用angular.module时,该方法处于创建模式,如果名为app的模块不存在,将创建一个名字为app的模块。但是调用angular.module时不指定第二个参数,它处于寻找模式,如果给定名称的模块存在将返回该模块,如果不存在将抛出错误,提醒我们注意第二个参数。该案例我们使用了正确的方式,但我们需要明白代码中的重要区别。这个参数用于导入外部模块,我们将会在angular动画模块时通过案例说明这一点。

2.3 加载应用模块-Loading the application module

在承载应用的HTML文档中,我们需要通过传递模块名称作为参数给ng-app指令告诉Angular加载应用模块。

<code><!-- index.html -->
<body ng-app="app">
  <!-- Other examples to be inserted here. -->
</body></code>

我们添加到应用模块中的任何组件都可以使用,让我们来看看如何使用模块定义一个控制器(不在使用全局的scope)。

2.4 定义控制器-Defining a controller

moduleAPI里面包含了定义特殊Angular组件的一些方法,截至目前我们比较熟悉的controller方法,我们使用controller方法创建控制器。首先我们需要取得一个应用模块的引用,同时我们需要给angular.module不指定第二个参数让它处于查询模式(主要是因为我们上文已经创建过应用模块了)。

<code>/* message-controller.js */
var app = angular.module('app');
app.controller('MessageController', function($scope) {
  $scope.message = "This is a model.";
});</code>

然后我们需要在调用ng-app的元素里面的某个位置,使用ng-controller调用我们的控制器。

<code><!-- message-controller.html -->
<p ng-controller="MessageController">
  {{message}}
</p></code>

编译结果为:This is a model.

您已经看到了,使用模块定义控制器比全局scope的方式稍微麻烦点,但不至于太糟。

2.5 链式定义-Chaining definitions

假设我们需要定义如下的两个控制器

<code><!-- another-message-controller.html -->
<p ng-controller="MessageController">
  {{message}}
</p>
<p ng-controller="AnotherMessageController">
  {{message}}
</p></code>

module的定义方法可以是链式操作,这样我们可以在一个语句里定义两个控制器。

<code>/* chained-controllers.js */
angular.module('app')
  .controller('MessageController', function($scope) {
    $scope.message = "This is a model.";
  })
  .controller('AnotherMessageController', function($scope) {
    $scope.message = "This is another model.";
  });</code>

编译结果为: 
This is a model. 
This is another model.

注意,第一个控制器的声明语句没有用分号结束。

如果你不喜欢链式操作风格,你可以随时检索调用模块,当然也可以存储在一个变量里(如下面代码所示)。如果您使用了变量,最好能够在IIFE(立即调用函数表达式)或者其他闭包里避免污染全局变量。

<code>/* separate-controllers.js */
var app = angular.module('app');

app.controller('MessageController', function($scope) {
  $scope.message = "This is a model.";
});

app.controller('AnotherMessageController', function($scope) {
  $scope.message = "This is another model.";
});</code>

选择哪种风格由您决定,不过链式风格更加流行些。

2.6 加载模块-Loading modules

Animation是Angular的一个新增特性,是一个名字叫做ngAnimate的独立模块。使用animations的第一步是引入包含模块,源文件中是Angular的核心部分,但是分发的时候以一个单独文件分发,如下所示。

<code><script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular-animate.js"></script>
<!-- 以上被墙,国内用另一个cdn -->
<script src="//cdn.bootcss.com/angular.js/1.3.14/angular.js"></script>
<script
src="//cdn.bootcss.com/angular.js/1.3.14//angular-animate.js"></script></code>

是时候我们使用module方法的第二个数组参数了,前面的案例我们只是传递了一个空参数, 这次我们声明模块的依赖性。

<code>/* ng-animate-module.js */
angular.module('app', ['ngAnimate']);</code>

这样,我们在应用中引入了ngAnimate。 模板中的ng-show、ng-hide、ng-class等指令将会在css和js里检测JS animation,也许会应用在类似于add、enter、leave、move、remove等DOM事件中 。在DOM事件中应用animation无需模板做任何改变(感觉像透明一样), 如果动画代码没有呈现, 或者作为css或者作为js通过[module.animation](http://docs.angularjs.cn/api/ng/type/angular.Module)注册,指令代码还是普通代码。

<code><!-- ng-animate.html -->
<input type="checkbox" ng-model="showMessage">
Check the box to show the message
<h2 ng-show="showMessage">
  The secret message.
</h2></code>

编译结果为: 
http://image84.360doc.com/DownloadImg/2015/04/2317/52753545_1

单击上面的复选框,我们能够看到元素之间的显示与隐藏式立刻进行的,因为我们没有在css或js中添加动画。接下来,我们使用css(通过声明式风格创建一般动画的较好选择),当然使用jQuery的animate方法也不错,Angular的module.animation能够兼容这些方法。

控制淡入淡出需要四个额外的css类,ng-hide-add、ng-hide-add-active、ng-hide-remove、ng-hide-remove-active等。

<code>/* ng-animate.css */
.ng-hide-add,
.ng-hide-remove {
  transition: all linear 1s;
  display: block !important;
}

.ng-hide-add.ng-hide-add-active,
.ng-hide-remove {
  opacity: 0;
}

.ng-hide-add,
.ng-hide-remove.ng-hide-remove-active {
  opacity: 1;
}</code>

这次的编译结果如下所示。 
http://image84.360doc.com/DownloadImg/2015/04/2317/52753545_2 
根据您的网站的用户需要,你可能需要给特定的css属性增加浏览器引擎前缀(vendor-prefix),例如针对iOS Safari6增加-webkit-transition(caniuse.com是一个比较好的html5、css3浏览器兼容性网站)。注意这个动画将附加到任何一次ng-show、ng-hide使用中,为了做更好的控制,我们可以在元素和选择器上增加自定义css类,例如.my-class.ng-hide-add.my-class.ng-hide-remove等等。

2.7 结论

添加ngAnimate的案例我想让您确信理解Angular 模块系统的重要性。而且,令人高兴的是,ngAnimate只是使用Angular附件模块的开始,除了可以使用官方提供的模块资源外,大家还可以到github之类的网站上寻找更多的第三方开源模块。最流行的一个项目是AngularUI,提供了UI Router、UI Bootstrap等强大的模块,这些模块可以方便地使用Bower进行安装,当您上手Angular之后,您也可以在Github和Bower发布您的Angular模块,等你哟!

ngAnimate模块演示一个如何使用附件模块发挥强大功能的案例,但是因为它特殊的嵌入方式,没有使用依赖注入。因为模块和依赖注入需要相互协作,下一章我们将讲解依赖注入。

3.声明

前端开发whqet,关注前端开发,分享相关资源。csdn专家博客,王海庆希望能对您有所帮助,限于作者水平有限,出错难免,欢迎拍砖! 
欢迎任何形式的转载,烦请注明装载,保留本段文字。 
本文原文链接,http://blog.csdn.net/whqet/article/details/45074873 
欢迎大家访问独立博客http://whqet.github.io

作者:whqet 发表于2015/4/19 11:54:36 原文链接

阅读:1486 评论:8 查看评论

06集合-AngularJS基础教程

0. 目录


1. 前言

AngularJS是为了克服HTML在构建应用上的不足而设计的。HTML是一门很好的为静态文本展示设计的声明式语言,但要构建WEB应用的话它就显得乏力了,所以AngularJS做了一些工作来来解决静态网页技术在构建动态应用上的不足。

AngularJS的初衷是为了简化web APP开发,精髓是简单。但是国内外有很多AngularJS的教程,都着重讲AngularJS的强大之处,从而增加了AngularJS学习的难度,本教程试图用通俗的语言讲解最为基础最为实用的内容,简化学习进程、降低学习难度是本教程的初衷。

本系列教程以翻译Chris SmithAngualr Basics为梗概,融合博主自己的理解,为大家提供一个简单明了的学习教程。

本文为系列教程第6篇集合,翻译自Collections

  1. 引言-Introduction
  2. 基础-Basics
  3. 控制器-Controllers
  4. 作用域-Scopes
  5. 集合-Collections
  6. 模块-Modules
  7. 依赖注入-Dependency Injection
  8. 服务-Services
  9. 过滤器-Filters
  10. 指令-Directives
  11. 指令作用域-Directive Scopes
  12. 路由-Routing
  13. 通信-HTTP
  14. 结论

2.正文

上一章Scope中我们了解到,Angular每次通过ng-controller调用controller构造函数时都会创建一个scope。当然,还有一些其他方式可以创建新的scope,其中最流行的方式就是使用相似对象的集合实现。不像Backbone,Angular没有Collection组件,但它支持相似对象的集合,本章将详述这些。

2.1 Set up

除了从Google Hosted Libraries加载Angular之外,为了让表格和列表好看一些,本章的动态案例还加载了Bootstrap。

<code><!-- index.html(head) -->
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css">
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.js"></script></code>

接着,通过ng-app指令加载我们的app模块,为了简单起见,我们使用了app的名称,实际上它不是最好的名称。

<code><!-- index.html -->
<body ng-app="app">
  <!-- Other examples to be inserted here. -->
</body></code>

正如前面文章所讲过的那样,为了避免使用modules创建controller,我们需要进行一些简单的设置。

<code>/* module.js */
angular.module('app', []);
angular.module('app').config(['$controllerProvider', function($controllerProvider) {
  $controllerProvider.allowGlobals();
}]);</code>

这样,我们就可以进行Angular方面集合、循环等方面的交互探索了。

2.2 Iteration

在普通JS中,如果你要通过for循环遍历集合,你可能需要声明一个local变量来引用当前元素,例如。

<code>/* items.js */
var items = [{name: "Item 1"},{name: "Item 2"}];
for (var i = 0; i < items.length; i++) {
  var item = items[i];
}
document.body.innerHTML = item.name;</code>

结果为: Item 2

虽然你可能相信(希望、盼望、打赌)JS为for循环里每一次迭代存在一个词法作用域,但正如上面案例所示,并不存在(词法作用域)。循环之外item属性可用,可以到 Mozilla’s JavaScript Guide了解更多详情。

Angular可以通过内置的for语句避免这些,为了演示起见,我们在控制器内显示item数组。

<code>/* items-controller.js */
function ItemsController($scope) {
  $scope.items = [
    {name: 'Item 1'},
    {name: 'Item 2'}
  ];
}</code>

假设item是一个长度位置的集合,我们需要遍历元素展示它的name属性。

2.3 ng-repeat

正如Angular为表达式创建一个顶层Angular scope以避免在JS全局作用域中创建变量一样,内置的ng-repeat指令通过为每次循环的每一次迭代创建一个Angular作用域来避免上面的问题

<code><!-- ng-repeat.html -->
<ol ng-controller="ItemsController">
  <li ng-repeat="item in items" ng-bind="item.name"></li>
  <li ng-bind="item.name"></li>
</ol></code>

编译结果为: 
1. Item 1 
2. Item 2 
3.

正如结果所示,ng-repeat循环之外的item属性不可用。

针对循环中的每一个元素,ng-repeat创意建一个具备指定属性的子scope。在本案例中,属性名为item,但是它可以是任何值,您可以尝试修改下,看看会怎么样。

2.4 Object properties

(key, value) in object syntax语法允许我们循环对象的属性。

<code><!-- ng-repeat-name.html -->
<table class="table table-condensed">
  <tr ng-repeat="(propertyName, propertyValue) in {b: 'two', a: 1.0, c: 3}">
    <td ng-bind="propertyName"></td>
    <td ng-bind="propertyValue"></td>
  </tr>
</table></code>

编译结果如下图所示。 
http://image84.360doc.com/DownloadImg/2015/04/2317/52753545_3

注意,ng-repeat在执行循环之前使用name对数据进行了排序。

我们给ng-repeat传递的item in items语法看起来挺像list comprehension([列表推导式](http://www.cainiao8.com/python/basic/python_14_list_comprehension.html)),但是不幸的是,他不返回任何值,除了对象成员或右边的数组,让我们试一下。

<code><!-- ng-repeat-for-keyword.html -->
<ol ng-controller="ItemsController">
  <!-- Invalid code! Syntax error, because 'for' is not supported! -->
  <li ng-repeat="item.name for item in items" ng-bind="item.name"></li>
</ol></code>

产生了错误,是吧。上面的代码没有正常工作。 
真正的list comprehension(列表推导式)允许for关键字从原始列表中返回我们需要的任何值,例如 CoffeeScrip的案例所示

2.5 $index

除了可以持有元素的属性外,ng-repeat还把当前元素的序号作为一个特殊的属性,叫做$index。如果我们想给列表加上序号,就可以使用$index了。

<code><!-- items-controller.html -->
<div ng-controller="ItemsController">
  <div ng-repeat="item in items">
     {{$index + 1}}. {{item.name}}
  </div>
</div></code>

编译结果为: 
1. Item 1 
2. Item 2

让我们尝试下嵌套使用ng-repeat。首先我们创建一个复杂一点的数据模型。

<code>/* items-controller-nested.js */
function ItemsController($scope) {
  $scope.items = [
    {name: 'Item 1',
      items: [
       {name: 'Nested Item 1.1'},
       {name: 'Nested Item 1.2'}
      ]
    },
    {name: 'Item 2',
      items: [
       {name: 'Nested Item 2.1'},
       {name: 'Nested Item 2.2'}
      ]
    }
  ];
}</code>

然后,我们通过一个两层循环,把数据显示成有序列表。

<code><!-- items-controller-nested.html -->
<div ng-controller="ItemsController">
  <ol>
    <li ng-repeat="item in items">
      {{item.name}}
      <ol>
        <li ng-repeat="item in item.items">
          {{item.name}}
        </li>
      </ol>
    </li>
  </ol>
</div></code>

编译结果为:

  1. Item 1 
    1. Nested Item 1.1
    2. Nested Item 1.2
  2. Item 2 
    1. Nested Item 2.1
    2. Nested Item 2.2

如果我们需要outline风格的嵌套计数怎么办(貌似1.1,1.2,2.1,2.2)?我们如何保证外层循环的数据不被内层循环的数据覆盖?

2.6 ng-init

你可能还记得,我们可以使用ng-init指令可以初始化scope属性,这个案例我们使用ng-init来重新声明变量,以避免外层数据的覆盖。

<code><div ng-controller="ItemsController">
  <div ng-repeat="item in items" ng-init="outerCount = $index">
    {{outerCount + 1}}. {{item.name}}
    <div ng-repeat="item in item.items">
       {{outerCount + 1}}.{{$index + 1}}. {{item.name}}
    </div>
  </div>
</div></code>

编译结果为: 
1. Item 1 
1.1. Nested Item 1.1 
1.2. Nested Item 1.2 
2. Item 2 
2.1. Nested Item 2.1 
2.2. Nested Item 2.2

除了$index之外,ng-repeat给循环的每一次迭代增加了一些逻辑属性,$first, $middle, $last, $even和$odd。你可以使用下面的案例尝试下体会一下。使用ng-class指令给条件为真的列表项设置绿色标签。你能让第一个和第五个设置绿色,其他灰色吗,来试一下吧。

<code><!-- ng-repeat-middle.html -->
<ol>
  <li ng-repeat="val in [1,2,3,4,5]">
    <span class="label label-default"
          ng-class="{'label-success': $middle}">
      {{val}}
    </span>
  </li>
</ol></code>

编译结果为下面动图所示。 
http://image84.360doc.com/DownloadImg/2015/04/2317/52753545_4

有没有注意到,我们在使用even和odd时,我们的序号是从0开始编号的。

2.7 Uniqueness

注意,如果在js的严格模式下,ng-repeat需要每一个数据都是唯一的(用严格相等)。

2.7.1 严格相等 ===

下面让我们花点时间研究下严格相等,熟悉的同学可跳过。

<code><!-- equals-controller.html -->
<table class="table table-condensed">
  <tr>
    <td>1 === 1</td>
    <td>{{1 === 1}}</td>
  </tr>
  <tr>
    <td>'1' === '1'</td>
    <td>{{'1' === '1'}}</td>
  </tr>
  <tr>
    <td>1 === '1'</td>
    <td>{{1 === '1'}}</td>
  </tr>
  <tr>
    <td>{} === {}</td>
    <td>{{ {} === {} }}</td>
  </tr>
  <tr>
    <td>{name: 1} === {name: 1}</td>
    <td>{{ {name: 1} === {name: 1} }}</td>
  </tr>
</table></code>

http://image84.360doc.com/DownloadImg/2015/04/2317/52753545_5

因为ng-repeat需要保证数据的唯一性,所以下面的代码会报错。但是,比如我们把[1,2,1]改成[1,2,'1']就没问题了。

<code><!-- ng-repeat-duplicates.html -->
<ol>
  <!-- Invalid code! Duplicate element error, because '1' is repeated! -->
  <li ng-repeat="val in [1,2,1]" ng-bind="val"></li>
</ol></code>

2.7.2 track by

如果你需要解决上面的问题,对不唯一的数据进行ng-repeat,可以使用track by关键字,ng-repeat将会忽略元素的相等检测。

<code><!-- ng-repeat-track-by.html -->
<ol>
  <li ng-repeat="val in [1,2,1] track by $index" ng-bind="val"></li>
</ol></code>

编译结果为:

  1. 1
  2. 2
  3. 1

当然,我们应该尽可能地保证数据的唯一性,例如给数据添加一个唯一的id属性等。如果您使用$index,结合的改变可能产生DOM事件问题

2.8 Callback functions

Angular同样可以方便的在控制器里建立对集合对象的引用,我们可以在指令中把集合元素属性传递给一个回调函数,如下代码所示。

我们简单的响应用户交互删除集合中的元素,在控制器中,我们可以定义个回调函数,名字可以随意,最好可以叫做destroy。

<code>/* items-controller-destroy.js */
function ItemsController($scope) {
  $scope.items = [
    {id: 1, name: 'Item 1'},
    {id: 2, name: 'Item 2'},
    {id: 3, name: 'Item 3'},
    {id: 4, name: 'Item 4'}
  ];

  $scope.destroy = function(item) {
    var index = $scope.items.indexOf(item);
    $scope.items.splice(index, 1);
  };
}</code>

然后,我们使用ng-click="destroy(item)"来个每一个按钮添加事件处理。

<code><!-- items-controller-destroy.html -->
<div ng-controller="ItemsController">
  <h4 ng-pluralize count="items.length"
      when="{'one': '1 item', 'other': '{} items'}">
  </h4>
  <table class="table table-condensed">
    <tr ng-repeat="item in items">
      <td ng-bind="item.name"></td>
      <td>
        <button class="btn btn-xs btn-default" ng-click="destroy(item)">
          destroy
        </button>
      </td>
    </tr>
  </table>
</div></code>

http://image84.360doc.com/DownloadImg/2015/04/2317/52753545_6

destroy方法的调用方法是Angular声明式语法的典型特性,另外本案例还是演示了[ng-pluralize](http://docs.ngnice.com/api/ng/directive/ngPluralize)的使用。

2.9 -start and -end

虽然不太常见,但是你也可能需要渲染集合成员的兄弟元素。例如,描述列表,定义列表中的dt、dd等。ng-repeat只针对单一元素,使用-start和-end可以扩展该指令。

<code><dl ng-controller="ItemsController">
  <dt ng-repeat-start="item in items">name</dt>
  <dd ng-bind="item.name"></dd>
  <dt>price</dt>
  <dd ng-repeat-end ng-bind="item.price"></dd>
</dl></code>

编译结果为: 
name 
Item 1 
price 
name 
Item 2 
price

这些前后缀不仅仅限于ng-repeat,也可以应用与其他指令上。在自定义指令时,记得不要使用这种前后缀命名指令。

2.10 结论

Angular内置的对集合的支持(ng-repeat)既强大又灵活,可以让我们非常快速的创建经典用户界面、增删改查应用。本章的案例证明了我们可以在不接触Angular内核的情况下实现功能强大的web开发。从这里开始,我们深入探究Angular,研究Angular如何管理组件,下一章,我们来学习Angular自制的模块系统。

3.声明

前端开发whqet,关注前端开发,分享相关资源。csdn专家博客,王海庆希望能对您有所帮助,限于作者水平有限,出错难免,欢迎拍砖! 
欢迎任何形式的转载,烦请注明装载,保留本段文字。 
本文原文链接,http://blog.csdn.net/whqet/article/details/45047181 
欢迎大家访问独立博客http://whqet.github.io

作者:whqet 发表于2015/4/16 11:32:39 原文链接

阅读:1309 评论:12 查看评论

05作用域-AngularJS基础教程

0. 目录


1. 前言

AngularJS是为了克服HTML在构建应用上的不足而设计的。HTML是一门很好的为静态文本展示设计的声明式语言,但要构建WEB应用的话它就显得乏力了,所以AngularJS做了一些工作来来解决静态网页技术在构建动态应用上的不足。

AngularJS的初衷是为了简化web APP开发,精髓是简单。但是国内外有很多AngularJS的教程,都着重讲AngularJS的强大之处,从而增加了AngularJS学习的难度,本教程试图用通俗的语言讲解最为基础最为实用的内容,简化学习进程、降低学习难度是本教程的初衷。

本系列教程以翻译Chris SmithAngualr Basics为梗概,融合博主自己的理解,为大家提供一个简单明了的学习教程。

本文为系列教程第5篇作用域,翻译自Scope

  1. 引言-Introduction
  2. 基础-Basics
  3. 控制器-Controllers
  4. 作用域-Scopes
  5. 集合-Collections
  6. 模块-Modules
  7. 依赖注入-Dependency Injection
  8. 服务-Services
  9. 过滤器-Filters
  10. 指令-Directives
  11. 指令作用域-Directive Scopes
  12. 路由-Routing
  13. 通信-HTTP
  14. 结论

2.正文

在Angular中scope到底是什么?从名字来看,你可能猜测它是表征程序状态的上下文,保护我们不受JS饱受诟病的全局作用域污染。听起来很简单,我们好像可以直接跳到下一章了。

不要太急,本章虽说不长,但是会涉及一些非常重要的内容:scope继承和体系。一个经典的Angular应用可能会创建10几个、上百个甚至近千个scope,这些scope形成一个体系。

在我们更进一步之前,让我们先搭建本章案例的环境。

2.1 setup

基础章,我们学会了通过向html元素添加ng-app指令告诉Angular需要处理那部分文档。

<code><!-- index.html -->
<body ng-app="app">
  <!-- Other examples to be inserted here. -->
</body></code>

ng-app指令的参数就是我们应用的根模块(本案例命名为app,只是为了简单起见)。Angular的模块我们会在下一张详细介绍。现在,我们只需考虑这个启动样板文件(也可以先暂时忽略掉)。

<code>/* module.js */
angular.module('app', []);
angular.module('app').config(['$controllerProvider', function($controllerProvider) {
  $controllerProvider.allowGlobals();
}]);</code>

现在让我们把这些放到一边,来点实用的。

2.2 $scope

上一章,我们学习了如何在$scope引用上附加属性的方式准备模型,让我们重复下练习。

<code>/* name-controller.js */
function NameController($scope) {
  $scope.name = "First";
}</code>

使用ng-controller指令,我们可以在DOM元素环境中调用控制器函数。我们在控制器中指定给scope的任意数据都在p元素内可用。

<code><!-- name-controller.html -->
<p ng-controller="NameController">
  {{name}}
</p></code>

编译结果为: First

我们调用NameController元素之外的元素,不能访问该控制器的scope。我们来测试一下。

<code><!-- name-controller-nested.html -->
<div>
  <p>
    Outside the scope: {{name}}
  </p>
  <div ng-controller="NameController">
    <p>
      Inside the scope: {{name}}
    </p>
  </div>
</div></code>

编译结果为: 
Outside the scope: 
Inside the scope: First

现在我们明白了scope和controller相匹配。接下来,我们来看下相反的情况:只有一个scope的Angular应用。

2.3 $rootScope

为了帮助我们避免scope使用中可能出现的麻烦,Angular为每一个控制器创建一个新的scope。但是,scope是层次结构的,在每个应用scope层次结构最底端的根scope是单一的。我们可以通过在控制器中声明特定的参数$rootScope访问它,最好不要使用普通的$scope引用。

<code>/* root-name-controller.js */
function RootNameController($rootScope) {
  $rootScope.name = "First";
}</code>

看起来有点天真,实际上它可以正常工作。

<code><!-- root-name-controller.html -->
<p ng-controller="RootNameController">
  {{name}}
</p></code>

编译结果为:First.

但是,当我们在另一个控制器里给它指定相同的属性时,就出问题了。

<code>/* second-root-name-controller.js */
function SecondRootNameController($rootScope) {
  $rootScope.name = "Second";
}</code>

这个不对,因为$rootScope在我们的应用中作为单体存在,所以我们只能给它指定一个值。

<code><!-- root-name-controllers.html -->
<p ng-controller="RootNameController">
  {{name}}
</p>
<p ng-controller="SecondRootNameController">
  {{name}}
</p></code>

编译结果为: 
Second 
Second

实际上,我们的SecondRootNameController中指定的name值覆盖了RootNameController中的值,所以这个问题就是全局变量的问题,是吧?

2.4 Isolation(隔离)

通过自动为每个控制器提供自己的作用域,Angular可以为我们提供非常安全的环境。接下去我们使用使用正确的方式重写控制器,弃用$rootscope而使用$scope注意一点,上文我们的$rootscope的案例仅仅是为了演示为什么每一个控制器自动生成一个scope对象。

<code>/* second-name-controller.js */
function SecondNameController($scope) {
  $scope.name = "Second";
}</code>

使用本章开头的NameController和上面的SecondNameController,我们来演示下$scope对象的隔离性。

<code><!-- second-name-controller.html -->
<p ng-controller="NameController">
  {{name}}
</p>
<p ng-controller="SecondNameController">
  {{name}}
</p></code>

编译结果为: 
First 
Second

该案例产生了正确的结果,每个控制器输出了自己的名字。当一个控制器不是另一个控制器的子元素,产生了隔离性(isolation)。如果是嵌套的情况,又会发生什么呢?

2.5 Nesting(嵌套)

我们稍微修改上面的案例,把第二个控制器SecondNameController移到加载NameController的div元素的子元素上,来看看嵌套的时候会发生什么。

<code><!-- nested-second-name-controller.html -->
<div ng-controller="NameController">
  <p>
    {{name}}
  </p>
  <p ng-controller="SecondNameController">
    {{name}}
  </p>
</div></code>

编译结果为: 
First 
Second

依然能够正常工作。如果我们改变两个p的位置会怎么样,不妨尝试一下,输出结果也会翻转一下,是吧。

好像看起来嵌套的控制器依然可以相互隔离,但其实是个误导。实际上,Angular基于DOM中的相对位置组织控制器的层次结构,嵌套的控制器会继承祖先的属性。我们这里没有变化的原因是,子scope的name属性覆盖了父scope的name属性。

让我们改变一下子scope的属性名称,来试试看scope嵌套中的继承特性。

<code>/* child-controller.js */
function ChildController($scope) {
  $scope.childName = "Child";
}</code>

我们在父元素和子元素中分别使用两个属性。

<code><-- child-controller.html -->
<div ng-controller="NameController">
  <p>
    {{name}} and {{childName}}
  </p>
  <p ng-controller="ChildController">
    {{name}} and {{childName}}
  </p>
</div></code>

编译结果为: 
First and 
First and Child

这样就很明了了,父控制器不能访问子控制器的属性,但是子控制器可以访问自己的属性和父控制器的属性。

因为name是继承的属性,好像我们可以在父scope和子scope中同时看到它的变化,事实是不是这样?我们给父控制器绑定个input来测试下。

<code><!-- child-controller-edit.html -->
<div ng-controller="NameController">
  <p>
    {{name}}
  </p>
  <p ng-controller="ChildController">
    {{name}}
  </p>
  <input type='text' ng-model='name'>
</div></code>

编译结果如下动图所示。 
http://image84.360doc.com/DownloadImg/2015/04/2317/52753545_7

如果您尝试编辑name属性,您将会看到期望的结果,如上动图所示。name属性会在两个scope环境内更新。但是,注意我们这里把name绑定在父scope上。

2.6 Inheritance(继承)

您可能觉得我们修改子scope属性会同步更新到父scope中,是这样的吗?让我们测试下,在两个控制器中增加input。

在下面的案例中,先修改头一个input,再修改第二个input,看看结果是不是你想的那样。

<code><!-- child-controller-input.html -->
<div ng-controller="NameController">
  <p>
    name: {{name}}
    <br>
    <input type='text' ng-model='name'>
  </p>
  <p ng-controller="ChildController">
    name: {{name}}
    <br>
    <input type='text' ng-model='name'>
  </p>
</div></code>

编译结果如下动图所示。 
http://image84.360doc.com/DownloadImg/2015/04/2317/52753545_8

Angular使用JS的普通原型继承,某种程度上来说不错,因为我们无需学习新的东西,某种程度上又不好,因为JS的原型继承不太直观。

对JS对象设置属性导致对象该属性的自动生成,对于属性继承来说不是好消息,因为它将覆盖对象自己的属性。

哈!简单的说,如果您没有修改第二个input,子scope中没有name属性。一旦您修改了input,自动在子scope建立name属性,赋input的值,并且阻断对父scope中name属性的访问。

明白了吗?不明白的话,可以稍微休息下,去学习下原型继承。如果明白了,我们继续。

如果我们想在继承的scope中修改模型数据,我们该如何做?

很简单,我们只需要把name属性移动另一个对象上。

<code>/* info-controller.js */
function InfoController($scope) {
  $scope.info = {name: "First"};
}</code>
<code>/* child-info-controller.js */
function ChildInfoController($scope) {
  $scope.info.childName = "Child";
}</code>
<code><!-- info-controller.html -->
<div ng-controller="InfoController">
  <p>
    {{info.name}} and {{info.childName}}
    <br>
    <input type='text' ng-model='info.name'>
  </p>
  <p ng-controller="ChildInfoController">
    {{info.name}} and {{info.childName}}
    <br>
    <input type='text' ng-model='info.name'>
  </p>
</div></code>

编译之后结果动图所示。 
http://image84.360doc.com/DownloadImg/2015/04/2317/52753545_9

注意ChildInfoController依赖于它的父控制器创建info对象。 如果您编辑ChildInfoController的源文件,把函数体替换为$scope.info = {childName: "Second"};会发生什么呢?尝试一下。

<code>function InfoController($scope) {
  $scope.info = {name: "First"};
}
function ChildInfoController($scope) {
  $scope.info = {childName: "Second"};
}</code>
<code><div ng-controller="InfoController">
  <p>
    {{info.name}} and {{info.childName}}
    <br>
    <input type='text' ng-model='info.name'>
  </p>
  <p ng-controller="ChildInfoController">
    {{info.name}} and {{info.childName}}
    <br>
    <input type='text' ng-model='info.name'>
  </p>
</div></code>

http://image84.360doc.com/DownloadImg/2015/04/2317/52753545_10

2.7 scope.$watch

大部分时候,Angular的双向绑定可以像您期望的那样完成交互: 当您使用input发生改变时,UI也会同步的发生更改。但是,计算属性(指从其它scope数据中拿到的数据),例如下面案例中所示的sum就是一个计算属性,不是那么一回事儿。

<code>/* sum-controller.js */
function SumController($scope) {
  $scope.values = [1,2];
  $scope.newValue = 1;
  $scope.add = function() {
    $scope.values.push(parseInt($scope.newValue));
  };

  // Broken -- doesn't trigger UI update
  $scope.sum = $scope.values.reduce(function(a, b) {
    return a + b;
  });
}</code>

本例的模板文件中,如下所示,我们使用select表单让用户选择数据(1,2,3)添加到数组的末尾。(顺便说一句,控制器给新的值提供了初始值,否则Angular应该为select元素添加空白option以避免武断地给新值赋予第一个option。这个行为对scope用处不大,但是我们有必要了解一下)

<code><!-- sum-controller.html -->
<p ng-controller="SumController">
  <select ng-model="newValue" ng-options="n for n in [1,2,3]"></select>
  <input type="button" value="Add" ng-click="add()">
  The sum of {{values}} is {{sum}}.
</p></code>

http://image84.360doc.com/DownloadImg/2015/04/2317/52753545_11

单击add按钮可以把数字添加到数组里去,但是,悲催的是,没有正确的反应到sum里去。

让我们来做点修正,把计算值得部分移到一个回调函数里去。将这个回调函数(具备一个watchExpression参数,本案例就是要被计算的属性)作为参数传递给$scope.$watch,当属性发生改变时调用计算函数求和。

<code>/* sum-watch-controller.js */
function SumController($scope) {
  $scope.values = [1,2];
  $scope.newValue = 1;
  $scope.add = function() {
    $scope.values.push(parseInt($scope.newValue));
  };
  $scope.$watch('values', function () {
    $scope.sum = $scope.values.reduce(function(a, b) {
      return a + b;
    });
  }, true);
}</code>

http://image84.360doc.com/DownloadImg/2015/04/2317/52753545_12 
可以看到,求和的函数就会随着变量的变化发生作用了。

2.8 scope.$apply

Angular内置的双向绑定指令已经很牛了,但是,我们也经常时不时的有些需要添加的行为。比如,如果您想让用户通过esc键同时清除文本框当前状态和scope中的绑定状态。我们应该如何编写这个自定义事件?

<code><!-- escape-controller.html -->
<div ng-controller="EscapeController">
  <input type="text" ng-model="message">
  is bound to
  "<strong ng-bind="message"></strong>".
  Press <code>esc</code> to clear it!
</div></code>

首先,我们需要在控制器中声明一个特定名字的参数$element,以便于Angular建立一个关联DOM的引用。使用给定元素的bind方法,我们可以注册一个回调函数,侦听keyup事件。在该回调函数中,我们更新scope属性。简单吧,我们试一下,试着输入点东西,然后按esc键。

<code>/* escape-controller.js */
function EscapeController($scope, $element) {
  $scope.message = '';
  $element.bind('keyup', function (event) {
    if (event.keyCode === 27) { // esc key

      // Broken -- doesn't trigger UI update
      $scope.message = '';
    }
  });
}</code>

没有正常工作吧。因为我们这里使用Dom,我们需要告诉Angular什么时候重新渲染视图。我们把需要改变的内容打包成一个回调函数传递给$scope.$apply

<code>/* escape-apply-controller.js */
function EscapeController($scope, $element) {
  $scope.message = '';
  $element.bind('keyup', function (event) {
    if (event.keyCode === 27) { // esc key

      $scope.$apply(function() {
        $scope.message = '';
      });
    }
  });
}</code>

http://image84.360doc.com/DownloadImg/2015/04/2317/52753545_13

这样就可以正常工作了吧。

2.9 结论

如果您试图让Angular适应于MVC模式,scopes会是一个难题。刚开始非常简单,Scopes是模型层的一部分,在Angular中,一个对象直到能够作为scope的属性可以访问时,才成为模型。但是,当您研究scopes如何通过控制器或者指令绑定到DOM时,这些东西将变得很有意思(我们后头再说)。幸运的是,排除这些学术问题外,如上面案例所示,scopes非常直观、简单易用。

3.声明

前端开发whqet,关注前端开发,分享相关资源。csdn专家博客,王海庆希望能对您有所帮助,限于作者水平有限,出错难免,欢迎拍砖! 
欢迎任何形式的转载,烦请注明装载,保留本段文字。 
本文原文链接,http://blog.csdn.net/whqet/article/details/44925881 
欢迎大家访问独立博客http://whqet.github.io

作者:whqet 发表于2015/4/14 16:54:43 原文链接

阅读:2599 评论:12 查看评论

04控制器-AngularJS基础教程

0. 目录


1. 前言

AngularJS是为了克服HTML在构建应用上的不足而设计的。HTML是一门很好的为静态文本展示设计的声明式语言,但要构建WEB应用的话它就显得乏力了,所以AngularJS做了一些工作来来解决静态网页技术在构建动态应用上的不足。

AngularJS的初衷是为了简化web APP开发,精髓是简单。但是国内外有很多AngularJS的教程,都着重讲AngularJS的强大之处,从而增加了AngularJS学习的难度,本教程试图用通俗的语言讲解最为基础最为实用的内容,简化学习进程、降低学习难度是本教程的初衷。

本系列教程以翻译Chris SmithAngualr Basics为梗概,融合博主自己的理解,为大家提供一个简单明了的学习教程。

本文为系列教程第4篇控制器,翻译自Controllers

  1. 引言-Introduction
  2. 基础-Basics
  3. 控制器-Controllers
  4. 作用域-Scopes
  5. 集合-Collections
  6. 模块-Modules
  7. 依赖注入-Dependency Injection
  8. 服务-Services
  9. 过滤器-Filters
  10. 指令-Directives
  11. 指令作用域-Directive Scopes
  12. 路由-Routing
  13. 通信-HTTP
  14. 结论

2.正文

在前一章中,我让您勒住缰绳,只把Angular作为HTML的功能扩展,当然Angular远不止这些,事实上通过Javascript实现自定义行为是每个Angular项目的必要部分。如果您在前一章中抱有编程的冲动,你的耐心应该得到嘉奖,现在该写点Javascript了。

最常见的给Angular view(视图)增加控制的方法是通过控制器(controller),最简单的书写控制器的方法是通过书写构造函数的方式。为了帮助大家理解整个过程,我们通过一个非常简单的控制器来演示一下(非常简单,甚至连hello world也没有,就是个空的controller)。

下面是我们的控制器(controller)。

<code>/* empty-controller.js */
function EmptyController() {

};</code>

当然,我们必须把angular类库导入进来,同时利用ng-app命令告诉html,我们将在这里使用angular。

<code><!-- index.html -->
<!-- 导入类库 -->
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.js"></script>
<!--google被墙,大家可以用国内的cdn-->
<script src="//apps.bdimg.com/libs/angular.js/1.2.16/angular.min.js"></script>
<!-- ng-app告诉html,我们将使用angular -->
<body ng-app="app">
  <!-- Other examples to be inserted here. -->
</body></code>

控制器可以在全局范围内使用构造函数的方式进行定义,本章就是使用这种方式。在Angular1.3以前版本中可以直接使用简单方式,不过现在需要进行一些配置,创建一个命名的应用模块(named application module)。如何使用Angular模块我们会在接下来的模块、依赖注入和服务章节中详细讲解,现在,你只需要使用下面的案例作为样板文件(boilerplate)即可。

<code>/* module.js */
angular.module('app', []);
angular.module('app').config(['$controllerProvider', function($controllerProvider) {
  $controllerProvider.allowGlobals();
}]);</code>

现在我们先不管这些,我们来试试什么都不干的控制器(noop controller)。

2.1 ng-controller

像通常那样,使用指令(directive)可以搞定这些,使用ng-controller指令可以通过它的名字找到并调用控制器函数。

<code><!-- empty-controller.html -->
<p ng-controller="EmptyController">

</p></code>

当然,正如您所想,它将调用我们上面定义的那个EmptyController构造函数,nothing to do。接下来,我们来研究下什么是控制器?如何使用?

2.2 构建模型-Constructing the model

在官方指南的核心概览部分把Angular的工作描述为“将变量和函数功能传递给表达式和指令”,这里的函数功能指回调函数,我们稍候重点讲解,现在先来了解一下变量初始化模型准备

明白Angular模型就是表达式作用域可以读取的一些普通Javascript代码之后,准备模型就是小意思了。普通javascript代码?是的,我们可以书写普通js代码,让我们给空的控制器增加一个string属性。

<code>/* message-controller-as.js */
function MessageController() {
  this.message = "This is a model.";
}</code>

简单吧!成功了吗?我们已经成功的创建了一个模型?是也不是?

答案是几乎。当我们在试图作用域中获得message属性之后,它就是一个模型了。完成这个任务的一种方法,就是让整个控制器作为scope中的一个属性,听起来很玄乎吧,当你了解了语法的秘密,一切将变得非常简单。

2.3 控制器作为属性名-Controller as propertyName

正像官方API描述的那样,ng-controller可以接受controll as propertyName表达式作为参数,注意只有Angular1.2以后版本才可使用本特性。

<code><!-- message-controller.html -->
<p ng-controller="MessageController as controller">
  {{controller.message}}
</p></code>

编译结果为:This is a model.

这样,我们就是用controller准备好了模型数据。

尽管这种给引用附加属性的方式直接明了,但是“把整个controller暴露给视图”容易给人误解,让人感觉控制器是模型的一部分,而实际上,控制器的作用仅仅是为模型准备数据。当你仅仅满足客户端需求,不考虑更多需求时,测试和调试都会非常简单。而且,这种方式会为视图代码带来噪声,因为所有的属性必须通过控制器的引用访问。视图代码通常是一个web应用噪声要求最苛刻的区域,它可能是一瞥之后出现理解困难的第一个地方。最后,控制器(controller)和作用域(scope)并进工作,从教学角度来说,明晰scope的引用并精细控制它,会非常有用,通过这些控制,我们可以明确地暴露我们想要暴露给视图的部分。因此,本书更倾向于这种能够明确管理scope对象的控制器样式。

那么问题来了,我们如何获得这个scope对象的引用?使用new运算符传建一个?还是从某个地方请求?

2.4 依赖注入-Dependency injection

不是的。我们通过依赖注入获得它。你可能已经注意到我并没有展示实例化控制器的代码,例如var controller=new MessageController();那么,谁创建了视图中使用的控制器?

当然是Angular。Angular是一个控制反转(Inversion of Control,英文缩写为IoC)容器,可以管理应用组件的生存周期。当需要一个新的控制器或者新的组件时,Angular会创建一个。这可以节省时间,而且更重要的是,可以让Angular给组件注入资源、依赖性等。

2.4.1 $scope

作为一个通过依赖注入框架管理的组件,控制器只需要通过一个参数$scope即可。参数的名字非常关键,实际上,如果你压缩了你的Javascript源代码,你会破坏依赖注入机制(无须担心,有一个变通方案,我们会在依赖注入章节详解)。

<code>/* message-controller-scope.js */
function MessageController($scope) {
  $scope.message = "This is a model.";
}</code>

通过给构造函数增加$scope参数,我们通知Angualr我们需要一个视图作用域的引用。可以再简单一些吗?现在我们可以不在控制器里声明message属性,而是直接在scope中定义它。

在模板中,我们从ng-controller指令中移除as controllercontroller.message变成message,这个是我们附加到scope的唯一的属性。

<code><!-- message-controller-scope.html -->
<p ng-controller="MessageController">
  {{message}}
</p></code>

编译结果为:This is a model.

尽管两种方式都可以正常工作,本书更多使用指定明确的$scope的方式。依赖注入是Angualr的特色,所以我们要尽可能的熟悉它。

2.5 模型视图控制器MVC-Model-view-controller

官方指南的控制器定义中也涉及到向视图暴露”功能性”。在我们讨论这个问题之前,我觉得有必要花点时间讨论下Angular控制器与经典MVC模式的不同。

根据维基百科model-view-controller (MVC)的解释,控制器“mediates input, converting it to commands for the model or view”。但是很难在Angular范围内理解“commands for the model or view”,因为Angular的MVC模型非常简单,采用仅有规则没有逻辑的贫血模型。截至目前,Angular最流行的方法是,把所有的业务逻辑(域逻辑)放到控制器里,换句话说,Angular应用趋向于瘦模型和胖控制器( skinny models and fat controllers)。

如果您熟悉模式,例如富域模型(rich domain model)、瘦模型胖控制器(Skinny Controller, Fat Model)等,我想你会发现Angular的方法有点倒退。我想这个只可能是优先级问题,在客户端编程中,两者之间存在着显著差异,一边是声明式的、DOM为中心的视图代码,另一边是命令式的、数据驱动的JS代码来处理业务逻辑和应用程序基础。这是巨大的成功,我很高兴类似于Angular之类的框架关注这些。一段时间之后,业务逻辑从控制器中分离开来可能占有更高的优先级,我们可能看到富模型(richer models)的趋势。

2.6 函数-Functions

让我们书写一个包含简单函数的控制器,体验下在视图中调用该函数。你可能会想起,在上一章中我们说过,Angular不允许在视图中声明函数。

下面的控制器把一个函数指定给scope的一个属性。

<code>/* count-controller.js */
function CountController($scope) {
  $scope.count = function() { return 12; }
}</code>

表达式里调用函数方法和普通js代码没有区别,使用{{ }}

<code><!--count-controller.html-->
<p ng-controller="CountController">
  There are {{count()}} months in a year.
</p></code>

编译结果为: There are 12 months in a year.

注意,函数不是简单地调用和忘记,而是绑定在模型上。 什么意思? 你Angular的绑定特性(你可能会逐步举得理所当然),意味着Angular不但会在视图第一次渲染时调用该函数,而且会在相关模型改变时调用。为了让大家明白工作原理,我们需要一个使用模型数据的函数,下面的求和函数使用两个scope中声明的模型属性,operand1operand2。Angular将会在任意属性发生改变时调用函数和渲染结果。

<code>/* addition-controller.js */
function AdditionController($scope) {
  $scope.operand1 = 0;
  $scope.operand2 = 0;
  $scope.add = function() {
    return $scope.operand1 + $scope.operand2;
  }
  $scope.options = [0,1,2,3,4];
}</code>

因为我们已经多次使用input指令,这次让我们用Angular的select指令改变模型数据。您可能已经注意到,上面代码的最后一行的$scope.options,我们接下来使用ng-options指令把数组数据放置到select里面。x for x in options这个写法感觉有点废话,无论如何记住它吧()。

<code><!-- addition-controller.html -->
<p ng-controller="AdditionController">
  <select ng-model="operand1" ng-options="x for x in options"></select>
  + <select ng-model="operand2" ng-options="x for x in options"></select>
  = {{add()}}
</p></code>

编译之后结果如下面动图所示。 
http://image84.360doc.com/DownloadImg/2015/04/2317/52753545_14

不错吧!但是,如果你不想使用operand1和operand2而想使用属性,我们可以在泛化性上做些改善。摆脱您的重构手册、撸起袖子进行一些参数抽象。

<code>/* addition-controller-params.js */
function AdditionController($scope) {
  $scope.number = 2;
  $scope.add = function(operand1, operand2) {
    return operand1 + operand2;
  }
}</code>

在表达式内部,函数参数可以是属性,也可以是字面值。

<code><!-- addition-controller-params.html -->
<p ng-controller="AdditionController">
  {{add(number, 2)}} is not the same as {{add(number, "2")}}
  <br>
  2 + 2 + 2 + 2 = {{add(2, add(2, add(2, 2)))}}
</p></code>

编译结果为: 
4 is not the same as 22 
2 + 2 + 2 + 2 = 8

接下来,我们通过一个回调函数来处理用户行为。

2.7 回调函数

上一章曾经有一个案例,利用ng-click切换boolean值,在ng-init指令中初始化authorized,利用ng-click="authorized = !authorized实现简单、行内回调,让我们把该案例做个改进,把模型初始化、切换logic变量都放到控制器里去。

<code>/* auth-controller.js */
function AuthController($scope) {
  $scope.authorized = true;
  $scope.toggle = function() {
    $scope.authorized = !$scope.authorized
  };
}</code>

现在toggle是一个作用域中的函数,ng-click的参数看起来像个函数调用toggle()。 它不是真正的函数调用, 只是一个用户单击时会执行的字符串而已。

<code><!-- auth-controller.html -->
<div ng-controller="AuthController">
  <p>
    The secret code is
    <span ng-show="authorized">0123</span>
    <span ng-hide="authorized">not for you to see</span>
  </p>
  <input class="btn btn-xs btn-default" type="button" value="toggle" ng-click="toggle()">
</div></code>

编译结果如下动图所示。 
http://image84.360doc.com/DownloadImg/2015/04/2317/52753545_15

案例依然正常工作,而且在一个更好的地方实现了逻辑切换。为什么更好呢?一个很好的问题。在控制器内处理用户行为的好处在于,当我们实现更为复杂的用户行为时可以保证代码清晰可读,例如处理来自远程服务器的异步模型数据(如果您迫不及待的想了解这部分知识,请跳至本书的http部分)。

2.8 结论

我们在Angular的使用中引入了javascript,控制器(controller)在MVC模式中扮演者为视图准备数据的功能,它通过在scope对象上声明属性,实现该功能。下一章中,我们将详细介绍scope对象,了解它如何组织成一个体系与应用的DOM结构匹配。

3.声明

前端开发whqet,关注前端开发,分享相关资源。csdn专家博客,王海庆希望能对您有所帮助,限于作者水平有限,出错难免,欢迎拍砖! 
欢迎任何形式的转载,烦请注明装载,保留本段文字。 
本文原文链接,http://blog.csdn.net/whqet/article/details/44698845 
欢迎大家访问独立博客http://whqet.github.io

作者:whqet 发表于2015/4/7 19:10:27 原文链接

阅读:3517 评论:12 查看评论

03基础-AngularJS基础教程

0. 目录


1. 前言

AngularJS是为了克服HTML在构建应用上的不足而设计的。HTML是一门很好的为静态文本展示设计的声明式语言,但要构建WEB应用的话它就显得乏力了,所以AngularJS做了一些工作来来解决静态网页技术在构建动态应用上的不足。

AngularJS的初衷是为了简化web APP开发,精髓是简单。但是国内外有很多AngularJS的教程,都着重讲AngularJS的强大之处,从而增加了AngularJS学习的难度,本教程试图用通俗的语言讲解最为基础最为实用的内容,简化学习进程、降低学习难度是本教程的初衷。

本系列教程以翻译Chris SmithAngualr Basics为梗概,融合博主自己的理解,为大家提供一个简单明了的学习教程。

本文为系列教程第3篇基础,翻译自Basics

  1. 引言-Introduction
  2. 基础-Basics
  3. 控制器-Controllers
  4. 作用域-Scopes
  5. 集合-Collections
  6. 模块-Modules
  7. 依赖注入-Dependency Injection
  8. 服务-Services
  9. 过滤器-Filters
  10. 指令-Directives
  11. 指令作用域-Directive Scopes
  12. 路由-Routing
  13. 通信-HTTP
  14. 结论

2.正文

AngularJS是一个通过增强HTML实现客户端web应用的框架。

我们来充分解读下。如果您是一个有经验的web开发者,您很可能拿Angular和其他熟悉的javascript框架(库)做个对比,诸如jQuery、Knockout、Backbone、Ember甚至是React;同样如果您了解GUI编程,您会从Angular想到MVC(Model View Controller)、MVVM(Model View ViewModel);这些想法极为自然,但是会影响到对Angular的理解。仅对本章而言,我建议您暂时不去深层理解它的工作原理,仅将Angular当做一个Javascript框架,将之当做对HTML的功能增强。

我们将讲解Angular的三个基础知识,表达式、指令、作用域。但是在进入具体的案例之前,让我们简单了解下如何让Angular在页面中工作(牛X的童鞋请阅读表达式部分)。

2.1 Set up

从哪里可以得到Angular?除了官网下载之外,从 Google Hosted Libraries使用CDN方式加载既方便又便捷(当然,老外没有被墙过,不接地气),本页正是使用script标签引入CDN的方式实现的。google被墙,国内程序员可以使用百度静态资源公共库的CDN。

<code><script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.js"></script>
<!-- or -->
<script src="//apps.bdimg.com/libs/angular.js/1.2.16/angular.min.js"></script></code>

引入AngularJS之后,您需要告诉Angular需要管理哪部分HTML。记住Angular是面向html的而不是面向javascript的,所以我们使用非标准的的html供Angular识别(给DOM中的任何元素添加ng-app属性)而不是书写一些javascript语句去加载它。在本章中,我们在body元素上添加该属性(我们在html或body元素上添加属性可以让Angular应用控制整个页面),如下面代码所示。

<code><body ng-app>
  <!-- Other examples in this chapter will be inserted here. -->
</body></code>

如果您愿意,可以选择一个较窄的作用域,允许您同时使用其他框架,加载其它Angular应用。相对于单页面应用(SPA),这种情况更适用于传统的文档中心网站客户端代码。

使用这个神奇的属性,我们就能让Angular工作了,接下来需要做什么呢?

2.2 表达式

注意:如果您接受过不唐突的Javascript训练,下面的代码也许会敲响警钟,因为Angular的第一条原则是允许在html中混合使用JS样式的表达式。Angular中可以使用简单、正确的JS,不允许使用类似于循环等的控制结构。在我们做这些古老的、流行的实验时,暂不考虑是否唐突的问题。

让我们来相加两个数。像本书的其他案例一样,编辑器里面的代码可以修改,然后可以看到相应的结果(博客技术角度考虑,本教程没有实现该功能,需要实验的同学请移步原教程)。

<code><p>The number {{3 + 4}}.</p></code>

结果为:The number 7.

再进一步,如果你修改3+4为新的数学表达式,Angular也会计算结果。看看有没有Angular不能计算的,如果输入的不是一个正确的表达式,它要不直接输出字符串,要不什么都不显示(出错的情况下),如下面的动图所示。 
http://image84.360doc.com/DownloadImg/2015/04/2317/52753545_16 
成对的大括号是模板界定符,如果您了解Mustache或Handlebars,这种方式就不陌生。有人把AngularJS当做一种精密的模板库,当页面加载的时候把ng-app标识的元素作为一个模板,当数据改变时重新渲染(不要担心不熟悉模板,我们会在后面简要介绍)。

我们可以判断两个值相等吗?当然,请看下面代码。

<code><p>Are strings and numbers equal? {{'2' == 2}}</p></code>

结果为:Are strings and numbers equal? true

对于javascript来说,结果是正确的。如果您有所怀疑,你可以参考javascript comparison operators,然后尝试将”==”修改成”===”看看结果(严格比较更好,是吧)。我们同样截个动图,请看下图。 
http://image84.360doc.com/DownloadImg/2015/04/2317/52753545_17

下面的案例演示字符串连接JS标准方法的调用(例如String的toUpperCase方法)。

<code><p>{{"Angular" + "js".toUpperCase()}}</p></code>

结果为:AngularJS

你是不是感觉我们可以在表达式中作任何事情,不一定哟,伙计。

<code><!-- 错误代码! 表达式中不允许出现函数调用 -->
<p>{{alert("Hello world")}}</p></code>

不能在表达式中使用alert,同样不能使用javascript的全局对象,例如MathNumberDate等等。请试着把alert("Hello world")替换成parseInt("1")、 Date.now()、 Number.NaN、 Math.random()等看看结果,AngularJS拒绝解析这些表达式,如下动图所示。 
http://image84.360doc.com/DownloadImg/2015/04/2317/52753545_18

尽管Angular表达式有局限性,但我猜你会兴奋的混合code和html,直到你感觉到框架的种种限制。让我们时刻注意这些限制。

你觉得可以定义变量吗?

<code><p>{{a = 1; a + a}}</p></code>

结果为:2 
可以正常工作,但是如果我们在变量名前加上var关键字,产生了一个错误吧?如果输出中包含了原始的表达式,并且输出了界定符“{{ …… }}”,说明发生了一个错误。有点失望吧,别担心,我们有收获。其实试误是一个很好的学习方法。 
http://image84.360doc.com/DownloadImg/2015/04/2317/52753545_19

实验同样是一个不错的学习方法,我不知道一个界定符中定义的变量能不能用在其他界定符中,为什么不试试呢?

<code><p>{{a = 1}} remains {{a}}</p></code>

结果为:1 remains 1 
yeah,没问题。如果我们把变量初始化放到第二个表达式中可以吗?试试看?原因又是什么? 
http://image84.360doc.com/DownloadImg/2015/04/2317/52753545_20

表达式支持三元运算符吗?我们试试看。

<code><p>There {{count = 1; (count == 1 ? "is " : "are ") + count}} of them.</p></code>

结果为:There is 1 of them. 
幸运的是三元运算符可以正常工作,三元运算符的简明语法将在模板中非常有用。

那么,自增自减可以吗?

<code><p>{{a = 1; ++a}}</p></code>

结果为:{{a = 1; ++a}} 
看来不行(自增自减)。好吧,我们必须得在我们的表达式中去掉这些失效的元素,然后for循环可以吗?

<code><p>{{for (i = 0; i < 10; i = i + 1) {i}}}</p></code>

同样不行(for循环),并且在控制台中产生了一个语法错误,这个语句作为javascript语句没有错误,但是作为表达式产生了错误。所以,我们悟到了一些界限:Angular表达式不是Javascript,它不支持条件语句、循环、抛出错误

Angular表达式我们应该已经讲得比较充分了,就我个人而言,并不排斥在标记中适度使用简明、视图相关的javascript代码,因此我喜欢Angular 表达式的部分许可特性。但是模板中过多的javascript代码容易造成混乱,表达式仅仅在Angular中担任支持的角色,所以不要过多使用javascript。

AngularJS真正令人兴奋的关键是指令(directive)。

2.3 指令

指令是Angular的核心和精髓,我强烈建议你把指令认作自定义HTML,大部分时候,指令以正常的html标签的属性出现。内置指令以”ng-“作为前缀,现在也有很多第三方指令,当您看到表达式中冗长的javascript代码时,“一定有相应的指令可以做到这个”应该成为您的第一反应。

其实,我们已经使用过一个指令了,还记得吗? 当我们想让我们的代码动态展示的时候,我们在body标签上添加了一个属性ng-app,我们没有为这个指令指定任何参数,如果需要指定参数,需要在指令后面跟等号和引号,像普通的html属性那样。例如,`ng-app=”myApp”(应用名称实际就是模块module名称,模块是Angular架构中非常重要的一个部分,请参阅本教程的第7部分)。

ng-bind

一些指令可以接受解析字符串表达式作为参数(你可以从AngularJS API中获取查询内置指令的参数),例如ng-bind指令仅仅像前面“{{ }}”表达式一样解析表达式,恰如下列代码所示。

<code><p>The number <span ng-bind="3 + 4"></span>.</p></code>

结果为:The number 7.

运行结果和上面的第一个案例结果一致。使用ng-bind有一个重要的不同,应用ng-bind的元素内容会在模板渲染时重置。因此要**优先使用**ng-bind,但是需要确保该元素内容为空(在angular替换原始html之前保证内容为空)。依赖于用户环境、您的Angular应用的大小和行为,可能会产生一些延迟导致用户看到双大括号界定符(”{{……}}”)或编译前的表达式,在空元素上使用ng-bind可以避免这个问题。

随着您的应用日益庞大,您可能需要通过加载完毕前隐藏信息来较高水平的解决这些闪烁问题, ng-cloak指令(碰墙,看这个)可以轻松搞定。明白“一定有个指令可以搞定这些”是多么好的习惯了吧?

ng-init

还记得如何在表达式中初始化变量吗?一定有个指令也可以初始化变量吧?是的,就是ng-init,您可以在绑定ng-init的元素内部任何地方初始化变量。

<code><div ng-init="sum = 3 + 2">
  <p>
    {{sum + 1}} is more than
    <span ng-bind='sum - 1'></span>
  </p>
</div></code>

结果为:6 is more than 4

使用分号分隔,您可以声明初始化多个变量

<code><div ng-init="count = 7; units = 'days'; collection = 'week'">
  <p>
    There are {{count}} {{units}} in a {{collection}}.
  </p>
</div></code>

结果为:There are 7 days in a week.

有些时候,您可能觉得使用一个对象的属性组织变量更好。

<code><div ng-init="time = {count: 12, units: 'months', collection: 'year'}">
  <p>
    There are {{time.count}} {{time.units}} in a {{time.collection}}.
  </p>
</div></code>

结果为:There are 12 months in a year.

数组也非常的有用。

<code><div ng-init="months = ['January','February','March','April']">
  <p>
    {{months[3]}} follows {{months[2]}} in {{months}}.
  </p>
</div></code>

结果为:April follows March in [“January”,”February”,”March”,”April”].

编写Angular模板时,仔细核对属性名称(be careful about property names)非常重要。出现问题时,您只能自己定位问题所在,而别想从Angular哪里获得任何帮助,它只是默默的容忍所有的属性访问错误,包括不存在父对象的嵌套属性、数组出界访问等。

<code>expression-property-failures.html
<div ng-init="paragraph = {sentence: {words: ['first','second','third']}}">
  <p>
    "{{typo.sentence.words[0]}}",
    "{{paragraph.typo.words[1]}}",
    "{{paragraph.sentence.typo[2]}}",
    "{{paragraph.sentence.words[3]}}".
  </p>
</div></code>

结果为:”“, “”, “”, “”.

就我个人而言,我希望Angular放弃这种容忍,借鉴CoffeeScript的精彩简介的存在操作符(existential operator -?)把这个静默访问错误作为一个选项而非一个规则。

截止到目前为止,你觉得可以在ng-init中定义函数吗?猜猜看?

<code><div ng-init="count = function() { return 12; }">
  <p>
    There are {{count()}} months in a year.
  </p>
</div></code>

结果为:There are months in a year.

显然,函数表达式不支持。在试图给ng-init传递参数时,Angular抛出了一个$parse:syntax错误(浏览器的Javascript控制台可以看到)。声明表达式中可用函数的正确的地方是控制器(controller,我们会在后续章节里进行学习)。实际上,控制器是为视图准备数据的最好的地方。我们在本章开心学习的ng-init指令,实际上是为了重叠变量(aliasing variables)以应付变量阴影(variable shadowing),我们会在集合(Collections )章节进行学习。

ng-non-bindable

顺便说一句,如果你需要在从AngularJS的处理文档中屏蔽一部分,可以使用ng-non-bindable编译时屏蔽该元素。

<code><p ng-non-bindable>
  {{2 * 2}} is the same as <span ng-bind='2 + 2'>?</span>
</p></code>

结果为:{{2 * 2}} is the same as ? 
如果去掉ng-non-bindable属性会发生什么?请看动图。 
http://image84.360doc.com/DownloadImg/2015/04/2317/52753545_21

ng-show

是时候跳出简单渲染表达式,上点难度来点特别的。对于初学者,我们如何实现根据条件显示、隐藏网页元素呢?

给大家分享我首次使用Angular实现隐藏显示元素的搞笑经历。第一次使用Angular时,我需要在用户成功提交表单后隐藏该表单。在处理用户行为的控制器里,我添加了jQuery.hide(),当然可以起到效果。“一定有个指令可以搞定这些”,是的,实际上有两个ng-hide和ng-show。

<code><p ng-init="authorized = true">
  The secret code is
  <span ng-show="authorized">0123</span>
  <span ng-hide="authorized">not for you to see</span>
</p></code>

结果为:The secret code is 0123

把true改为false,看看会发生什么。因为Angular随时侦听变化,您可以在任何地方切换authorized值,视图将为用户进行同步更新。 
http://image84.360doc.com/DownloadImg/2015/04/2317/52753545_22

此时,我们可以在变量、变量如何工作方面更进一步了。

2.4 作用域

Scopes在您的应用里提供了唯一正确信息源(single source of truth)。无论您在视图的什么地方展示数据,都应该一个统一的地方(scope)改变数据(管理数据),您做出的任何改变都会迅速的反馈到整个视图中去。

这种自动渲染、重新渲染需要技术基础,就是在scopes中使用的看起来非常普通的javascript对象。您可能熟悉简单域模型对象(plain old domain model objects)的概念,它来源于POJO(Plain Old Java Object),当Martin Fowler第一次阐释POJO的概念时,相对于可以从超类中继承特殊能力的庞大的域模型对象,他更倾向于语言运行时直接提供的简单、普通的对象。而且,概念的意义进行了扩展,看似简单,功能却在特殊运行时能力、经典的持久力方面进行了增强。

附加到Angular scopes中的对象时Javascript POJOs,因为Angular使用运行时精密变动检测以便能够传送改变。当Object.observer语言特性成为主流javascript技术时,Angular实际上成为一种plain old javascript 框架。

在本章中,我们已经用到过scope属性。还记得上面的使用ng-init初始化变量的案例吗,就是使用了scope。还记得我让你在表达式中声明变量前使用var关键字吗?使用var发生错误,主要是因为我之前调用的变量实际上scope对象的一个属性。下一章将会深度解析scope,现在让我们亲手做些案例体会一下如何使用scope属性。

双向绑定(Two-way binding)

截止到目前,我们已经看到过很多单向绑定的案例,scope属性动态更新到视图中。当你利用html修改数据,这些数据同步更新到视图中,这就叫双向绑定。

第一个案例,我们使用ng-click指令动态修改一个boolean值。

<code><p ng-init="authorized = true">
  The secret code is
  <span ng-show="authorized">0123</span>
  <span ng-hide="authorized">not for you to see</span>
  <br>
  <input type="button" value="toggle" ng-click="authorized = !authorized">
</p></code>

当我们单击按钮时,authorized的值将发生改变,同时根据authorized值的变化视图也发生改变,如下动图所示。 
http://image84.360doc.com/DownloadImg/2015/04/2317/52753545_23 
ng-click的参数可以是任何表达式,尽管它是一个多才多艺的指令,非常简单的实现翻转变量。但是如果我们需要绑定表单和数据呢?

ng-model

几乎每一个教程讲解双向绑定时都会举例“输入框绑定字符串”,现在轮到我们了。

<code><input type="text" ng-model="title">
is bound to
"<strong ng-bind="title"></strong>".
Type something in the box!</code>

运行结果我们还是搞个动图。 
http://image84.360doc.com/DownloadImg/2015/04/2317/52753545_24

再让我们看看其他类型的表单,如何表现。

<code><input type='checkbox' ng-init='optIn = true' ng-model='optIn'>
is bound to
<strong ng-bind="optIn"></strong>.
Change it!</code>

http://image84.360doc.com/DownloadImg/2015/04/2317/52753545_25

如果您使用ng-true-value和ng-false-value,您可以把字符串绑定到checkbox。

<code><input type='checkbox' ng-init='feeling = "love it"' ng-model='feeling'
    ng-true-value='"love it"' ng-false-value='"hate it"'>
is bound to
"<strong ng-bind="feeling"></strong>".
Change it!</code>

结果恰如动图所示。 
http://image84.360doc.com/DownloadImg/2015/04/2317/52753545_26

下拉列表select的案例,如下所示。

<code><select ng-model='answer'>
  <option value='yes' selected>Yes</option>
  <option value='no'>No</option>
  <option value='maybe'>Maybe</option>
</select>
is bound to
"<strong ng-bind="answer"></strong>".
Change it!</code>

http://image84.360doc.com/DownloadImg/2015/04/2317/52753545_27 
如果改成单选按钮(radio)也是可以的。 
http://image84.360doc.com/DownloadImg/2015/04/2317/52753545_28

现在您已经拥有了利用指令操作scope数据的实战经验,Angular拥有许许多多的内置的指令,我们会在接下来的章节中进行逐步学习。

2.5 总结

在本章开始的时候,我建议您不要将Angular看做JS框架,而是把它作为HTML的扩展。正如Angular之父Mi?ko Hevery在一次访谈中所言,Angular初衷是帮助前端开发者快速、有效地增强web页面功能。

Angular采用声明式语言格式以更好地适应快速GUI开发,基于开发者熟知的HTML构建Angular,没有在另辟蹊径(启用新的DSL,加重开发者负担)。

至少是现在,离开自定义的Javascript就没有Angular,现实中的Angular应用需要利用控制器、服务、路由、模块、依赖注入、Ajax基础,所有这些都会在本书中有所涉及。但是最好的方式还是,开发者主要在声明空间工作,坚信“一定有个指令可以搞定这个”,书写自定义指令是一个颇有挑战性的工作,这里稍微提醒下大家。

现在明白了,我们的终极目标是有效地、有力地扩展HTML,而不是在一个框架内书写JS代码,您准备好深入细节了吗?

3.声明

前端开发whqet,关注前端开发,分享相关资源。csdn专家博客,王海庆希望能对您有所帮助,限于作者水平有限,出错难免,欢迎拍砖! 
欢迎任何形式的转载,烦请注明装载,保留本段文字。 
本文原文链接,http://blog.csdn.net/whqet/article/details/44648511 
欢迎大家访问独立博客http://whqet.github.io

作者:whqet 发表于2015/3/28 9:29:19 原文链接

阅读:3885 评论:22 查看评论

02引言-AngularJS基础教程

0. 目录


1. 前言

AngularJS是为了克服HTML在构建应用上的不足而设计的。HTML是一门很好的为静态文本展示设计的声明式语言,但要构建WEB应用的话它就显得乏力了,所以AngularJS做了一些工作来来解决静态网页技术在构建动态应用上的不足。

AngularJS的初衷是为了简化web APP开发,精髓是简单。但是国内外有很多AngularJS的教程,都着重讲AngularJS的强大之处,从而增加了AngularJS学习的难度,本教程试图用通俗的语言讲解最为基础最为实用的内容,简化学习进程、降低学习难度是本教程的初衷。

本系列教程以翻译Chris SmithAngualr Basics为梗概,融合博主自己的理解,为大家提供一个简单明了的学习教程。本文为系列教程第2篇引言,翻译自Introduction

  1. 引言-Introduction
  2. 基础-Basics
  3. 控制器-Controllers
  4. 作用域-Scopes
  5. 集合-Collections
  6. 模块-Modules
  7. 依赖注入-Dependency Injection
  8. 服务-Services
  9. 过滤器-Filters
  10. 指令-Directives
  11. 指令作用域-Directive Scopes
  12. 路由-Routing
  13. 通信-HTTP
  14. 结论

2.正文

这是一本袖珍书,灵感来自于其他免费、简短、友好的编程书,例如Alex MacCaw的《The Little Book on CoffeeScript》。我写这本书的目的是像我学习AugularJS那样介绍它,关注典型环境的快速web开发中如何应用AngularJS。依据帕累托原则Pareto principle), 优先讲简单、实用的功能,课程结束之后,您可能只了解AngularJS的一部分,却可以利用该框架轻松创建强大的前端应用。本书是一个动态网站,所有案例使用最新版的AngularJS(Angular 1.3.14),如果您倾向于交互学习,那么修改代码指令,您可以实时的看到运行效果,鼓励大家进行探究性、实验性学习。

如果您想了解更多我为什么写这本书,了解AngularJS的历史,AngularJS是否适合您的项目,请继续阅读本文,如果您非常确定您为什么阅读本文,急于学习AngularJS请移步系列教程第03篇基础。

2.1 AngularJS 难或易?

我有近十年的web MVC的工作经验,从 Struts 到Spring MVC到Ruby on Rails到Backbone,我甚至还写了一本Backbone and CoffeeScript方面的书,因此我自认为学习AngularJS起来肯定非常简单。但是,当我沉浸在API和文档中时,我发现各种各样不熟悉的名词和术语(transclusion, directive, and isolate scope)阻碍了学习进程,当我阅读官方文档和教程时,所谓的简单易学好像没有出现,反而是一头雾水,比如说下面这段折磨人的文字。

The advantage of transclusion is that the linking function receives a transclusion function which is pre-bound to the correct scope. In a typical setup the widget creates an isolate scope, but the transclusion is not a child, but a sibling of the isolate scope. This makes it possible for the widget to have private state, and the transclusion to be bound to the parent (pre-isolate) scope.

现在官方文档里面已经没有这段文字了,通过AngularJS官方团队的不懈努力,官方文档的可读性进一步增强了很多。但是,当我第一次阅读时,这段文字让我感觉非常无知,好像走进了一个新的领域,我感觉到AngularJS是一个复杂的技术(没有开玩笑), AngularJS的各种细节在广泛的被讨论,难以调试、怪癖、陷阱等。

像任何自尊自爱的程序猿一样,我撸起袖子奋战到底,慢慢地我熟悉了那些名词和术语,以为学习必然有点陡。直到有一天,我看到AngularJS的创始人Mi?ko Hevery的视频采访,根据Hevery的解释,我发现了一个简单而又重要的事实:

使用简单是AngularJS的初衷

Hevery解释道,当他构思AngularJS时,把它作为非编程人员仅仅使用预定义的标签就能创建动态web应用的工具。我才意识到,我卷入了太多的AngularJS的细节而没有获得相应的收益。Angular项目及相关社区在相互协作方面投入太多精力,增加了Angular的复杂性和学习的难度,我们需要探索更好的使用方式以趋近于Angular的开发初衷,去除大量的精巧设计的控制,不考虑微弱的内部安全隐患,直接向前。

对我来说这个方法是正确的,尽管Angular不是我唯一的web开发语言,但绝对是最闪耀的那个。

2.2 您需要Angular吗?

虽然类似于Angular的客户端js工具让创建单页面应用变得如此简单,但是不少web项目仍然使用传统的架构,服务端渲染、jQuery实现交互。坦率的讲,您需要考虑您的项目是否需要在客户端管理数据、渲染html,您是否有足够的理由开始尝试一项新的技术。

当然,Angular是一个明显的、固执己见的选择(Angular is a broad, opinionated solution)。虽然通常被认作一个库,但是Angular内置的对模块化、依赖注入的支持,将会改变您的项目管理方式。如果你想找一个Angular的替代方案,或许更加成熟的,或许最佳的,但是这些都不可能完全替代Angular。除非您的项目肯定能达到Google工程师认为的经典的水准,你可能不需要达到Angular核心团队认定的组织支持的水平。依赖注入尽管可以用在单元测试方面,但还是太过复杂,大家可以到Dependency Injection 看看实在的案例。

当您需要引入非Angular代码和Angular的数据绑定和渲染机制协同工作的话,将会产生另一个问题,您需要非常深入这些神秘的细节。相对来说,Backbone基于jQuery,更加简单、更加易于理解,如果您的项目深度依赖jQuery,利用Backbone实现渐进增强也是一个不错的选择。

另外,Angular在UI组件和模型对象之间的双向绑定,也会增加应用的复杂性。Facebook的开发者分享了双向数据绑定中的挫败感:

我们发现双向数据绑定容易导致级联更新: 一个对象的改变导致另一个对象的改变,甚至更多对象的改变。随着应用的增长,由用户行为导致的级联更新将会很难预测。

相对于Angular来说,尽管Facebook的 React不重在关注渲染,但是基于React的客户端技术也值得一试。

2.3 使用Angular的理由

Angular可能是当今最流行的Javascript MV*解决方案,github得到了36722个star,当然阅读本文时可能更多。它迅速从同类的解决方案中脱颖而出,成为顶级解决方案,大家可以到TodoMVC project比较诸多MV*方案。让我们来看看Angular成功的原因。

  • 快速成型(Immediate productivity) 
    如果您的项目需要用户界面实现数据的增删改查操作,Angular可以非常漂亮地兑现快速成型的承诺。仅仅需要在普通的html添加几个属性,使用少量的几行javascript代码你就可以实现普通类库需要大量技能、大量努力才能实现的功能。
  • 熟悉度 
    Angular包含开发人员熟知的原生JS和几乎普通的HTML,用几乎这个词主要是因为Angular引入了一些新增的html元素,属性和一些可能看起来恐怖的行内代码。相对于其他模板系统,Angular已经非常接近html了,大部分开发人员都能够轻松理解。
  • 适应性 
    Angular可以很好支持当前的UI核心的JS类库,如果想使用类似于Bootstrap这样的流行类库的话,您可以有效利用AngularUI这样的第三方项目实现简单整合。所以,无论你想为传统文档中心网站增加交互性,还是开发一个炫彩的单页面应用,Angular都可以是一个不错的选择。
  • 未来标准 
    尽管我不能确定Angualr能达到什么高度,Angular母公司Google会如何创造未来,有一点非常明确,Angular的使用方式非常接近目前提出的标准 Web Components,另外,Angular接近于下个版本Javascript的特征(例如Object.observe)它的基础原则将会被维持而不会弃用。
  • 社区 
    当然,一个非常重要的原因是它非常流行,应用于大量的超负荷网站中,Google的定力支持,大量的插件和学习资源。当然大部分人的正确选择不一定是每个人的正确选择,你还需要谨慎考虑Angular是否真的适合您。

2.4 关于本书

很多年前当我在My MacBook Air上阅读一本JS编程书时,已经有了脚本书( ScriptyBooks)的点子。需要运行书中的一个案例时,我们从网站中下载案例,着急于如何打开它,从大量的文件中找到需要的文件,费劲九牛二虎之力找到正确的文件,打开它,修改它,但是很可能你还是不能运行,你可能需要安装支持环境。

为什么我们在讲解一个前端技术时,还需要耗费诸多努力才能运行一个案例,如果能够可以在书中展示代码,允许您进行修改,并且实时展示代码呈现结果,那就很美妙了。

本书采用这样的方式。所有代码均可以动态修改,并实时展示代码运行结果。如下所示。

<code><strong>The lucky number {{3 + 5}}.</strong></code>

The lucky number 8.

我利用 CodeMirror 和Angular实现动态交互功能,如果还是angular菜鸟,不妨修改下上面的代码,看看会发生什么(博客支持技术有限,本教程没有提供动态修改代码功能,望见谅。请阅读原教程代码)。

2.5 接下来

接下来,准备讲解客户端模板(client-side templating)和双向绑定(two-way bindings),向您展示Angular的基础。尽管大部分代码非常简单,一眼看过去就可以理解,但相信哪怕有经验的Angular开发人员也可以有所收获。

3.声明

前端开发whqet,关注前端开发,分享相关资源。csdn专家博客,王海庆希望能对您有所帮助,限于作者水平有限,出错难免,欢迎拍砖! 
欢迎任何形式的转载,烦请注明装载,保留本段文字。 
本文原文链接,http://blog.csdn.net/whqet/article/details/44596577 
欢迎大家访问独立博客http://whqet.github.io

作者:whqet 发表于2015/3/26 11:03:43 原文链接

阅读:2365 评论:19 查看评论

01序-AngularJS 基础教程

0. 目录


1. 前言

AngularJS是为了克服HTML在构建应用上的不足而设计的。HTML是一门很好的为静态文本展示设计的声明式语言,但要构建WEB应用的话它就显得乏力了,所以AngularJS做了一些工作来来解决静态网页技术在构建动态应用上的不足。

AngularJS的初衷是为了简化web APP开发,精髓是简单。但是国内外有很多AngularJS的教程,都着重讲AngularJS的强大之处,从而增加了AngularJS学习的难度,本教程试图用通俗的语言讲解最为基础最为实用的内容,简化学习进程、降低学习难度是本教程的初衷。

欢迎大家批评指正

2. AngularJS?

AngularJS主要用于构建单页面Web应用,同其他历史悠久的Web技术(HTML、CSS和JavaScript)配合使用,使构建交互式的现代Web应用变得更加简单。

AngularJS提供了开发者在现代Web应用中经常要用到的一系列高级功能,例如解耦应用逻辑、数据模型和视图,Ajax服务,双向绑定、依赖注入、测试等功能,通过原生的Model-View-Controller(MVC,模型?视图?控制器)功能增强了HTML,从而降低构建复杂应用的难度。

3. 系列教程目录

  1. 引言-Introduction
  2. 基础-Basics
  3. 控制器-Controllers
  4. 作用域-Scopes
  5. 集合-Collections
  6. 模块-Modules
  7. 依赖注入-Dependency Injection
  8. 服务-Services
  9. 过滤器-Filters
  10. 指令-Directives
  11. 指令作用域-Directive Scopes
  12. 路由-Routing
  13. 通信-HTTP
  14. 结论

4. 学习资源

  1. ANGULAR BASICS
  2. 官方网站–墙外,大家懂的,可以访问官方github
  3. angularjs中文社区
  4. AngularJS Nice Things–爱好者聚集地
  5. AngularJS中文网
  6. AngularJS手册中文手册
  7. 七步从Angular.JS菜鸟到专家

5.声明

前端开发whqet,关注前端开发,分享相关资源。csdn专家博客,王海庆希望能对您有所帮助,限于作者水平有限,出错难免,欢迎拍砖! 
欢迎任何形式的转载,烦请注明装载,保留本段文字。 
本文原文链接,http://blog.csdn.net/whqet/article/details/44586849 
欢迎大家访问独立博客http://whqet.github.io