【问题标题】:Angular - Filtering object with properties with multiple valuesAngular - 过滤具有多个值的属性的对象
【发布时间】:2016-08-03 09:15:42
【问题描述】:

我已经建立了一个基于this post 的过滤器。 问题在于,在某些对象中,我的属性可以同时具有多个值(值数组)(例如,一种葡萄酒可以同时属于“葡萄酒”和“香槟”类别) .我一直在尝试修改过滤器,但没有成功。这是我的html:

<div ng-controller="myCtrl">
<div ng-repeat="(prop, ignoredValue) in wines[0]" ng-init="filter[prop]={}">
    <b>{{prop}}:</b><br />
    <span class="quarter" ng-repeat="opt in getOptionsFor(prop)">
        <b><input type="checkbox" ng-model="filter[prop][opt]" />&nbsp;{{opt}}</b>
    </span>
    <hr />
</div>
<div ng-repeat="w in filtered=(wines | filter:filterByProperties)">
    {{w.name}} ({{w.category}})
</div>

还有我的角度逻辑:

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: ["champagne","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;
};

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

我想让它工作,所以当一个对象的属性具有一个以上值的数组时(如示例中的白色和香槟色),只需选择其中一个(或两者)即可选择该对象.提前致谢。

我已经更新了 JsFiddle here

【问题讨论】:

    标签: javascript angularjs


    【解决方案1】:

    我猜您正在尝试过滤每个属性的对象。如果这是您想要的,我更改了您的代码,该代码将每个属性的所有属性和所有可能的值列为复选框。

    首先,它传递每种葡萄酒以获取所有可用的过滤器选项并将其设置为数组变量。还删除重复项。

    然后,当每个复选框被选中时,filterByCurrentFilter 方法会查找所有选定的过滤器并检查葡萄酒是否支持所有这些条件。

    这是demo

    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: ["champagne","white"] },
            { name: "wine G", category: "champagne"},
            { name: "wine H", category: "champagne" }    
        ];
        $scope.filter = {};
    
        $scope.getFilterOptions = function() { 
                var optsAsObj = $scope.wines.reduce( function(prev,current) {
                for ( field in current ) {
                    prev[field] = prev[field] || [];
                    prev[field] = prev[field].concat(current[field])
                    // remove duplicates
                    prev[field] = prev[field].filter(function(item, pos) {
                        return prev[field].indexOf(item) == pos;
                    });
                }
                return prev;
            }, {});
    
            var result = []
            for (key in optsAsObj) {
                if ( key == '$$hashKey')
                   continue;
                    result.push ( { name:key, values: optsAsObj[key] })
            }
    
            return result;
    
        };
    
        // for performance, calculate once and set it to a variable
        $scope.filterOptions = $scope.getFilterOptions();
    
        $scope.filterByCurrentFilter = function (wine) {
            var selectedFilters = {}
            for ( filterName in $scope.filter) {
    
                    // i.e filterName: name, category
                    var criteria = $scope.filter[filterName]
                console.log('filterName:' + filterName)
                // i.e criteria: { 'Wine A': true, 'Wine B': null, 'Wine C':false }
                for (criteriaName in criteria) {
                        // i.e. criteriaName: 'Wine A'
                        if (criteria[criteriaName]) {
    
                            // a filter is true for criteriaName
                            var criteriaVals = selectedFilters[filterName] || []
                        criteriaVals.push(criteriaName);
    
                        console.log('filterName:' + filterName + ' criteriaName:' + criteriaName + " criteriaVals:" + criteriaVals);
    
                        if (criteriaVals.length > 0)
                            selectedFilters[filterName] = criteriaVals
                    }
                }
            }
    
            for (key in selectedFilters) {
                console.log(key + ':' + selectedFilters[key])
                    var filterVals = selectedFilters[key];
                if (!filterVals || filterVals.length == 0)
                        continue;
    
                var wineVal =  wine[key];
                wineVal = angular.isArray(wineVal) ? wineVal : [wineVal];
    
                var matches = wineVal.some ( function(item) {
                        return filterVals.indexOf(item) != -1;
                });
    
                if (!matches)
                    return false;
            }
    
            return true;
        };
    
        function noFilter(filterObj) {
            for (var key in filterObj) {
                if (filterObj[key]) {
                    return false;
                }
            }
            return true;
        }            
    });
    

    【讨论】:

    • 好的,我明白了。除了其他解决方案之外,在这个解决方案中,当您已经按一个属性(f.ex 名称)过滤,并且您按另一个属性(f.ex.类别)中的值过滤时,它不会添加两个集合,但它使交叉点。我认为这更好地解决了问题,所以我将其切换为有效。谢谢!
    【解决方案2】:

    这将起作用:

    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: ["champagne","white"] },
            { name: "wine G", category: "champagne"},
            { name: "wine H", category: "champagne" }    
        ];
        $scope.filter = {};
    
        $scope.getOptionsFor = function (propName) {
            return ($scope.wines || []).map(function (w) {
            if(w[propName] instanceof Array)
            {
              for (var key in w[propName]) {
                return w[propName][key];
              }
            }else{
                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(wine[prop]  instanceof Array)
                {
                    for (var key in wine[prop]) {
                            if (!$scope.filter[prop][wine[prop][key]]){
                      matchesAND = false;
                      }else{
                        matchesAND = true;
                        break;
                      }
                    }
                }
                else if (!$scope.filter[prop][wine[prop]]) {
                    matchesAND = false;
                    break;
                }
            }
            return matchesAND;
        };
    
        function noSubFilter(subFilterObj) {
            for (var key in subFilterObj) {
                if (subFilterObj[key]) return false;
            }
            return true;
        }
    });
    

    http://jsfiddle.net/8d6f5fdf/4/

    【讨论】:

      【解决方案3】:

      我建议将所有类别转换为这样的数组:

      $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: ["champagne","white"] },
          { name: "wine G", category: ["champagne"]},
          { name: "wine H", category: ["champagne"] }    
      ];
      

      然后您可以在任何地方将此属性视为一个数组。它将更加一致和可读。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-03-28
        • 2013-12-12
        • 1970-01-01
        • 2015-03-25
        • 2021-07-07
        • 2019-09-16
        相关资源
        最近更新 更多