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。

<!-- 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>

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

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

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

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

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

2.2 Iteration

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

/* 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;

结果为: Item 2

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

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

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

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

2.3 ng-repeat

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

<!-- 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>

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

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

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

2.4 Object properties

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

<!-- 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>

编译结果如下图所示。
技术分享

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

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

<!-- 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>

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

2.5 $index

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

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

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

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

/* 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‘}
      ]
    }
  ];
}

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

<!-- 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>

编译结果为:

  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来重新声明变量,以避免外层数据的覆盖。

<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>

编译结果为:
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指令给条件为真的列表项设置绿色标签。你能让第一个和第五个设置绿色,其他灰色吗,来试一下吧。

<!-- 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>

编译结果为下面动图所示。
技术分享

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

2.7 Uniqueness

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

2.7.1 严格相等 ===

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

<!-- 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>

技术分享

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

<!-- 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>

2.7.2 track by

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

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

编译结果为:

  1. 1
  2. 2
  3. 1

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

2.8 Callback functions

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

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

/* 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);
  };
}

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

<!-- 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>

技术分享

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

2.9 -start and -end

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

<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>

编译结果为:
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

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。