Angular JS - 创建一个过滤器来比较 2 个数组 [英] Angular JS - Creating a filter to compare 2 arrays

查看:24
本文介绍了Angular JS - 创建一个过滤器来比较 2 个数组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在为应用程序开发一个搜索页面.我在谷歌上搜索了很多,但除了关于过滤器的零散信息之外,我什么也找不到.

我需要的是一个包含特定元素列表的复选框列表(如示例中的语言).我为客户的名称设置了一个用于排序的过滤器,但我想将过滤器添加到辅助表中,例如(正如我所说的)语言.

客户:

$scope.clientes = [{ 'id': '3', 'name': 'Susana', 'surname': 'Rodríguez Torrón', 'languages': [{'id': 1, 'name':'english'}, {'id': 2, 'name': 'spanish'}] },{ 'id': '4', 'name': 'Pablo', 'surname': 'Campos Pérez', 'languages': [{'id': 3, 'name': 'german'}, {'id': 5, 'name': 'japanese'}] }];

语言:

$langs = [{'id': 1, 'name': 'english' },{'id': 2, 'name': '西班牙语' },{'id': 3, 'name': '德语' },{'id': 4, 'name': 'japanese' }];

HTML(用于复选框列表):

<div class="col-md-12"><h4>语言</h4><div class="checkbox-block" ng-repeat="lang in langs"><label class="checkbox" for="{{lang.id}}"><input type="checkbox" ng-model="langs[lang.id]" name="languages_group" id="{{lang.id}}"/><span ng-bind-html="lang.name"></span>

;

客户:

...每个客户的所有数据都显示在这里...

;

在数组 $scope.langs 中,我检查了检查,现在我想将它与每个客户端进行比较并仅显示具有该语言的客户端.可以做到吗?你能帮我知道怎么做吗??

过滤器代码.

 app.filter('langsFilter', function () {返回函数(输入,$scope){var 输出 = [];to_langs = $scope.filters.langs;var trueContro = false;for (var lang in to_langs) {if (to_langs[lang] === true) {trueContro = true;}}for (var client in input) {for (var i = 0; i < input[client].langs.length; i++) {if (trueContro === true) {如果(to_langs[client.langs[i]]){输出.推(输入[客户端]);}} 别的 {输出.推(输入[客户端]);}}}返回输出;};});

正如你所说,我发布了我正在使用的代码.我知道它现在不起作用,但它会让您了解我需要实现的目标:

我们有一组客户端,除了其他过滤器之外,此过滤器会比较每个客户端的语言以仅显示具有这些语言的客户端.

解决方案

(注意:为了简单起见,我将所有内容都塞进了这里的指令中;在现实生活中,这些代码的大部分都属于控制器或其他地方)

不要这样做

此示例显示了按您描述的方式工作的过滤器.我不得不更改您的 ng-model -- 将 ng-model="langs[lang.id]" 放在复选框上会覆盖您在 langs 数组中的数据该框的已选中"状态,因此我将用户的语言选择与 scope.selectedLangs 相关联——但它会使用您现有的数据结构.

var app = angular.module("app", []);app.directive('sampleDirective', function() {返回 {限制:'A',链接:功能(范围){范围.langs = [{'id': 1, 'name': 'english'},{'id': 2, 'name': 'spanish'},{'id': 3, 'name': '德语'},{'id': 4, 'name': 'japanese'}];scope.selectedLangs = {};scope.clientes = [{'id': '3','name': '苏珊娜','姓氏':'罗德里格斯·托伦',语言":[{'id': 1, 'name': 'english'},{'id': 2, 'name': 'spanish'}]}, {'id': '4','name': '巴勃罗','姓氏':'坎波斯佩雷斯',语言":[{'id': 3, 'name': '德语'},{'id': 4, 'name': 'japanese'}]}];}};});app.filter('sampleFilter', function() {返回函数(客户,selectedLangs){var ret = [];angular.forEach(客户,功能(客户){var 匹配 = 假;angular.forEach(client.languages, 函数(l) {如果(selectedLangs[l.id]){匹配 = 真;}});如果(匹配){ret.push(客户端);}});返回 ret;};});

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script><div ng-app="app"><div 样本指令>语言:<div ng-repeat="lang in langs"><标签><input type="checkbox" ng-model="selectedLangs[lang.id]" name="languages_group">{{lang.name}}

<br>客户:<div ng-repeat="client in clientes | sampleFilter:selectedLangs">{{client.name}} {{client.surname}}

但是请考虑不要这样做 - 角度过滤器的性能本质上不是很好(并且您为语言和客户端选择的低效数据结构加剧了这种情况.您至少应该考虑更改将带有 ID 的对象数组放入由这些 ID 键控的哈希表中,即:

scope.langs = {1:{名称:'英文'},2:{名称:'西班牙语'}//...等等}

这将使您不必遍历如此多的嵌套循环来根据可用语言检查客户端语言——您现在拥有的方式是两全其美的,因为如果您需要通过 ID 查找任何内容,您仍然必须遍历数组.)

改为这样做

与其依赖于每个 $digest 都运行的过滤器,不如观察所选语言的变化并仅在需要时更新结果,如下所示:

var app = angular.module("app", []);app.directive('sampleDirective', function() {返回 {限制:'A',链接:功能(范围){范围.langs = [{'id': 1, 'name': 'english'},{'id': 2, 'name': 'spanish'},{'id': 3, 'name': '德语'},{'id': 4, 'name': 'japanese'}];scope.clientes = [{'id': '3','name': '苏珊娜','姓氏':'罗德里格斯·托伦',语言":[{'id': 1, 'name': 'english'},{'id': 2, 'name': 'spanish'}]}, {'id': '4','name': '巴勃罗','姓氏':'坎波斯佩雷斯',语言":[{'id': 3, 'name': '德语'},{'id': 4, 'name': 'japanese'}]}];scope.selectedLangs = {};scope.filterByLanguage = function() {scope.matchedClients = [];angular.forEach(scope.clientes, function(client) {var 匹配 = 假;angular.forEach(client.languages, 函数(l) {如果(范围.selectedLangs[l.id]){匹配 = 真;}});client.matchesLanguage = 匹配;});}}};});

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script><div ng-app="app"><div 样本指令>语言:<div ng-repeat="lang in langs"><标签><input type="checkbox" ng-model="selectedLangs[lang.id]" name="languages_group" ng-change="filterByLanguage()">{{lang.name}}

<br>客户:<div ng-repeat="client in clientes" ng-show="client.matchesLanguage">{{client.name}} {{client.surname}}

注意过滤器"(现在是指令作用域上的一个函数)现在只在语言选择上触发 ng-change 处理程序时运行;也不是单独的 selectedLanguages 变量,它只是向每个客户端添加一个matchesLanguage"字段,供 ng-show 使用.

未选择任何内容时

对于评论中请求的异常——如果没有选择任何语言,则显示所有客户端——您可以添加另一个循环:

 scope.noLanguagesSelected = true;angular.forEach(Object.keys(scope.selectedLangs), function(k) {如果(范围.selectedLangs[k]){scope.noLanguagesSelected = false;}});

然后更改您的 ng-show 以显示客户端是否选择了该特定语言或根本没有选择任何语言(这可能比在这种情况下人为地在所有内容上设置 client.matchesLanguage 更好:)

ng-show="client.matchesLanguage || noLanguagesSelected"

如下图:

var app = angular.module("app", []);app.directive('sampleDirective', function() {返回 {限制:'A',链接:功能(范围){范围.langs = [{'id': 1, 'name': 'english'},{'id': 2, 'name': 'spanish'},{'id': 3, 'name': '德语'},{'id': 4, 'name': 'japanese'}];scope.clientes = [{'id': '3','name': '苏珊娜','姓氏':'罗德里格斯·托伦',语言":[{'id': 1, 'name': 'english'},{'id': 2, 'name': 'spanish'}]}, {'id': '4','name': '巴勃罗','姓氏':'坎波斯佩雷斯',语言":[{'id': 3, 'name': '德语'},{'id': 4, 'name': 'japanese'}]}];scope.selectedLangs = {};scope.noLanguagesSelected = true;scope.filterByLanguage = function() {angular.forEach(scope.clientes, function(client) {var 匹配 = 假;angular.forEach(client.languages, function(l) {如果(范围.selectedLangs[l.id]){匹配 = 真;}});client.matchesLanguage = 匹配;});/*特殊情况:如果没有选中复选框,则假装它们都匹配.(在现实生活中,您可能希望以不同的方式表示这一点在 UI 中,而不仅仅是设置matchesLanguage=true).我们不能只检查是否 selectedLangs == {},因为如果用户选中然后取消选中一个框,selectedLangs 将包含 {id: false}.所以我们必须实际检查每个键的真实性:*/scope.noLanguagesSelected = true;//假设为真,直到证明否则:angular.forEach(Object.keys(scope.selectedLangs), function(k) {如果(范围.selectedLangs[k]){scope.noLanguagesSelected = false;//否则证明.}});}}};});

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script><div ng-app="app"><div 样本指令>语言:<div ng-repeat="lang in langs"><标签><input type="checkbox" ng-model="selectedLangs[lang.id]" name="languages_group" ng-change="filterByLanguage()">{{lang.name}}

<br>客户:<div ng-repeat="client in clientes" ng-show="client.matchesLanguage || noLanguagesSelected">{{client.name}} {{client.surname}}

I am developing a search page for an application. I have searched a lot at google, but I can't find anything than fragmented information about filters.

What I need is a list of checkboxes containing certain lists of elements (like in the example, languages). I had put a filter for ordering, for the name of the client, but I want to add filters to auxiliar tables like (as I said) languages.

Clients:

$scope.clientes = [
    { 'id': '3', 'name': 'Susana', 'surname': 'Rodr&iacute;guez Torr&oacute;n', 'languages': [{'id': 1, 'name': 'english'}, {'id': 2, 'name': 'spanish'}] },
    { 'id': '4', 'name': 'Pablo', 'surname': 'Campos P&eacute;rez', 'languages': [{'id': 3, 'name': 'german'}, {'id': 5, 'name': 'japanese'}] }
];

Languages:

$langs = [
     {'id': 1, 'name': 'english' },
     {'id': 2, 'name': 'spanish' },
     {'id': 3, 'name': 'german' },
     {'id': 4, 'name': 'japanese' }
];

HTML (for checkboxes list):

<div class="row">
  <div class="col-md-12">
    <h4>Languages</h4>
    <div class="checkbox-block" ng-repeat="lang in langs">
      <label class="checkbox" for="{{lang.id}}">
        <input type="checkbox" ng-model="langs[lang.id]" name="languages_group" id="{{lang.id}}" />
        <span ng-bind-html="lang.name"></span>
      </label>
    </div>
  </div>
</div>;

Clients:

<div class="client-wrapper" ng-repeat="client in clients | orderBy:order_field:order_type | filter:query">
    ... all the data of every client showed here ...
</div>;

In the array $scope.langs I havethe checks checked, now I want to compare it with every client and to show only the clients having that language(s). Can it be done? Can you help me to know how??

EDIT: Code of the filter.

    app.filter('langsFilter', function () {
        return function (input, $scope) {
            var output = [];
            to_langs = $scope.filters.langs;


            var trueContro = false;
            for (var lang in to_langs) {
                if (to_langs[lang] === true) {
                    trueContro = true;
                }
            }

            for (var client in input) {
                for (var i = 0; i < input[client].langs.length; i++) {
                    if (trueContro === true) {
                        if (to_langs[client.langs[i]]) {
                            output.push(input[client]);
                        }
                    } else {
                        output.push(input[client]);
                    }
                }
            }

            return output;
        };
    });

As you said I posted the code I am working at. It doesn't works right now, I know, but It will give you an idea of what I need to achieve:

We have a set of clients, in addition to another filters, this filter will compare the langs of every client for show only the client with those langs.

解决方案

(Caveat: for simplicity's sake I've stuffed everything into a directive here; in real life much of this code would belong in a controller or elsewhere)

DON'T DO THIS

This sample shows a filter working as you describe. I had to change your ng-model -- putting ng-model="langs[lang.id]" on a checkbox overwrites the data you had in the langs array with the box's "checked" status, so instead I tied the user's language selections to scope.selectedLangs -- but it otherwise uses your existing data structure.

var app = angular.module("app", []);
app.directive('sampleDirective', function() {
  return {
    restrict: 'A',
    link: function(scope) {
      scope.langs = [
        {'id': 1, 'name': 'english'}, 
        {'id': 2, 'name': 'spanish'},
        {'id': 3, 'name': 'german'}, 
        {'id': 4, 'name': 'japanese'}
      ];
      scope.selectedLangs = {};

      scope.clientes = [{
        'id': '3',
        'name': 'Susana',
        'surname': 'Rodríguez Torrón',
        'languages': [
          {'id': 1, 'name': 'english'}, 
          {'id': 2, 'name': 'spanish'}
        ]
      }, {
        'id': '4',
        'name': 'Pablo',
        'surname': 'Campos Pérez',
        'languages': [
          {'id': 3, 'name': 'german'}, 
          {'id': 4, 'name': 'japanese'}
        ]
      }];
    }
  };
});

app.filter('sampleFilter', function() {
  return function(clientes, selectedLangs) {
    var ret = [];
    angular.forEach(clientes, function(client) {
      var match = false;
      angular.forEach(client.languages, function(l) {
        if (selectedLangs[l.id]) {
          match = true;
        }
      });
      if (match) {
        ret.push(client);
      }
    });
    return ret;
  };
});

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
  <div sample-directive>
    Languages:
    <div ng-repeat="lang in langs">
      <label>
        <input type="checkbox" ng-model="selectedLangs[lang.id]" name="languages_group">{{lang.name}}
      </label>
    </div>
    <br>Clients:
    <div ng-repeat="client in clientes | sampleFilter:selectedLangs">{{client.name}} {{client.surname}}
    </div>
  </div>
</div>

But please consider not doing it this way -- angular filters are not very performant by nature (and this is exacerbated by the inefficient data structure you've chosen for both languages and clients. You should at the very least think about changing your arrays-of-objects-with-IDs into hash tables keyed by those IDs, i.e:

scope.langs = {
  1: {name: 'english'},
  2: {name: 'spanish'}
  // ...etc
}

This would save you having to iterate through so many nested loops to check client languages against available languages -- the way you have it now is the worst of both worlds, because if you need to find anything by ID you still have to iterate through the array.)

DO THIS INSTEAD

Instead of depending on a filter which will run every $digest, you'll be better off watching for changes to the selected languages and updating your results only when needed, like so:

var app = angular.module("app", []);

app.directive('sampleDirective', function() {
  return {
    restrict: 'A',
    link: function(scope) {
      scope.langs = [
        {'id': 1, 'name': 'english'}, 
        {'id': 2, 'name': 'spanish'},
        {'id': 3, 'name': 'german'}, 
        {'id': 4, 'name': 'japanese'}
      ];

      scope.clientes = [{
        'id': '3',
        'name': 'Susana',
        'surname': 'Rodríguez Torrón',
        'languages': [
          {'id': 1, 'name': 'english'}, 
          {'id': 2, 'name': 'spanish'}
        ]
      }, {
        'id': '4',
        'name': 'Pablo',
        'surname': 'Campos Pérez',
        'languages': [
          {'id': 3, 'name': 'german'}, 
          {'id': 4, 'name': 'japanese'}
        ]
      }];

      scope.selectedLangs = {};

      scope.filterByLanguage = function() {
        scope.matchedClients = [];
        angular.forEach(scope.clientes, function(client) {
          var match = false;
          angular.forEach(client.languages, function(l) {
            if (scope.selectedLangs[l.id]) {
              match = true;
            }
          });
          client.matchesLanguage = match;
        });
      }
    }
  };
});

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>

<div ng-app="app">
  <div sample-directive>
    Languages:
    <div ng-repeat="lang in langs">
      <label>
        <input type="checkbox" ng-model="selectedLangs[lang.id]" name="languages_group" ng-change="filterByLanguage()">{{lang.name}}
      </label>
    </div>
    <br>Clients:
    <div ng-repeat="client in clientes" ng-show="client.matchesLanguage">{{client.name}} {{client.surname}}
    </div>
  </div>
</div>

Note the "filter" (now a function on the directive scope) is now only run when the ng-change handler is fired on a language selection; also instead of a separate selectedLanguages variable this just adds a 'matchesLanguage' field to each client for ng-show to use.

WHEN NOTHING IS SELECTED

For the exception requested in comments -- show all the clients if none of the languages are selected -- you could add another loop:

    scope.noLanguagesSelected = true;
    angular.forEach(Object.keys(scope.selectedLangs), function(k) {
      if (scope.selectedLangs[k]) {
        scope.noLanguagesSelected = false;
      }
    });

and then alter your ng-show to show the client if either that specific language, or no language at all is selected (this is probably better than just artificially setting client.matchesLanguage on everything in that case:)

ng-show="client.matchesLanguage || noLanguagesSelected"

as shown in the following:

var app = angular.module("app", []);

app.directive('sampleDirective', function() {
  return {
    restrict: 'A',
    link: function(scope) {
      scope.langs = [
        {'id': 1, 'name': 'english'}, 
        {'id': 2, 'name': 'spanish'},
        {'id': 3, 'name': 'german'}, 
        {'id': 4, 'name': 'japanese'}
      ];

      scope.clientes = [{
        'id': '3',
        'name': 'Susana',
        'surname': 'Rodríguez Torrón',
        'languages': [
          {'id': 1, 'name': 'english'}, 
          {'id': 2, 'name': 'spanish'}
        ]
      }, {
        'id': '4',
        'name': 'Pablo',
        'surname': 'Campos Pérez',
        'languages': [
          {'id': 3, 'name': 'german'}, 
          {'id': 4, 'name': 'japanese'}
        ]
      }];

      scope.selectedLangs = {};
      scope.noLanguagesSelected = true;

      scope.filterByLanguage = function() {
        angular.forEach(scope.clientes, function(client) {
          var match = false;
          angular.forEach(client.languages, function(l) {
            if (scope.selectedLangs[l.id]) {
              match = true;
            }
          });
          client.matchesLanguage = match;
        });
        
        /* 
        special case: if no checkboxes checked, pretend they all match.
        (In real life you'd probably wnat to represent this differently 
        in the UI rather than just setting matchesLanguage=true).
        We can't just check to see if selectedLangs == {}, because if user
        checked and then unchecked a box, selectedLangs will contain {id: false}.
        So we have to actually check each key for truthiness:
        */
        scope.noLanguagesSelected = true; // assume true until proved otherwise:
        angular.forEach(Object.keys(scope.selectedLangs), function(k) {
          if (scope.selectedLangs[k]) {
            scope.noLanguagesSelected = false; // proved otherwise.
          }
        });
      }
    }
  };
});

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>

<div ng-app="app">
  <div sample-directive>
    Languages:
    <div ng-repeat="lang in langs">
      <label>
        <input type="checkbox" ng-model="selectedLangs[lang.id]" name="languages_group" ng-change="filterByLanguage()">{{lang.name}}
      </label>
    </div>
    <br>Clients:
    <div ng-repeat="client in clientes" ng-show="client.matchesLanguage || noLanguagesSelected">{{client.name}} {{client.surname}}
    </div>
  </div>
</div>

这篇关于Angular JS - 创建一个过滤器来比较 2 个数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
前端开发最新文章
热门教程
热门工具
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆