Take the 2-minute tour ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

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..

  • Search by 'name' of product
  • Search by 'tags' of product

in combination with search operations

  • CONTAINS
  • STARTS WITH
  • EQUALS

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?

share|improve this question
    
Where have you register InstantSearchController controller with module. –  Chandermani Jul 24 at 7:34
    
Its there in the Fiddle. The first line in my JS code. –  Ananthan Unni Jul 24 at 7:36
    
My bad.. I didn't know that kind of a registration process exists. Can you guide me please? –  Ananthan Unni Jul 24 at 7:38

2 Answers 2

up vote 1 down vote accepted

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

share|improve this answer
    
Thanks, Edminsson. I integrated your changes to my solution as I had some more changes done yesterday, leaving one of my filters there, hardcoded. Now its running fine. Thank you so much. Much appreciated. :) –  Ananthan Unni Jul 25 at 6:25

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/

share|improve this answer
    
Let me check with that. Thank you for your time. I didn't know Global controller and registration exists as its just my DAY#2 with AngularJS. Still learning. –  Ananthan Unni Jul 24 at 7:41
    
Reading documentation always helps :) –  Chandermani Jul 24 at 7:42
    
I tried it here locally but it says Circular Dependancy. Can you please edit my fiddle (jsfiddle.net/3GLcC/1)? Actually the first fiddle (jsfiddle.net/3GLcC) is the working one, but without that dynamic search implemented. –  Ananthan Unni Jul 24 at 7:50

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.