Filter ng-repeat by status - javascript

I have a dropdown with values in it. They're accessed through the .value property. I have an ng-repeat on a div that is repeating a lot of data. That data has statuses. When a user selects to filter by a status in the dropdown, I want to filter the ng-repeat by whatever status they chose and the status in the ng-repeat. Here's a better example of what I mean:
data-ng-repeat="stackoverflow in overflows| filter:stackoverflow.property.status===status.value"
In my case, I need to access stackoverflow.property.status and compare it to whatever status is in the drop down.

You'll want to do something like the following:
<span ng-repeat="stackoverflow in overflows | filter: { status : selectedStatus }"></span>
selectedStatus will be the value of ng-model on your dropdown.

You could have something like this
ng-repeat="stackoverflow in overflows | filter: { locations: [{ status: status.value }] }"

Under the circumstances, it ended up just being easier to write my own function and filter by it. In this case, I have a function search that I pass to filter. Works great for me.
I followed an answer that I found on another question. You can view it here: http://jsfiddle.net/suCWn/
$scope.search = function (shop) {
if ($scope.selectedCityId === undefined || $scope.selectedCityId.length === 0) {
return true;
}
var found = false;
angular.forEach(shop.locations, function (location) {
if (location.city_id === parseInt($scope.selectedCityId)) {
found = true;
}
});
return found;
};`
Here's the question: Angularjs filter nested object

Related

how to filter ng-reapet values according to my ng-model?

Beginner in Angular, so it might sound a little silly question, but couldn't find an answer yet.
I have two select boxes -
one which describes a module which I use as ng-model=module ([x,y,z]).
The second one is an array which in each index I have an array with 3 attributes - id, name and module( [1, "first", x])
I am using ng-repeat for my second select box and I want to filter according to the module and the third index.
Basically, it's something like that: "option in options | filter: module === secondbox[2]", but obviously I'm doing something wrong, maybe by syntax.
Please assist me to execute it right. Thanks!
I think it would be best to write a custom filter for this:
.filter('moduleMatch', function() {
return function(items, module, itemIndex, moduleIndex) {
let out = [];
// make sure a filter value was supplied
if (module) {
items.forEach(i => {
if (i[itemIndex] === module[moduleIndex]) {
out.push(i);
}
});
// return the items that matched the filter value
return out;
}
// no filter value was supplied - return the unfiltered collection
return items;
}
})
Then use it in the second select:
"option in options | moduleMatch: module:2:2"

Angular JS filter - Do not update DOM value if new value is empty

I'm working on a data heavy Angular project in which I have a ng-repeat where many values get updated every second. If a value is empty, it now updates the DOM to show an empty value; which is correct behaviour.
Needed solution
What I want is a filter or expression which doesn't update the value in the DOM when the new value is empty or NULL. Latching data, I believe they call it.
Possible solutions
I found a couple of possible solutions with $watch, but I believe they are not suitable in ng-repeat, or at least not efficient:
angularjs $watch old value and new value are the same
Example of what I would like to achieve: (this does not work)
app.filter('latchIt', function () {
return function (valueOld, valueNew) {
if (valueNew == '') {
// Do not update the DOM for this value, perhaps a return of the old value
return valueOld;
} else {
return valueNew;
}
};
});
HTML
<div class="item" ng-repeat="item in items track by item.id">
<div class="value" ng-repeat="value in item.data">{{ value | latchIt }}</div>
</div>
Thanks in advance for any help & advice you can give me.
I'd create a directive for that:
codepen: http://codepen.io/anon/pen/EVXgjz
angular.module('app').directive('latched', [function() {
return {
template: '<span>{{saved}}</span>',
link: function($scope) {
$scope.$watch('value', function(val) {
if (!$scope.saved) {
$scope.saved = val || 'Not defined yet';
}
if (val) {
$scope.saved = val;
}
})
},
scope: {
value: '='
}
}
}])
Well I would say that $watch option is good enough to use it, but if you are agains it you can try to combine old collection values and new collection values according to your business rules in controller/directive and after than pass it to view.

AngularJs: programmatically filter by either of 2 columns

I have the following code in my controller:
$scope.filteredTransactions = $scope.invoiceTransactionsObject.transactions.concat(); // make a copy of the initial array
if ($scope.searchTerm.message)
{
var search = $scope.searchTerm.message;
$scope.filteredTransactions = $filter('filter')($scope.filteredTransactions, ({ message: search } || { item: search }));
}
I want to be able to filter by typing some value and search either in the message column or item column. According to How to filter multiple values (OR operation) in angularJS it should work, but it doesn't and if I type something that can be found in the message, it works, but when I type something from the item, it returns empty array.
Do you see where is my mistake?
Update Deleted irrelevant/mistaken initial answer
Since you're applying $filter inside a JS script, and it doesn't use any of the advanced features of $filter, I'd switch over to the JS-native method of filtering an array:
$scope.filteredTransactions = $scope.invoiceTransactionsObject.transactions.concat(); // make a copy of the initial array
if ($scope.searchTerm.message)
{
var search = $scope.searchTerm.message;
$scope.filteredTransactions = $scope.filteredTransactions.filter(function (trans) {
return trans.message.toLowerCase().indexOf(search) >= 0 || trans.item.toLowerCase().indexOf(search) >= 0;
});
}
...assuming that $filter is case-insensitive and matches anywhere in the string. If that's not the functionality of $filter and/or not what you want, you'd adjust the code accordingly.

Angularjs filtering over array of objects in ngRepeat doesn't restore items with empty search property

Please, consider the following example:
Template:
<body ng-controller="MainCtrl">
Search: <input ng-model="search.$" ><br>
Search by tag: <input ng-model="search.tag" >
<p ng-repeat="item in items | filter:search">
<span>{{item.content}}</span> <span>{{item.tag}}</span>
</p>
</body>
Script:
app.controller('MainCtrl', function ($scope, $filter, filterFilter) {
$scope.items = [
{content:'3333113', tag:['a','b','c']},
{content:'111111g', tag:['b','c','d']},
{content:'345345', tag:[]},
{content:'2221122', tag:['c','d','e']},
{content:'2221122', tag:[]},
{content:'333', tag:[]}
];
});
When searching via the first input ng-model="search.$" with any data everything is Ok.
When searching via the seond input ng-model="search.tag" search does work by tags like a, b, but when it is cleared the restored array lacks the items which had empty search value, e.g. {content:'2221122', tag:[]} in this example.
jsBin example
Why does it happen ? Is there an easy way to avoid it ?
Short answer:
here is a working code: http://jsbin.com/AwunOyAt/2
You need a directive to make search.tag undefined when it's empty:
Directive:
app.directive('modelUndefined', function(){
return {
require: 'ngModel',
link: function(scope,elm,attrs,ngModel){
ngModel.$parsers.push(function(val){
return val === "" ? undefined : val;
});
}
}
});
html:
<input ng-model="search.tag" model-undefined>
Long answer:
From the docs of filter:filter:
In HTML Template Binding
{{ filter_expression | filter:expression:comparator }}
Parameters#expression
Object: A pattern object can be used to filter specific properties on objects contained by array. For example {name:"M", phone:"1"} predicate will return an array of items which have property name containing "M" and property phone containing "1". A special property name $ can be used (as in {$:"text"}) to accept a match against any property of the object. That's equivalent to the simple substring match with a string as described above.
As you can see, the filter expression can be an object with more then one predicates.
how to trace it?
Initially ngModel won't set search.tag until there is an input so it's still undefined.
First I pass an input into search.$, the search object looks like so:
$scope.search = {
'$' : 'something'
}
Then I'll pass something into search.tag, the search object:
$scope.search = {
'$' : 'something',
'tag': 'anything'
}
But when I clear it then the search object still have the tag property
$scope.search = {
'$' : 'something',
'tag': ''
}
filter:filter filters based on both predicates, this is the source code:
case "object":
// jshint +W086
for (var key in expression) {
(function(path) {
if (typeof expression[path] == 'undefined') return;
predicates.push(function(value) {
return search(path == '$' ? value : (value && value[path]), expression[path]);
});
})(key);
In our case the expression is the search object , and the paths are $ and tag.
See this line: if (typeof expression[path] == 'undefined') return;
If we set search.tag = undefined , the filter ignores it.
But If we set search.tag = '' this tag path is added to the predicates check array.
How to enforce ngModel to make search.tag undefined when it's empty?
See the directive above, you need to use ngModelController#$parsers to change the way the view value is converted when it updates the model.
I do not know if it helps but I put in the following:
<p>{{search.tag == undefined}}</p>
It then showed that initially it is undefined und later on it is an empty string (if you empty the input). The search results then kind of make sense.
Looks like there is a change in AngularJS version 1.2.10. Using https://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.min.js does not eliminate empty entries.

Angular filter with minimum and maximum values

I'm having some trouble getting angular to properly filter my results. I'm attempting to use a custom filter that gets arguments from a minimum input and a maximum input.
/index.html
<input ng-model="minHorsepower">
<input ng-model="maxHorsepower">
...
tr(ng-repeat="plane in planes | filter:horsepowerFilter")
/controllers.js
//Horsepower filter
$scope.horsepowerFilter = function(plane) {
var ret = true;
if($scope.minHorsepower && $scope.minHorsepower > plane.horsepower) {
ret = false;
}
if($scope.maxHorsepower && $scope.maxHorsepower < plane.horsepower) {
ret = false;
}
return ret;
};
$scope.planes = [
{
'make' : 'Piper',
'model' : 'Arrow',
'modelNumber' : 'PA-28R-180',
'horsepower' : '180',
'gear' : 'retractable',
},
{
'make' : 'Piper',
'model' : 'Arrow',
'modelNumber' : 'PA-28R-200',
'horsepower' : '200',
'gear' : 'retractable',
}
];
It works INITIALLY when I set $scope.minHorsepower/$scope.maxHorsepower in controllers.js, but only initially, not when I put something else in the <input>s. Furthermore, it prefills the inputs AND filters the results. It just doesn't work properly when I change the value of the inputs.
I've referenced this Stack Overflow thread, but I can't find any material differences in our code... AngularJS multiple filter with custom filter function
Thanks for the help.
To ensure the model values are numbers and not strings, change the type for your inputs to be number.
<input type="number" ng-model="minHorsepower" />
<input type="number" ng-model="maxHorsepower" />
Also, make sure your model values are numbers and not strings.
Alternatively, you can run everything through parseFloat(...) in your filter.
Unfortunately, I can't pinpoint exactly the problem you're having with your code. However, I think I have created a plnkr that (I believe) works as intended.
Apart from parsing the inputs with parseFloat, the logic seems to be the same. Not parsing the inputs to a numeric form shouldn't "break" it, but will possibly make it behave strangely ("9" > "10" for example).
Anyway, hopefully you can pull out the useful pieces and get it working.
Since you only send to the filter a function, it doesn't know to watch for any value changes, and thus to trigger the filter function when it happens.
When you define a filter as {{ filter_expression | filter : filterValue}}, angular watches the filterValue and triggers the filter function when it changes.
To achieve what you need you can define your own filter:
angular.module('myApp')
.filter('range', function(){
return function(items, property, min, max) {
return items.filter(function(item){
return item[property] >= min && item[property] <= max;
});
};
});
and call it like this:
ng-repeat="plane in planes | range : 'horsepower' : minHorsepower : maxHorsepower"

Categories