Ananthan Unni
Ananthan Unni

Reputation: 1304

AngularJS Dynamic Filter Configuration fail to resolve

I'm a newbie to AngularJS with some fair knowledge with KnockoutJS also.

I'm trying to implement a search feature on 'products' in my ViewModel that is configurable by the end user by combining..

in combination with search operations

I believe you understood the functionality I am trying to build up.

The following is the ViewModel I'm using.

var InstantSearchController = function ($scope) {
    var self = this;

    $scope.filtersAvailable = [
        {
            displayText: 'Tag',
            filterMethod: 'tagFilter',
            description: 'Filter by Tags'
        },
        {
            displayText: 'Description',
            filterMethod: 'descriptionFilter',
            description: 'Filter by description'
        }
    ];
    $scope.selectedFilter = $scope.filtersAvailable[1];

    $scope.filterBehaviorsAvailable = [
            {
                displayText: 'CONTAINS',
                regexPrefix: '',
                regexPostfix: ''
            },
            {
                displayText: 'STARTS WITH',
                regexPrefix: '^',
                regexPostfix: ''
            },
            {
                displayText: 'EQUALS',
                regexPrefix: '^',
                regexPostfix: '$'
            }
    ];
    $scope.selectedFilterBehavior = $scope.filterBehaviorsAvailable[0];

    $scope.products = [
        {
            name: 'Household Product',
            description: 'Description household',
            tags: ['personal', 'home']
        },
        {
            name: 'Office product',
            description: 'Business equipments',
            tags: ['office', 'operations', 'business']
        },
        {
            name: 'Misc products',
            description: 'Uncategorized items',
            tags: ['noclass']
        }
    ];
}

Now, the following is my filters list.

   var app = angular.module('InstantSearchModule', []);

//FILTERS BEGIN
app.filter('descriptionFilter', function () {
    var filterFunction = function (data, filterBy) {
        if (filterBy == null || filterBy === '')
            return data;

        var filtered = [];
        var regExp = new RegExp(filterBy, 'gi');
        angular.forEach(data, function (item) {
            if (item.description.match(regExp))
                filtered.push(item);
        });

        return filtered;
    };
    return filterFunction;
});

app.filter('tagFilter', function () {
    var tagFilter = function (data, filterBy) {
        if (filterBy == null || filterBy === '')
            return data;

        var filtered = [];
        var regExp = new RegExp('^' + filterBy, 'gi');

        debugger;
        angular.forEach(data, function (item) {
            var isMatching = false;
            angular.forEach(item.tags, function (t) {
                isMatching = isMatching || (t.match(regExp) != null);
            });

            if (isMatching)
                filtered.push(item);
        });

        return filtered;
    };

    return tagFilter;
});
// FILTERS END

I have created a working part to configure search criteria including the 'filterString'(in a textbox), search operand[tags or description](with a select list) and a search mode[starts with / contains / equals](with another select list). Both of the filters are working fine if I specify the filter functions (tagFilter or descriptionFilter) directly in AngularJS directives as follows [JSFiddle Here].

 <div data-ng-repeat="p in products|tagFilter:filterString|orderBy:'description.length'">        
        <h4 style="margin-bottom: 5px">{{$index+1}}.&nbsp;{{p.name}}</h4>
        <div>
            {{p.description}}
            <button data-ng-repeat="t in p.tags|orderBy:'toString()'">{{t}}</button>
        </div>
    </div>

I was expecting the following to work for me as {{selectedFilter.filterMethod}} is rendering the value successfully, but is showing an error. Please see the HTML I tried to use for it.JSFiddle Here

<div data-ng-repeat="p in products|{{selectedFilter.filterMethod}}:filterString|orderBy:'description.length'">        
        <h4 style="margin-bottom: 5px">{{$index+1}}.&nbsp;{{p.name}}</h4>
        <div>
            {{p.description}}
            <button data-ng-repeat="t in p.tags|orderBy:'toString()'">{{t}}</button>
        </div>
    </div>

I have attached the error I'm receiving in Google Chrome developer tools along with the resultant HTML to the subject. Please see below. Error info in Google Chrome Console As you can see in the HTML, the filter method is not resolved and so, its not working for me. Do you guys have an advice what I am doing wrong?

Upvotes: 0

Views: 181

Answers (2)

Edminsson
Edminsson

Reputation: 2506

If I understand it correctly all you need is a way to dynamically change filters. Everything else seems to be working. I dont think you can use the syntax you are trying to use but you can make a third filter that injects the two others and chooses the right one depending on the parameters you send in.

New filter:

app.filter('multiFilter', function (descriptionFilterFilter, tagFilterFilter) {
    var filterFunction = function (data, filterBy, filterRegExp, selectedFilter) {
      if(selectedFilter.displayText === 'Description') {
        return descriptionFilterFilter(data, filterBy, filterRegExp);
      }
      else {
        return tagFilterFilter(data, filterBy, filterRegExp);
      }
    };
    return filterFunction;
});

As you can see it also takes the filterRegExp and the selectedFilter as parameters. I also changed your old filters to take selectedFilter as a parameter. Also notice that you have to append "Filter" to the filter name in order to inject it.

You call the new filter like this

 multiFilter:filterString:filterRegExp:selectedFilter

So the div could loke something like this

<div data-ng-repeat="p in products|multiFilter:filterString:filterRegExp:selectedFilter|orderBy:'description.length'"
    title="{{selectedFilter.filterMethod}}">        
    <h4 style="margin-bottom: 5px">{{$index+1}}.&nbsp;{{p.name}}</h4>
    <div>

I made a working fork of your fiddle

Upvotes: 1

Chandermani
Chandermani

Reputation: 42669

Your fiddle is not working and has other error but, the reason filters are not loading is that you have used global controller function and not registered with your app module for the injection to work. Your filter belong to module InstantSearchModule but you controller does not.

Try the module registration syntax

app.controller('InstantSearchController',function($scope) {
});

see the Angular guide on controller https://code.angularjs.org/1.2.15/docs/guide/controller

Update: As it turns out the issue is not with dependency injection. It is because you cannot use expression to dynamically change filter. When i set to fixed filter it works fine

<div data-ng-repeat="p in products|descriptionFilter:filterString|orderBy:'description.length'" title="{{selectedFilter.filterMethod}}">

You would have to either combine then or find a way to do select filtering.

See my fix here http://jsfiddle.net/cmyworld/pW9EZ/1/

Upvotes: 1

Related Questions