06集合-AngularJS基础教程
0. 目录
1. 前言
AngularJS是为了克服HTML在构建应用上的不足而设计的。HTML是一门很好的为静态文本展示设计的声明式语言,但要构建WEB应用的话它就显得乏力了,所以AngularJS做了一些工作来来解决静态网页技术在构建动态应用上的不足。
AngularJS的初衷是为了简化web APP开发,精髓是简单。但是国内外有很多AngularJS的教程,都着重讲AngularJS的强大之处,从而增加了AngularJS学习的难度,本教程试图用通俗的语言讲解最为基础、最为实用的内容,简化学习进程、降低学习难度是本教程的初衷。
本系列教程以翻译Chris Smith的Angualr Basics为梗概,融合博主自己的理解,为大家提供一个简单明了的学习教程。
本文为系列教程第6篇集合,翻译自Collections。
- 序
- 引言-Introduction
- 基础-Basics
- 控制器-Controllers
- 作用域-Scopes
- 集合-Collections
- 模块-Modules
- 依赖注入-Dependency Injection
- 服务-Services
- 过滤器-Filters
- 指令-Directives
- 指令作用域-Directive Scopes
- 路由-Routing
- 通信-HTTP
- 结论
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>
编译结果为:
- Item 1
- Nested Item 1.1
- Nested Item 1.2
- Item 2
- Nested Item 2.1
- 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>
编译结果为下面动图所示。
有没有注意到,我们在使用
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
- 2
- 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
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。