使用AngularJS根据其属性对javascript数组项进行分组

时间:2018-12-13 10:33:31

标签: javascript arrays angularjs json grouping

想象一下有一系列葡萄酒:

[  
   {  
      "name":"wine A",
      "category":[  
         "red",
         "merlot"
      ]
   },
   {  
      "name":"wine B",
      "category":[  
         "white",
         "chardonnay"
      ]
   },
   {  
      "name":"wine C",
      "category":[  
         "white",
         "chardonnay",
         "red",
         "merlot"
      ]
   }
]

我需要使用AngularJS创建过滤/分组机制,这样,如果用户选择“霞多丽”,结果将是:

Results: wine B, wine C

我有一个有效的示例,但是请检查该示例的结构数据。在对象项中未定义任何数组。我应该在控制器中进行哪些调整以使其与上述(数组)结构兼容?

var app = angular.module("myApp", []);
app.controller("myCtrl", function($scope) {
  $scope.wines = [
    {
      name: "Wine A",
      category: "red"
    },
    {
      name: "Wine B",
      category: "red"
    },
    {
      name: "wine C",
      category: "white"
    },
    {
      name: "Wine D",
      category: "red"
    },
    {
      name: "Wine E",
      category: "red"
    },
    {
      name: "wine F",
      category: "white"
    },
    {
      name: "wine G",
      category: "champagne"
    },
    {
      name: "wine H",
      category: "champagne"
    }
  ];
  $scope.filter = {};

  $scope.getOptionsFor = function(propName) {
    return ($scope.wines || [])
      .map(function(w) {
        return w[propName];
      })
      .filter(function(w, idx, arr) {
        return arr.indexOf(w) === idx;
      });
  };

  $scope.filterByProperties = function(wine) {
    // Use this snippet for matching with AND
    var matchesAND = true;
    for (var prop in $scope.filter) {
      if (noSubFilter($scope.filter[prop])) continue;
      if (!$scope.filter[prop][wine[prop]]) {
        matchesAND = false;
        break;
      }
    }
    return matchesAND;
    /**/
    /*
            // Use this snippet for matching with OR
            var matchesOR = true;
            for (var prop in $scope.filter) {
                if (noSubFilter($scope.filter[prop])) continue;
                if (!$scope.filter[prop][wine[prop]]) {
                    matchesOR = false;
                } else {
                    matchesOR = true;
                    break;
                }
            }
            return matchesOR;
    /**/
  };

  function noSubFilter(subFilterObj) {
    for (var key in subFilterObj) {
      if (subFilterObj[key]) return false;
    }
    return true;
  }
});

app.filter("capitalizeFirst", function() {
  return function(str) {
    str = str || "";
    return str.substring(0, 1).toUpperCase() + str.substring(1).toLowerCase();
  };
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script>
<div ng-app="myApp">
  <div ng-controller="myCtrl">
    <div ng-repeat="(prop, ignoredValue) in wines[0]" ng-init="filter[prop]={}">
      <b>{{prop | capitalizeFirst}}:</b>
      <br />
      <span class="quarter" ng-repeat="opt in getOptionsFor(prop)">
        <input type="checkbox" ng-model="filter[prop][opt]" />&nbsp;{{opt}}
      </span>
      <hr>
    </div>
    <div ng-repeat="w in filtered=(wines | filter:filterByProperties)">
      {{w.name}} ({{w.category}})
    </div>
    <hr> Number of results: {{filtered.length}}
  </div>
</div>

2 个答案:

答案 0 :(得分:0)

只需使用search过滤器:

 <b>Pick your category: (Hard coding)</b>
            <ul>
              <li style="cursor:pointer" ng-click="click_filter('red')">red</li>
              <li style="cursor:pointer" ng-click="click_filter('merlot')">merlot</li>
              <li style="cursor:pointer" ng-click="click_filter('white')">white</li>
              <li style="cursor:pointer" ng-click="click_filter('chardonnay')">chardonnay</li>
            </ul>

    <b>Result</b>
          <div ng-repeat="wine in wines | filter:search ">
          <b>{{wine.name}}</b> - (category): {{wine.category}}
          </div>
    </div>

JS:

 $scope.click_filter = function(item) {
         $scope.search = item;
     }

还有什么要做的(如果您不希望对类别进行硬编码)-寻找如何从ng-repeat中删除重复的条目(在我的朋克示例中,存在此问题)

朋克:http://next.plnkr.co/edit/POW2i0rkQhpcvZYh?preview

答案 1 :(得分:0)

从列表移到数组时,您必须修改如何检索选项并添加对flat()的调用

  $scope.getOptionsFor = function(propName) {
    return ($scope.wines || [])
      .map(function(w) {
        return w[propName];
      })
      .flat()
      .filter(function(w, idx, arr) {
        return arr.indexOf(w) === idx;
      });
  };

然后filterByProperty需要能够处理数组以及纯值:

  $scope.filterByProperties = function(wine) {
    // Use this snippet for matching with AND
    var matchesAND = true;
    for (var prop in $scope.filter) {
      if (noSubFilter($scope.filter[prop])) 
        continue;

      if(Array.isArray(wine[prop])) {
          let matchCount = 0;  
          for(let i in wine[prop]) {
            if ($scope.filter[prop][wine[prop][i]]) {
              ++matchCount;
            }
          }
          let trueFilter = 0
          Object.values($scope.filter[prop]).forEach(element => {
              if (element)
                ++trueFilter
          });
          matchesAND = matchCount == trueFilter;
          if (! matchesAND)
            break
      }
      else {
      if (!$scope.filter[prop][wine[prop]]) {
        matchesAND = false;
        break;
      }}
    }
    return matchesAND;
  };