【问题标题】:Angular JS - Creating a filter to compare 2 arraysAngular JS - 创建一个过滤器来比较 2 个数组
【发布时间】:2016-01-18 17:06:48
【问题描述】:

我正在为一个应用程序开发一个搜索页面。我在 google 上搜索了很多,但除了过滤器的零散信息外,我找不到任何东西。

我需要的是一个包含某些元素列表的复选框列表(如示例中的语言)。我已经为客户的名称添加了一个排序过滤器,但我想将过滤器添加到辅助表中,比如(正如我所说的)语言。

客户:

$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': 'spanish' },
     {'id': 3, 'name': 'german' },
     {'id': 4, 'name': 'japanese' }
];

HTML(用于复选框列表):

<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>;

客户:

<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>;

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

编辑: 过滤器的代码。

    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;
        };
    });

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

我们有一组客户端,除了另一个过滤器,这个过滤器会比较每个客户端的语言,只显示有这些语言的客户端。

【问题讨论】:

  • 谷歌角度客户过滤器。
  • 问之前google了一下,没找到解决办法。
  • 你尝试了什么?也许考虑发布您为过滤功能编写的代码。
  • 提示:使用过滤器并不高效,因为它必须在每个摘要中重新评估这些过滤器。相反,仅当用户更改其中一个时,才生成与所有过滤器匹配的记录列表作为单独的范围属性,并改为绑定到该属性。额外提示:不要忘记在 ng-repeat 表达式中使用 track by 以允许对不变的行重用 DOM。
  • 我已经发布了代码,但它不完整并且无法运行。只为给你看。

标签: javascript angularjs angularjs-filter


【解决方案1】:

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

不要这样做

此示例显示了按照您描述的方式工作的过滤器。我不得不更改您的ng-model - 将ng-model="langs[lang.id]" 放在复选框上会用框的“已检查”状态覆盖您在 langs 数组中的数据,因此我将用户的语言选择绑定到scope.selectedLangs - 但它否则使用您现有的数据结构。

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>

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

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

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

改为这样做

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

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>

请注意,“过滤器”(现在是指令范围内的一个函数)现在仅在 ng-change 处理程序在语言选择上触发时运行;也不是单独的selectedLanguages 变量,而是为每个客户端添加一个“matchesLanguage”字段供ng-show 使用。

未选择任何内容时

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

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

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

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

如下图:

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>

【讨论】:

  • 令人难以置信的回答丹尼尔,我对你在这里谈论的很多事情都有理解问题。我很高兴,非常感谢。只有一个问题(我还没有研究过你的代码),你的逻辑是相反的,我的意思是,它应该是一个过滤器,所有客户端都必须是可见的,除非你选中一个复选框,然后只有客户端有选择将可见。这意味着如果我检查德语和英语,可见客户必须有英语和德语。这对我来说是最困难的情况。
  • 如果我理解你,你不是在寻找与我的逻辑相反的东西;而是相同的逻辑(如果选中了复选框,则显示确实具有该语言的客户端)但有一个额外的例外(如果未选中复选框,则显示所有客户端)?如果这是正确的,我可以将其添加到答案中
  • 或者等等。现在我已经添加了该示例,我意识到您可能还有其他意思——为了让客户匹配,他们需要检查他们的 所有 语言,而不仅仅是 至少一种他们的语言(正如我编码的那样)?也就是说,如果 Jane 有英语和德语,但我只检查了德语,Jane 不应该出现吗?
  • 很抱歉之前没有回答,我的工作占用了我所有的时间。我想在未选中复选框时查看所有人。当我检查英语时,我想查看所有说该语言的人,不管他们是否说另一种语言,如果他们说英语,就会显示出来。例如,Jane 会说英语和德语,Tob 会说西班牙语和英语,Mai 会说日语和西班牙语。如果没有选中检查,我们将看到 3 个人。如果检查英语,我们将看到 Jane 和 Tob。
  • 太棒了。那么好吧——我回答中的第三个 sn-p 的行为与您描述的一样(它与第二个 sn-p 相同,但有额外的位来处理“noLanguagesSeleted”情况。)
【解决方案2】:

我会向所有客户端显示 css 的“display:none”和一个带有该语言的附加类,因此,您可以订阅过滤器事件:

filterEl.addEventListener("onchange",filter);

function filter(ev){
    // first hide all clients
    document.getElementsByClassName("clients").forEach(function(){
        this.style.display = "none";
    });

    // then show all clients with XXX language
    var checkbox = ev.target;
    document.getElementsByClassName(checkbox.id).forEach(function(){
        this.style.display = "block";
    });
}

客户

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

【讨论】:

  • 在 Angular 中直接修改 DOM 绝不是一个好主意;你的所有this.style.display 设置将在下一个 $digest 中被清除。
猜你喜欢
  • 2021-08-31
  • 1970-01-01
  • 1970-01-01
  • 2017-10-07
  • 1970-01-01
  • 2019-02-15
  • 2021-12-12
  • 2015-08-04
  • 2020-03-18
相关资源
最近更新 更多