AngularJS filter on multiple lists of multiple checkboxes - angularjs

I apologise if this has been answered already, but I'm new to Angular so might have missed it.
I have a need to provide multiple sets of checkbox lists, which need to be combined to create an AND query.
It's on Plunker here http://plnkr.co/OGmGkz22n4J4T8p74yto but enclosed below. At the moment I can select the bottom row and the correct names appear from storeArray, but I cannot work out how to add the Format array into the filter.
I've tried:
<div ng-repeat="store in storeArray | filter:(formatFilter && tillFilter)">
and
<div ng-repeat="store in storeArray | filter:formatFilter:tillFilter">
but they don't work.
Any suggestions please?
var myApp = angular.module('myApp', []);
function MyCtrl($scope) {
$scope.formatFilter = function(a) {
for (var fmt in $scope.formatsArray) {
var f = $scope.formatsArray[fmt];
if (f.on && a.format.indexOf(f.name) > -1) {
return true;
}
}
};
$scope.tillFilter = function(a) {
for (var till in $scope.tillsArray) {
var t = $scope.tillsArray[till];
if (t.on && a.tills.indexOf(t.name) > -1) {
return true;
}
}
};
$scope.formatsArray = [{
name: "Super",
on: false
}, {
name: "Express",
on: false
}, {
name: "Other",
on: false
}];
$scope.tillsArray = [{
name: "Main",
on: false
}, {
name: "Service",
on: false
}, {
name: "Petrol",
on: false
}];
$scope.storeArray = [{
"id": "1",
"name": "101",
"format": "Super",
"tills": ["Main", "Service", "Petrol"]
}, {
"id": "2",
"name": "102",
"format": "Express",
"tills": ["Main", "Service"]
}, {
"id": "3",
"name": "103",
"format": "Other",
"tills": ["Main", "Petrol"]
}, {
"id": "4",
"name": "104",
"format": "Super",
"tills": ["Service", "Petrol"]
}];
}

While you can chain filters together like this:
<div ng-repeat="store in storeArray | filter:formatFilter | filter:tillFilter)">
This won't fix your problem since the first filter will do it's job, and filter items out that you may want to include in your second filter. I'm not sure of any way to do an "or" filter. Is there any reason you can't do a custom filter that includes both? I modified your plunker with a custom filter:
http://plnkr.co/edit/han1LFl7toTsSX27b9Q0?p=preview
The code isn't super clean... it does the job. :) You may want to polish it up a bit.

Related

AngularJS Filed nested array of objects with array of objects

I'm trying to filter a nested array of objects with my own objects, by idSubject. But I'm not getting the right result.
I have articles (which have subjects)
And a array of objects (which are the subjects I want to filter the articles with)
Data looks like this:
So I'm trying to filter the array of articles by its subjects.
I tried the following:
<div class="panel panel-default"
ng-repeat="searchArticle in searchArticles | filter: {subjects: filterSubjects} as articleSearchResult">
So filterSubjects is the second screenshot and SearchArticles is the first screenshot.
Without much luck.
Hope you can help, please tell me if things are still unclear.
This custom filter will help you.
Example : http://plnkr.co/edit/jMizCLxPH6DtDA5wL15Q?p=preview
HTML:
<body ng-app="myApp">
<div ng-controller="MainCtrl">
<h2>Select Subjects</h2>
<div ng-repeat="subject in subjects">
<label>
<input type="checkbox" ng-model="filterSubjects[subject.id]" ng-true-value="'{{subject.id}}'" ng-false-value="''">{{subject.name}}</label>
</div>
<h2>Filtered Articles</h2>
<div ng-repeat="searchArticle in searchArticles | subjectFilter:filterSubjects">{{searchArticle.name}}</div>
</div>
</body>
JS:
var app = angular.module('myApp', []);
app.controller('MainCtrl', function($scope) {
$scope.searchArticles = [{
"name": "Article1",
"sid": "1"
}, {
"name": "Article2",
"sid": "1"
}, {
"name": "Article3",
"sid": "2"
}];
$scope.subjects = [{
"name": "Subject1",
"id": "1"
}, {
"name": "Subject2",
"id": "2"
}];
$scope.filterSubjects = [];
});
app.filter('subjectFilter', function() {
return function(articles, filterSubjects) {
filtered = articles.filter(function(e){return filterSubjects.indexOf(e.sid) >= 0},filterSubjects);
return filtered;
}
});
if you want to filter based on object :
var app = angular.module('myApp', []);
app.controller('MainCtrl', function($scope) {
$scope.searchArticles = [{
"name": "Article1",
"sid": "1"
}, {
"name": "Article2",
"sid": "1"
}, {
"name": "Article3",
"sid": "2"
}];
$scope.subjects = [{
"name": "Subject1",
"id": "1"
}, {
"name": "Subject2",
"id": "2"
}];
$scope.filterSubjects = [{
"name": "Subject1",
"id": "1"
}, {
"name": "Subject1",
"id": "2"
}];
});
app.filter('subjectFilter', function() {
return function(articles, filterSubjects) {
var sFiltered = [];
for (var i = 0; i < filterSubjects.length; i++) {
sFiltered.push(filterSubjects[i].id);
}
var filtered = articles.filter(function(e) {
return sFiltered.indexOf(e.sid) >= 0;
}, sFiltered);
return filtered;
}
});

Angular filtering object based on array of strings

I'd like to be able to filter an entire object based on an array of strings. Currently the default filter will search an entire object based on a single string value, but not with an array of strings.
current jsfiddle
<div ng-controller="myController">
<div>
<ul determine-filtering>
<li get-filter-tag active-state="false" tag="{{button}}"
ng-repeat="button in buttons">
{{button}}
</li>
</ul>
<hr/>
<ul>
<li ng-repeat="item in content | filter:filterArray">
{{item.data.headline}}
</li>
</ul>
</div>
</div>
app.js
var testapp = angular.module('testapp', [])
.filter('inArray', function($filter){
return function(list, arrayFilter){
if(arrayFilter.length > 0){
console.log(arrayFilter);
return $filter("filter")(list, function(listItem){
return arrayFilter.indexOf(listItem) != -1;
});
}else{
return $filter("filter")(list, function(listItem){
return arrayFilter.indexOf(listItem) == -1;
});
}
};
})
.directive('getFilterTag', function () {
return {
link: function postLink(scope, element, attrs) {
element.on('click', function(){
var tag = attrs.tag;
var filterArray = scope.filterArray;
if(filterArray.indexOf(tag) === -1){
scope.filterArray.push(tag);
}else{
filterArray.splice(filterArray.indexOf(tag), 1);
}
scope.$apply();
});
}
};
})
.directive('activeState', function () {
return {
link: function (scope, element, attrs) {
element.on('click', function(){
attrs.activeState = !attrs.activeState;
if(!attrs.activeState){
$(this).addClass('active');
}else{
$(this).removeClass('active');
};
});
}
};
})
.controller('myController', function($scope){
$scope.filterArray = [];
$scope.buttons = ['corn', 'vegetable', 'onion'];
$scope.content = [
{
"type": [
"recipe"
],
"data": {
"prepTimeInMinutes": 10,
"serves": "6 to 8",
"headline": "North Carolina Piedmont Slaw",
"ingredients": [
{
"item": "medium head cabbage",
"quantity": {
"number": 1
},
"notes": "cored and chopped (5 to 6 cups)"
},
{
"unit": "cup",
"item": "ketchup",
"quantity": {
"number": 1
}
},
{
"unit": "tbsp.",
"item": "sugar",
"quantity": {
"number": 3
}
},
{
"unit": "tbsp.",
"item": "apple cider vinegar",
"quantity": {
"number": 1
}
},
{
"unit": "tsp.",
"item": "kosher salt",
"quantity": {
"fraction": {
"display": "½",
"denominator": 2,
"numerator": 1
}
}
},
{
"unit": "tsp.",
"item": "black pepper",
"quantity": {
"fraction": {
"display": "½",
"denominator": 2,
"numerator": 1
}
},
"notes": "freshly ground"
},
{
"item": "Generous dash hot sauce, such as Texas Pete Hot Sauce or Tabasco brand"
}
],
"description": "",
"cookTimeInMinutes": 180,
"categories": {
"Dish Type": [
"Side"
],
"Main Ingredient": [
"Vegetable"
]
},
"cookingDirections": [
{
"step": "Place the cabbage in a large bowl."
},
{
"step": "Combine the ketchup, sugar, vinegar, salt, pepper and hot sauce in a liquid measuring cup. Pour over the cabbage and toss to coat thoroughly. Cover and refrigerate for at least 3 hours, and preferably overnight, before serving."
},
{
"step": "Serve on top of the pulled pork"
}
]
}
}
]
} );
I have a custom filter which worked with arrays, but not with arrays with nested objects. What is the best approach to do this kind of filtering? Is there anything the $filter service already provides for searching based on arrays?
If you need to filter an object based on an array of strings (matching the objects properties) I would look into lodash#omit.
Usage:
var obj = { a: 'a', b: 'b', c: 'c' };
var arr = ['a', 'c'];
_.omit(obj, arr); // { b: 'b' }
I'm not sure whether omit supports deep/nested objects. If not, you could compose it with an iterator to determine whether the given key of an object is an object itself and run another omit on that.

AngularJs filter is not working correctly

Please see the link
http://plnkr.co/edit/Cn0sNDGEkPOzV19ye8NW?p=preview
I have changed my JSON, then I can't able to filter the data.
var result = $filter('filter')(foo.results, {id:2426})[0];
if you limit on your JSON data
and you can make sure the data property value is num
maybe you can try this ...
var foo = {
"count": 70,
"language": "en",
"0": {
"id": "2420",
"name": "Medical Center"
},
"1": {
"id": "7840",
"name": "Conference Room"
},
"2": {
"id": "2426",
"name": "Deck 5 Starboard"
}
};
foo.results = [];
for(var property in foo){
if(property == parseInt(property, 10).toString()){
foo.results.push(foo[property]);
}
}
var result = $filter('filter')(foo.results, {id:2426})[0];
$scope.name = result.name;
wish help !!

How to automatically update connected parts of json with angularjs

I have next json structure:
{
"items": [
{"name":"item1"},
{"name":"item2"},
{"name": "item3"}
],
"groups": [{
"name": "group1",
"items": ["item1", "item3"]
},
{
"name": "group2",
"items": ["item2", "item3"]
},
{
"name": "group3",
"items": ["item1", "item2"]
}]
}
As you can see in my groups I have names of the items.
Is there a way in angularjs to automatically update strings in group > items, when the name of the particular item change. (Is there a way to connect particular parts of the json model?)
Thank you.
You can set up a deep $watch for each item in the item array. When the item changes, iterate over the group items, and replace the old name with the new name:
angular.forEach($scope.model.items, function (item) {
$scope.$watch(function () { return item; }, function (newVal, oldVal) {
angular.forEach($scope.model.groups, function (group) {
var items = group.items;
var itemIndex = items.indexOf(oldVal.name);
if (itemIndex >= 0) {
items[itemIndex] = newVal.name;
}
});
}, true);
});
Demo Fiddle

ng-repeat does not react to given limitTo or filter

I have a directive that displays subcategories of a feature list.
Problem is, all items are displayed, regardles of the filters i use for the ng-repeat.
I use a filter to only show items with a certain category_uid and i also use a limitTo.
app.directive('featureCategory', function() {
return {
restrict: 'A',
transclude: true,
link: function($scope) {
$scope.featureCountBuffer = $scope.featureCount;
$scope.listLength = Object.keys($scope.featureList).length;
},
scope: {
featureList: '=',
featureCount: '#',
categoryId: '#'
},
template:
'<li>featureCountBuffer : {{featureCountBuffer}} / listLength {{listLength}} / categoryId {{categoryId}}</li>' +
'<check-item ng-repeat="feature in featureList | filter:{category_uid:categoryId} | limitTo: featureCountBuffer" feature="feature"></check-item>'
}
});
This shows above 40 items thou it starts with the li element showing featureCountBuffer is 5 andcategory_uid being 1 which only applies to 8 items.
Does anyone see my Mistake here?
The featureList looks like this:
{
"1": {
"uid": "1",
"title": "foo",
"category_uid": "1",
"checked": false
},
"2": {
"uid": "2",
"title": "bar",
"category_uid": "1",
"checked": false
},
"3": {
"uid": "3",
"title": "foobar",
"category_uid": "2",
"checked": false
},
"4": {
"uid": "4",
"title": "barfoo",
"category_uid": "2",
"checked": false
},
"5": {
"uid": "5",
"title": "barbar",
"category_uid": "3",
"checked": false
}
...
}
I found the Problem.
Angulars ng-repeat works with objects of objects but the filters do not.
You can either write your own filters to fix this or just use an array of objects.
I used lodash's _.toArray to convert mine. My Changes:
[..]
link: function($scope) {
[..]
$scope.featureList = _.toArray($scope.featureList);
},
[..]
Youcan read about it in more detail in this article.
Thanks for posting this! I found an alternative solution to use the $index property:
<ul>
<li ng-repeat="(id, value) in test" ng-if="$index < 3" >
{{ $index }} {{ id }} {{ value }}
</li>
</ul>

Resources