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所有这些属性上进行匹配。
运行效果: