AngularJS filter based on array of strings?

user2275203 picture user2275203 · Feb 25, 2014 · Viewed 30.6k times · Source

I'm having a hard time wrapping my head around how to go about doing an Angular filter to solve a problem that I'm running into.

Here's a basic example of my data structure, an array of tasks:

var tasks = [
    { Title: "This is a task title",
      Tags: ["Test","Tag","One","Two","Three"] },
    { Title: "Another test tag title",
      Tags: ["Some", "More", "Tags"] },
    { Title: "One more, why not",
      Tags: ["I","Like","Dirt"] },
    { Title: "Last one!",
      Tags: ["You","Like","Dirt"] }
];

So each object has an array of tags. For the sake of example let's say I am outputting each of those objects as a row in a table.

Once the pages ng-controller is initialized, I'm grabbing all of the tags from all of the tasks (without duplicates) and assembling them into a tags array.

Then, I'm outputting those tags as toggle-able buttons on the page. All the buttons are blue by default, meaning "active" (in other words: show tasks with this tag contained therein). I need to be able to click on one of those buttons to "toggle off" that tag -- which will filter out any tasks rows in the table wherein the task has that tag.

Like so for visual reference -- grey = "hide tasks whose tags contains this tag", blue = "show tasks whose tags contain this tag":

So you'd see a big row of buttons like this.

Clicking on a button turns it grey, filtering out any tasks in the table that have that tag. I can then click the buttons again to toggle that tag back on, re-showing all tasks with that tag.

Am I explaining this clearly enough? It's a confusing system.

Anyway, I've tried the following:

ng-filter="task in filteredWithTags = (tasks | filter: { tags: arrayOfTags }" to no avail.

Someone mind pointing me in the right direction? :)

PS: I had this working earlier this week by calling a filterByTag(tag) function in my controller. This would loop through every task in the array of tasks and if it had the tag that matched, it would hide that task. Similarly re-activating a tag would do the same thing, loop through everyone and work the magic... but I'm told that my method was slow + overkill, because "angular filters can handle all of that for you, and it will be more best-practicy". Hence, why I'm here, trying to figure out how to let Angular do the work for me :)

Any help is appreciated!

Answer

Mikke picture Mikke · Feb 25, 2014

You could write a custom filter. The filter would be given the list of active tags, tags, and the array of tasks to be filtered, tasks, and would output an array of filtered tags. It will be much the same as what you've already done, but with no extra function on the scope.

angular.module('myApp').filter('selectedTags', function() {
    return function(tasks, tags) {
        return tasks.filter(function(task) {

            for (var i in task.Tags) {
                if (tags.indexOf(task.Tags[i]) != -1) {
                    return true;
                }
            }
            return false;

        });
    };
});

Then you can use it like

<ul>
    <li ng-repeat="task in tasks | selectedTags:tags"></li>
</ul>

Check out this fiddle.