AngularJS : custom filter with special rules - angularjs

I'm trying to make multi filter, that filters three properties in object.
what I already did:
<div class="" ng-repeat="selectedCard in Cards | filter {
status:filterValueStatus,
monitorLevel:filterValueType,
monitorSystem:filterValue
} ">
The problem is that I need to show the object if status equals to '2' , and not continue to the two filtered properties : monitorLevel and monitorSystem that come after. (It doesn't work).
Also I need to show the object if status equals to '3' , then filter it by the two properties monitorLevel and monitorSystem.
In summary it needs to check the status and only after , to decide whether to make the other two filters or not.
The filter I built until now :
app.filter('cardFilter', ['$filter', function($filter) {
return function(status, monitorLevel,monitorSystem) {
if (status == '2') {
return $filter('filter')(status);
} else {
return $filter('filter')(status,monitorLevel,monitorSystem);
}
};
}]);
the answer :
<div class="" ng-repeat="selectedCard in Cards | filter: filterValueStatus == '2' ? {status:'2'} : {status:'3', monitorLevel:filterValueLevel, monitorSystem:filterValue}">

You can split your filters into 2 and use ternary operation. You get the concept:
<div ng-repeat="selectedCard in Cards | filter : { status:filterValueStatus} |
filter : selectedCard.status === 2 ? {} : {
monitorLevel:filterValueType,
monitorSystem:filterValue
}
} ">

the answer is not so complex as I thought it would be as start.
<div class="" ng-repeat="selectedCard in Cards | filter: filterValueStatus == '2' ? {status:'2'} : {status:'3', monitorLevel:filterValueLevel, monitorSystem:filterValue}">
It first , check if status equals to '2' and only afterwords filters the cards, according to the status.

Related

AngularJS filter of more than one property

I use this filter in my AngularJS application and now I need also to filter or firstname. Currently my filter looks like this:
| filter: {user: {surname: searchEmployeesText}}
Is there a possibility to filter also of firstname without a filter function?
Create your custom filter gives you more flexibility,
HTML
<li ng-repeat="user in users | filter:isExists(searchText)">First : {{user.name}} || Last : {{user.lname}}</li>
JS here i am searching entered searchText in fname as well as in lname.
$scope.isExists = function (searchText) {
return function (user) {
return (!searchText || (user.lname.indexOf(searchText) > -1) || (user.name.indexOf(searchText) > -1));
}
};
DEMO

OR logic on builtin filters in AngularJs

I have an object that looks something like this:
{HospitalName: "hospital name", DoctorDetails: {Name: "doctor name"}}
I am using ng-repeat to iterate over a list of such objects. At the same time, I am applying two filters with an OR logic such that at least one of them should be return a match(if any).
<input type="text" ng-model="searchQuery" placeholder="Search by hospital or doctor name">
<div ng-repeat="hospital in list | filter: {HospitalName : searchQuery} || {DoctorDetails : {Name : searchQuery}}">
<div>{{hospital.HospitalName}}</div>
</div>
However, only the first filter gets triggered. Even though there are objects that have doctor names that match the search query, no matches are returned. Any ideas where I am going wrong?
I was using this post as reference for the conditional logic on the filter: Making an Angular Filter Conditional
UPDATE:
I was not able to figure out why the conditional logic was not working. So, taking #Walfrat's advice,I had to resort to using a custom filter and it gets the job done. If anyone is interested, here is the code I used:
angular.module('TestApp')
.filter('searchBox', function($filter, $rootScope) {
return function(input, searchParam) {
if(input) {
var searchResults = null;
//check if a valid search query has been entered
if(searchParam){
//use $filter to find hospital names that match query
searchResults = $filter('filter')(input,{HospitalName: searchParam});
//if no hospital names match, fill searchResults with doctor name results(if any) that match query
//otherwise just append the results to searchResults
if(searchResults.length == 0){
searchResults = $filter('filter')(input,{DoctorDetails : {Name: searchParam}});
}
else{
angular.forEach($filter('filter')(input,{DoctorDetails : {Name: searchParam}}), function(result){
searchResults.push(result);
});
}
//if there are no doctor or hospital names that match,
// set searchResult to approprriate message to denote no results found
if(searchResults.length == 0){
searchResults = ["Nothing"];
}
return searchResults;
}
//console.log(out);
return input;
}
return [];
}
});
For more concise code, see #Alex Chance's answer. Cheers!
In your situation, it may be easier to apply a custom filter function.
You apply the filter in your ng-repeat like so
<div ng-repeat="hospital in list | filter:filterFn ">
The filter function would look like this:
$scope.filterFn = function(hospital)
{
return hospital.HospitalName.search($scope.searchQuery) != -1 ||
hospital.DoctorDetails.Name.search($scope.searchQuery) != -1;
}
Here is a working fiddle.

ng-class with multiple options

I'm fetching data using a REST API and one of the attributes returned can be any of 3 options;
The options are; Negative, Warning, Success.
I want to set a class using ng-class based on the value returned;
I'm able to do this but only for one;
Code below;
<div class="alert" ng-class="restaurantNotice(restaurant[0].notice)" ng-if="restaurant[0].notice.length">
<div class="alert-inner inner-large" ng-bind="restaurant[0].notice"></div>
</div>
$scope.restaurantNotice = function(a) {
return (a = Negative) ? 'negative' : '';
};
As you can see, if a = Negative then a class negative is applied else nothing is applied.
Now I want to check for all three options and apply 3 different classes respectively
Instead of function in ng-class, use object
ng-class="{'negative' : restaurant[0].notice == 'Negative', 'warning' : restaurant[0].notice == 'Warning', 'success' : restaurant[0].notice == 'Success'}"
You can try
<div class="alert" ng-class="{'negative' : restaurant[0].notice.length, 'warning' : restaurant[1].notice.length, 'success' : restaurant[2].notice.length}">
<div class="alert-inner inner-large" ng-bind="restaurant[0].notice"></div>
</div>
duplicate of Adding multiple class using ng-class
In this way:
<p ng-class="{a: deleted, b: important, c: error}">Example</p>
if a is true, apply class deleted, and so on.
If you are just going to set the same notice type (Negative, Warning, Success) as class then just convert to lower case and return from the function instead of putting conditions.
$scope.restaurantNotice = function(a) {
return a.toLowerCase();
};
OR
<div class="alert" class="{{restaurant[0].notice | lowercase}}" ng-if="restaurant[0].notice.length">

How to use filters in Angular JS by matching properties dynamically

I have a use case wherein I need to filter a list on the basis of selections from various drop down (one at a time). I am aiming at making a common function to be called on ng-change from dropdown as follows:
$scope.filterTasks = function(selection, filterOn) {
if (filterOn === "trade") {
$scope.tasks.forEach(function(t) {
if ((t.team.code === selection.team.code) && (t.deal.code === selection.deal.code)) {
t.filtered = "trade";
}
$scope.filterBy = "trade";
});
}
else if (filterOn === "cluster") {
//similar approach in this case
}
}
and in html I am doing something like:
<li ng-repeat="t in tasks | filter : {filtered: '{{filterBy}}'} : true track by t.tid" class="li_row" ng-if="!t.hide">
By default I am keeping filtered property as 'show' and $scope.filterBy = 'show', so that all the objects in array get displayed.
This do not seem to be working.
Is there some invalid approach up here. What can be a suitable way to achieve this. (i.e display all elements by default, and on selection from dropdown, display only those element where some deep properties of selection matches those properties in array objects).
I have found few post demonstrating above, but could not grasp much out of them

how many times a filter gets called

assume a filter like
app.filter('unread', function () {
return function (note) {
console.log(note);
return (note.status == 'unread');
};
});
I use this filter on an array in $rootScope
<span ng-class="(note| unread).length == 0 ? '' : 'active'">{{value.length}}</span>
in which $rootScope.note is an array. the span element is outside of ng-view and it's not related to $scope and I have many arrays of objects in the $scope.
I thought that the filter would log the note in the number of $rootScope.note.length. but It logs much more of it and I can't figure out a reasonable relation between elements of $rootScope, $scope and the logs in the console. may you please explain about this?
Edit:
filter corrected.
Isn't your html call to the filter incorrect ?
Try removing the :note like this :
<span ng-class="(note| unread).length == 0 ? '' : 'active'">{{value.length}}</span>
What's after the semicolon are additional arguments. For example if you wanted to filter important unread notes only :
app.filter('unread', function () {
return function (note, type) {
console.log(note);
return (note.status == 'unread' && note.type == type);
};
});
<span ng-class="(note| unread:'important').length == 0 ? '' : 'active'">{{value.length}}</span>

Resources