简体   繁体   中英

How to filter nested object array with dynamic multiple check boxes in AngularJS. OR with and Filter

I trying to build a product list based on multiple filters. I thought this should be very straight forward but it's not for me at least.

Here is the plunkr http://plnkr.co/edit/vufFfWyef3TwL6ofvniP?p=preview

Checkboxes are dynamically generated from respective model eg sizes , colours , categories . Subcategory checkbozes should perform 'OR' query but cross section it should perform 'AND' query.

basically something like

filter:{categories:selectedcategories1} || {categories:selectedcategories2} | filter:{categories:selectedsizes1} || {categories:selectedsizes2}

problem is generating these filters dynamically. I also tried with filter in controller as-

var tempArr = [{'categories':'selectedvalue1'}, {'categories':'selectedvalue2'}];
var OrFilterObjects = tempArr.join('||');
$scope.products = $filter('filter')($scope.products, OrFilterObjects, true);

But couldn't find a way to assign correct value for OrFilterObjects .

Now as latest attempt (which is in plunkr) I am trying to use a custom filter. It's also not returning OR result.

Right now I am using it as productFilter:search.categories:'categories' if it would have returned OR result then I'd planned to use it as-

`productFilter:search.categories:'categories' | productFilter:search.colours:'colours' | productFilter:search.sizes:'sizes'`

Since I am here seeking help, it would be nice to have like productFilter:search .

I've spent considerable amount of time to find solution of this supposedly simple problem but most of examples use 'non-dynamic' checkboxes or flat objects.

May be I am thinking in wrong direction and there is a more elegant and simple Angular way for such scenarios. I would love to be directed towards any solution to similar solution where nested objects can be filtered with automated dynamically generated filters. Seems to me very generic use case for any shopping application but till now no luck getting there.

First thing you need to understand: this problem is not, by any definition, simple. You want to find a match based on a property of an object in an array which is a property of an object inside an input array you're supplying, not to mention [OR intra group] + [AND inter group] relations, search properties defined by either .title or .name , as well as criteria selection being completely dynamic. It's a complex problem.

Though it's a common scenario for shopping cart websites, I doubt that any web framework will have this kind of functionality built into its API. It's unfortunate but I don't think we can avoid writing the logic ourselves.

At any rate, since ultimately you want to just declare productFilter:search , here it is:

app.filter('productFilter', function($filter) {
    var helper = function(checklist, item, listName, search) {
        var count = 0;
        var result = false;
        angular.forEach(checklist, function(checked, checkboxName) {
            if (checked) {
                count++;
                var obj = {};
                obj[search] = checkboxName;
                result = result || ($filter('filter')(item[listName], obj, true).length > 0);
            }
        });
        return (count === 0) || result;
    };

    return function(input, definition) {
        var result = [];

        if (angular.isArray(input) && angular.isObject(definition)) {
            angular.forEach(input, function(item) {
                var matched = null;
                angular.forEach(definition, function(checklist, listName) {
                    var tmp;
                    if (listName !== 'colours') {
                        tmp = helper(checklist, item, listName, 'title');
                    } else{
                        tmp = helper(checklist, item, listName, 'name');
                    }
                    matched = (matched === null) ? tmp : matched && tmp;
                });
                if (matched) result.push(item);
            });
        }
        return result;
    };
});

A couple of notes:

  • How to use: ng-repeat="product in products | productFilter:search" .
  • The filter only does some basic checks: input must be an array, and definition must be an object. If you need more, you may do so there.
  • I would say that *.name is an exception to the rule (I assume that most of the criteria is defined by *.title ). So, we handle that in if/else .
  • The count variable in a helper function is used to track how many checked checkbox(es) we went through for a particular criteria group. If we went through none, it means that whole criteria group is inactive , and we just return true .
  • It's a good design to create a filter that doesn't mutate the states of other objects outside it. That's why using count is better than calling cleanObj() . This is especially crucial when designing common components for other devs to use in a team: you want to minimize the element of surprise as much as possible.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM