javascript - Angular JS - 创建一个过滤器来比较 2 个数组

标签 javascript angularjs angularjs-filter

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

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

客户:

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

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

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

最佳答案

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

不要这样做

此示例显示了一个按您描述的方式工作的过滤器。我不得不更改您的 ng-model - 将 ng-model="langs[lang.id]" 放在复选框上会覆盖您在 langs 数组中的数据该框的“选中”状态,因此我将用户的语言选择绑定(bind)到 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>

但请考虑不要这样做—— Angular 过滤器本质上不是非常高效(并且您为语言和客户端选择的低效数据结构加剧了这种情况。您至少应该考虑改变您的带有 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 使用。

未选择任何内容时

对于注释中请求的异常——如果未选择任何语言则显示所有客户端——你可以添加另一个循环:

    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>

关于javascript - Angular JS - 创建一个过滤器来比较 2 个数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34860269/

相关文章:

angularjs - 将方法从父指令传递到子指令

javascript - AngularJS 中使用整数字段进行过滤

javascript - jQuery 计算滴仙

javascript - AngularJS - 为所有应用程序环境动态设置配置参数

javascript - 垂直对齐不起作用 CSS/HTML

angularjs - 如何使用 AngularJS 在 Laravel 4 中保护 API 访问

javascript - 使用自定义过滤器 angularjs 过滤 2 个属性

json - 按特定值过滤 JSON ionic 2

javascript - 如何使用 JavaScript 更改光标位置,然后在其中放置新内容?

javascript - 将导航中的下拉子菜单对齐到与其父级相同的高度