Benjamin - 专注前端开发和用户体验

NGJS-4 过滤器(Filters)

AngularJS教程 Benjamin 2235℃ 0评论

<h2>一、基本介绍</h2>
过滤器是用来格式化需要展示的数据的,可用于表单验证、客户端搜索、日期格式化等。还记得我们在介绍控制器的文章中的对字符串取反的实例吧,如果我们使用过滤器代替此函数,那么控制器逻辑将会更加简洁。看下面实例:

<div ng-app="myApp">
	<div ng-controller="FirstController as d">
		<span>正:{{d.data.message}}</span><br>
		<span>反:{{d.data.message | reverse}}</span>
	</div>
</div>

<script type="text/javascript">
	angular.module('myApp', [])

	.controller('FirstController', function($scope) {
		this.data = {
			message: "Hello World"
		}
	})

	.filter('reverse', function() {
		return function(str) {
			return str.replace(/(\w+)(\s+)(\w+)/, "$3$2$1");
		};
	});

</script>

二、自定义过滤器

上例中首先定义了一个过滤器reverse,对字符串取反。在HTML模板绑定{{}}中可用“|”分隔符来调用过滤器,同时也可以通过“filterName: param01 : param02”给过滤器添加一个或多个参数。下面一个实例,来实现json对象过滤:

<style type="text/css">
	ol, li{
		margin: 0;
		padding: 0;
	}
	.container {
		width: 300px;
		margin: 0 auto;
	}
	.ulist {
		list-style-type: none;
	}
	.ulist li {
		padding-left: 10px;
		line-height: 30px;
		border-bottom: 1px dotted #ccc;
	}
	.ulist li span {
		display: inline-block;
		width: 80px;
	}
</style>
<div ng-app="myApp" class="container">
	<div ng-controller="FirstController as d">
		<div>
			<label for="username">搜索:</label>
			<input type="text" ng-model="username" id="username" placeholder="请输入用户名称">
		</div>

		<ol  class="ulist">
			<li>
				<span>&nbsp</span>
				<span>用户名</span>
				<span>性别</span>
			</li>
			<li ng-repeat="user in d.users | filterTable: username as results">
				<span>{{$index + 1}}</span>
				<span>{{user.name}}</span>
				<span>{{user.sex}}</span>
			</li>
			<li ng-if="results.length === 0">
				No Found
			</li>
		</ol>
	</div>
</div>
<script type="text/javascript" src="../js/angular-1.3.4/angular.js"></script>	
<script type="text/javascript">
	angular.module('myApp', [])

	.controller('FirstController', function($scope, dataService) {
		this.users = dataService;
	})

	.factory('dataService', function() {
		return [{
			name: "John",
			sex : "male"
		}, {
			name: "Benjamin",
			sex : "male"
		}, {
			name: "Lucy",
			sex : "female"
		}];
	})

	.filter('filterTable', function() {
		return function(arr, username) {
			return arr.filter(function(item) {
				if(!username) {
					return true;
				}else {
					return item.name.toLowerCase().indexOf(username.toLowerCase()) >= 0;
				}
			});
		};
	});
</script>

本实例中我们没有Angular内置的filter过滤器,而是自己定义了一个filterTable过滤器,从而传递参数来实现客户端搜索的功能。要看具体实例,请戳这里

三、内置filter过滤器及实现原理

下面我们使用内置的filter过滤器来过滤数据:

<div ng-app="myApp" class="container">
	<div ng-controller="FirstController as d">
		<div>
			<label for="search">搜索:</label>
			<input type="text" ng-model="search" id="search" placeholder="请输入搜索条件">
		</div>

		<ol  class="ulist">
			<li>
				<span>&nbsp</span>
				<span>用户名</span>
				<span>性别</span>
			</li>
			<li ng-repeat="user in d.users | filter: search as results">
				<span>{{$index + 1}}</span>
				<span>{{user.name}}</span>
				<span>{{user.sex}}</span>
			</li>
			<li ng-if="results.length === 0">
				No Found
			</li>
		</ol>
	</div>
</div>
<script type="text/javascript" src="../../tdocs/angular-1.3.4/angular.js"></script>	
<script type="text/javascript">
	angular.module('myApp', [])

	.controller('FirstController', function($scope, dataService) {
		this.users = dataService;
	})

	.factory('dataService', function() {
		return [{
			name: "John",
			sex : "male"
		}, {
			name: "Benjamin",
			sex : "male"
		}, {
			name: "Lucy",
			sex : "female"
		}, {
			name: "Sam",
			sex : "female"
		}];
	});
</script>

具体展示,请戳这里。现在我们来通过源码来看看具体的API实现[angular-1.3.4/angular.js line 16452-16672]:

<script type="text/javascript">
function filterFilter() {
  return function(array, expression, comparator) {
    if (!isArray(array)) return array;

    var comparatorType = typeof(comparator),
        predicates = [];

    predicates.check = function(value, index) {
      for (var j = 0; j < predicates.length; j++) {
        if (!predicates[j](value, index)) {
          return false;
        }
      }
      return true;
    };

    if (comparatorType !== 'function') {
      if (comparatorType === 'boolean' && comparator) {
        comparator = function(obj, text) {
          return angular.equals(obj, text);
        };
      } else {
        comparator = function(obj, text) {
          if (obj && text && typeof obj === 'object' && typeof text === 'object') {
            for (var objKey in obj) {
              if (objKey.charAt(0) !== '$' && hasOwnProperty.call(obj, objKey) &&
                  comparator(obj[objKey], text[objKey])) {
                return true;
              }
            }
            return false;
          }
          text = ('' + text).toLowerCase();
          return ('' + obj).toLowerCase().indexOf(text) > -1;
        };
      }
    }

    var search = function(obj, text) {
      if (typeof text === 'string' && text.charAt(0) === '!') {
        return !search(obj, text.substr(1));
      }
      switch (typeof obj) {
        case 'boolean':
        case 'number':
        case 'string':
          return comparator(obj, text);
        case 'object':
          switch (typeof text) {
            case 'object':
              return comparator(obj, text);
            default:
              for (var objKey in obj) {
                if (objKey.charAt(0) !== '$' && search(obj[objKey], text)) {
                  return true;
                }
              }
              break;
          }
          return false;
        case 'array':
          for (var i = 0; i < obj.length; i++) {
            if (search(obj[i], text)) {
              return true;
            }
          }
          return false;
        default:
          return false;
      }
    };
    switch (typeof expression) {
      case 'boolean':
      case 'number':
      case 'string':
        // Set up expression object and fall through
        expression = {$:expression};
        // jshint -W086
      case 'object':
        // jshint +W086
        for (var key in expression) {
          (function(path) {
            if (typeof expression[path] === 'undefined') return;
            predicates.push(function(value) {
              //console.log({"name":"a"} && "b"); // b
              return search(path == '$' ? value : (value && value[path]), expression[path]);
            });
          })(key);
        }
        break;
      case 'function':
        predicates.push(expression);
        break;
      default:
        return array;
    }
    var filtered = [];
    for (var j = 0; j < array.length; j++) {
      var value = array[j];
      if (predicates.check(value, j)) {
        filtered.push(value);
      }
    }
    return filtered;
  };
}
</script>

从源码可以看出,过滤器采用的是模糊搜索(a.indexOf(b) > -1)当expression为表达式时string/boolean/number时,expression = {$: “ng-model的数据”};
当expression值为”j”时,expression= {$: “j”};

当expression值为{name: “Benjamin”, sex: “male”},直接for in循环遍历
当expression值为function(value, index) {
return index > 2;
}
返回users数组索引值大于2的项组成的数组。更多请参见AngualrJS API filter一节

四、同时设置多个过滤器

同时使用多个过滤器时,过滤器间以”|”分隔。

<div ng-app="myApp" class="container">
	<div ng-controller="FirstController as d">
		<div>
			<label for="search">搜索:</label>
			<input type="text" ng-model="search" id="search" placeholder="请输入搜索条件">
		</div>

		<ol  class="ulist">
			<li>
				<span>&nbsp</span>
				<span ng-click="ordername='name'; reverse=!reverse" class="poi">用户名</span>
				<span ng-click="ordername='sex'; reverse=!reverse" class="poi">性别</span>
			</li>
			<li ng-repeat="user in d.users | filter: search | orderBy: ordername : reverse  as results">
				<span>{{$index + 1}}</span>
				<span>{{user.name}}</span>
				<span>{{user.sex}}</span>
			</li>
			<li ng-if="results.length === 0">
				No Found
			</li>
		</ol>
	</div>
</div>

<script type="text/javascript">
	angular.module('myApp', [])

	.controller('FirstController', function($scope, dataService) {
		this.users = dataService;
	})

	.factory('dataService', function() {
		return [{
			name: "John",
			sex : "male"
		}, {
			name: "Benjamin",
			sex : "male"
		}, {
			name: "Lucy",
			sex : "female"
		}, {
			name: "Sam",
			sex : "female"
		}, {
			name: "Park",
			sex : "male"
		}, {
			name: "Lenade",
			sex : "female"
		}, {
			name: "Lucky",
			sex : "female"
		}];
	});
</script>

本例中我们增加了orderBy过滤器,并添加了两个参数字段名称和是否反序。多个过滤器共用实现表格排序和搜索功能。请戳这里查看~~。
关于orderBy过滤器的源码参见[angular-1.3.4/angular.js line 17467-17530],这里就不过多赘述了。其它currency/lowercase/upercase/limitTo/json等内置过滤器请大家参考API文档学习。

关于Filters就谈这么多,如果你对文中所述有疑问,欢迎给我留言。下一节将深入AngularJS中最重要的部分:指令(Directives)。

喜欢 (2)
发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址