Angular - 过滤具有多个值

时间:2016-04-12 16:10:08

标签: javascript angularjs

我已经构建了一个基于this post的过滤器。 问题是在某些对象中我有同时具有多个值(值数组)的属性(例如,一种类型的葡萄酒可以同时属于“wine”和“champagne”类别) 。我一直试图修改过滤器没有运气。这是我的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

3 个答案:

答案 0 :(得分:1)

这将有效:

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/

答案 1 :(得分:1)

我猜你试图为每个属性过滤对象。如果这是你想要的,我改变了你的代码,它列出了每个属性的所有属性和所有可能的值作为复选框。

首先,它传递每个wine以获取所有可用的过滤器选项并将其设置为数组变量。同时删除重复项。

然后,当选中每个复选框时,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;
    }            
});

答案 2 :(得分:0)

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

$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"] }    
];

然后你可以把这个属性当作一个数组处理。它将更加一致和可读。