AngularJS Team List应用:过滤器跟控制器的交互

AngularJS Team List应用:过滤器和控制器的交互

        在这个例子中,我们将会通过实例展示如下两个主要的方面:

1.如何以一种优雅、简洁的方式把过滤器和迭代器联合起来使用?

2.如何使没有继承关系的控制器进行交互

        应用本身很简单,其中的数据是关于各种体育运动的团队列表,例如篮球、足球(是美式足球,不是英式足球)和曲棍球。对于每一个团队,都有队名、城市、运行名称,以及该团队是否是推荐团队这几个字段。

        我们需要在屏幕上显示这个团队列表,同时在左侧显示一个过滤器,当过滤规则被修改时,列表会立即刷新。我们打算使用两个控制器,一个用来存储数据,另一个和过滤器配合使用。另外,我们打算使用一个服务,它负责ListCtrl和FilterCtrl之间的交互。

        先来看看这个服务,整个应用将会由它来驱动。

service.js

angular.module('myApp.services', []).
 factory('filterService', function() {
	 return {
		 activeFilters: {},
		 serchText:''
	 };
 });

        AngularJS服务都是单例的(它是带有小写"s"的单例,也就是作用域中的单例,而不是全局可见或者全局可访问的单例)。当我们声明filterService的时候,就已经保证了在整个应用中filterService的实例只有一个。

        我们最终让filterService成为了FilterCtrl和ListCtrl之间的交互通道,因为这两个控制器可以都绑定在它上面,并且当FilterService发生更新的时候,两控制器可以访问其中的东西。这两个控制器都极其简单,因为它们基本上什么都不做,只是简单地进行赋值操作。

controller.js

var app = angular.module('myApp', ['myApp.services']);

app.controller('ListCtrl', function($scope, filterService) {
	$scope.filterService = filterService;
	$scope.teamsList = [
    	{id:1,name:'Dalls Mavericks',sport:'Basketball',city:'Dallas',featured:true},
    	{id:2,name:'Dalls Cowboys',sport:'Football',city:'Dallas',featured:false},
    	{id:3,name:'New York Knicks',sport:'Basketball',city:'New York',featured:false},
    	{id:4,name:'Brooklyn Nets',sport:'Basketball',city:'New York',featured:false},
    	{id:5,name:'New York Jets',sport:'Football',city:'New York',featured:false},
    	{id:6,name:'New York Giants',sport:'Football',city:'New York',featured:true},
    	{id:7,name:'Los Angeles Lakers',sport:'Basketball',city:'Los Angeles',featured:true},
    	{id:8,name:'Los Angeles Clippers',sport:'Basketball',city:'Los Angeles',featured:false},
    	{id:9,name:'Dallas Stars',sport:'Hockey',city:'Dallas',featured:false},
    	{id:10,name:'Boston Bruins',sport:'Hockey',city:'Boston',featured:true}
	];
});

app.controller('FilterCtrl', function($scope, filterService) {
	$scope.filterService = filterService;
});

List.html

<!DOCTYPE html>
<html ng-app="myApp">

<head lang="en">
	<meta charset="utf-8"></meta>
	<title>Teams List App</title>
	<script src="jquery/jquery-1.8.3.js"></script>
	<script src="angular/angular.js"></script>
	<link rel="stylesheet" href="bootstrap/bootstrap.css"></link>
	<script src="bootstrap/bootstrap.js"></script>
	
	<script src="service.js"></script>
	<script src="controller.js"></script>
</head>

<body>
	<div class="row-fluid">
		<div class="span3" ng-controller="FilterCtrl">
			<form class="form-horizontal">
				<div class="controls-row">
					<label for="searchTextBox" class="control-label">Search:</label>
					<div class="controls">
						<input type="text" id="searchTextBox" ng-model="filterService.searchText"></input>
					</div>
				</div>
				
				<div class="controls-row">
					<label for="sportComboBox" class="control-label">Sports:</label>
					<div class="controls">
						<select id="sportComboBox" ng-model="filterService.activeFilters.sport" ng-options="sport for sport in ['Basketball', 'Hockey', 'Football']">
						</select>
					</div>
				</div>
				
				<div class="controls-row">
					<label for="cityComboBox" class="control-label">City:</label>
					<div class="controls">
						<select id="cityComboBox" ng-model="filterService.activeFilters.city" ng-options="city for city in ['Dallas', 'Los Angeles', 'Boston', 'New York']">
						</select>
					</div>
				</div>
				
				<div class="controls-row">
					<label class="control-label">Featured:</label>
					<div class="controls">
						<input type="checkbox" ng-model="filterService.activeFilters.featured" ng-false-value="" />
					</div>
				</div>
			</form>
		</div>
		<div class="offset1 span8" ng-controller="ListCtrl">
			<table>
				<thead>
					<tr>
						<th>Name</th>
						<th>Sport</th>
						<th>City</th>
						<th>Featured</th>
					</tr>
				</thead>
				<tbody id="teamListTable">
					<tr ng-repeat="team in teamsList | filter:filterService.activeFilters | filter:filterService.searchText">
						<td>{{team.name}}</td>
						<td>{{team.sport}}</td>
						<td>{{team.city}}</td>
						<td>{{team.featured}}</td>
					</tr>
				</tbody>
			</table>
			<br>
			<div>
				<span>过滤器对象activeFilters:{{filterService.activeFilters}}</span>
			</div>
		</div>
	</div>
</body>	
</html>

        如上HTML模板中,复选框绑定在filterService.activeFilters.featured属性上。需要注意的是,当featured被选中时,我们只想把那些featured=true的团队显示出来。当它被反选时,我们需要把那些featured=true和featured=false的团队都显示出来。为了实现这一点,我们使用ng-false-value=""指令,用来说明当复选框被反选时需要清除featured过滤器的效果。

       我们再来看一下ng-repeat这段代码:

"team in teamsList | filter:filterService.activeFilters | filter:filterService.searchText"

        第一部分和平常一样,不同之处在于后面两个新的过滤器。第一个过滤器告诉AngularJS使用filterService.activeFilters来过滤数据列表。基本的处理方法是:获取被过滤对象上的每一个属性,并保证迭代器中的每一个对象都与过滤器中对应的属性相匹配。所以,如果acitveFilters[city]=Dallas,那么只有过滤器中满足city=Dallas的对象才会被选中。如果存在多个过滤器,那么所有过滤器都必须进行匹配。第二个过滤器是一个文本匹配过滤器。它的基本处理方式是:如果对象的所有取值中存在与filterService.searchText相等的值,则选中该对象。所以,它会在cities、teamnames、sports以及featured所有这些属性上进行匹配。

运行效果:

AngularJS Team List应用:过滤器跟控制器的交互
AngularJS Team List应用:过滤器跟控制器的交互
AngularJS Team List应用:过滤器跟控制器的交互