AngularJS filter based on array of strings?
Asked Answered
M

1

9

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!

Maryellen answered 25/2, 2014 at 19:48 Comment(0)
I
25

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.

Itemize answered 25/2, 2014 at 20:21 Comment(8)
Solid answer covering the concept, however I think to be slightly more accurate to what the question wants it would be rejecting any Tasks containing a tag in a list of tags that are not selected in the interface. As opposed to accepting Tasks containing a tag in a list of tags that are selected in the interface.Brit
@Brit I'm not completely sure what you mean. Do you mean the "list of tags" should be of rejected/deactivated tags, rather than accepted/activated tags?Itemize
Yea, that's what it seemed like the question is asking for. Quoting '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.'Brit
I see your point, but I think the question is ambiguous to this point. 'All the buttons are blue by default, meaning "active" (in other words: show tasks with this tag contained therein).' Whether a task needs to have all its tags activated to be shown is not clear, if I'm not missing anything?Itemize
I think you are right. Rereading it, the language in the question is contradictory to itself so either interpretation is possible. 'Like so for visual reference -- grey = "hide tasks whose tags contains this tag", blue = "show tasks whose tags contain this tag":' In fact the interpretation you have is more common sense anyway.Brit
Yeah sorry guys the wording is a bit confusing. "Whether a task needs to have all its tags activated to be shown is not clear, if I'm not missing anything?" -- that's a good point that I hadn't considered. I think the way it's actually meant (gotta talk to my boss about it) is essentially: blue = do nothing.... grey = hide tasks that contain this tag, simple as that.Maryellen
@Maryellen Obtaining this functionality is as simple as swapping return false; and return true;. If the answer solves your problem, please mark it as accepted.Itemize
You are a lifesaver! I adapted this example to use Resources and Keyword instead of Tasks and Tags respectively and it worked a treat. The only difference is that when no tags are selected I want all resources to be displayed, so I have a conditional that says if (keywords.length > 0) { /* do the filtering */ } else {return true;}Intracardiac

© 2022 - 2024 — McMap. All rights reserved.