In my model I have data similar to:
$scope.list = [{id:0,tags:['tag1','tag2']},{id:2,tags:['tag2']}};
I want to show a list of tags (contains unique values of 'tag1' and 'tag2') with checkboxes. Hopefully something like:
<div ng-repeat="tag in list.tags">
<label class="checkbox">
<input type="checkbox" ng-model="filter.tag" />
{{tag}}
</label>
</div>
I know how to filter the main list based on whats checked if I hard code the list, but not how to generate the list of unique tags automatically.
You are looking to perform three operations:
$scope.list
You can do this with pure JavaScript, but to make things easier, I would recommend using Underscore, a library that gives you access to many functions for manipulating and inspecting arrays, objects, and so forth.
Let's start with this code:
$scope.list = [
{id: 0, tags: ['tag1', 'tag2']},
{id: 1, tags: ['tag2']},
{id: 2, tags: ['tag1', 'tag3', 'tag4']},
{id: 3, tags: ['tag3', 'tag4']}
];
Now, let's perform the first operation: get the array from the tags
property for each object in $scope.list
. Underscore provides the pluck
method, which is just what we need.
pluck
_.pluck(list, propertyName)
A convenient version of what is perhaps the most common use-case for map: extracting a list of property values.
Using pluck, we can get the following:
var tags = _.pluck($scope.list, 'tags');
// gives us [['tag1', 'tag2'], ['tag2'], ['tag1', 'tag3', 'tag4'], ['tag3', 'tag4']]
Now, we want to flatten that array.
flatten
_.flatten(array, [shallow])
Flattens a nested array (the nesting can be to any depth). If you pass shallow, the array will only be flattened a single level.
tags = _.flatten(tags);
// gives us ['tag1', 'tag2', 'tag2', 'tag1', 'tag3', 'tag4', 'tag3', 'tag4']
Finally, you only want one instance of each tag.
uniq
_.uniq(array, [isSorted], [iterator])
Alias:unique
Produces a duplicate-free version of the array, using === to test object equality. If you know in advance that the array is sorted, passing true for isSorted will run a much faster algorithm. If you want to compute unique items based on a transformation, pass an iterator function.
tags = _.unique(tags)
// gives us ['tag1', 'tag2', 'tag3', 'tag4']
We can combine these together with Underscore's useful chain
method to chain these together. Let's create a function on the scope that returns the unique tags:
$scope.uniqueTags = function() {
return _.chain($scope.list)
.pluck('tags')
.flatten()
.unique()
.value();
};
Since this is a function, it will always return the unique tags, no matter if we add or remove items in $scope.list
after the fact.
Now you can use ng-repeat
on uniqueTags
to show each tag:
<div ng-repeat="tag in uniqueTags()">
<label class="checkbox">
<input type="checkbox" ng-model="filter.tag" />
{{tag}}
</label>
</div>
Here is a working jsFiddle that demonstrates this technique: http://jsfiddle.net/BinaryMuse/cqTKG/
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With