Making an angular filter conditional - angularjs

I'm currently using a text input to filter a list of items. I'd like to make it so when a particular variable is set, the list doesn't filter, regardless of what the text input is. Any advice on how I can accomplish this?
<a ng-repeat="set in data | filter: { value: search }" data-id="{{set.id}}" ng-mousedown="setBox(set)" ng-mouseover="setSelected(set, $event)" ng-bind-html="set.value | trustHTML"></a>

You can achieve this if you set the filter expression to '' (or undefined) - this causes the filter not to be applied - for when your disableFilter is set, or to the actual filter expression otherwise.
EDIT 2:
The other answer (below by #Ryan) is simpler and easier to understand. Can't remember now whether it didn't work for me initially or I simply didn't think of this simpler way.
So, assuming, this toggling variable - disableFilter - is a boolean :
<a ng-repeat="set in data | filter: (!disableFilter || '') && filterExpression">
(with filterExpression being whatever the expression you want to filter by). Your specific case would be:
<a ng-repeat="set in data | filter: (!disableFilter || '') && {value: search}">
EDIT:
To explain how the above works.
Remember that || and && return the value of one of its operands.
|| and && use short-circuit evaluation - true || (anything) returns true; false && (anything) returns false - without evaluating the (anything) expression.
'' is falsy (or use undefined instead, if it's clearer)
And so,
when disableFilter === true, !disableFilter === false, thus the second operand of || - the empty string '' - is evaluated (it's falsy), and (!disableFilter || '') returns '' - a falsy value, which short-circuits the && operation and does not evaluate the second operand of &&. The return value of the expression is thus ''.
when disableFilter === false, !disableFilter === true, which short-circuits the || operation, then the second operand of && is evaluated and returned. The return value of the expression is thus {value: search}.
Read more about logical operators here

I think the following is a slightly less tricky solution. Tricky solutions cause bugs for future developers.
Here's my suggestion:
<a ng-repeat="set in data | filter: (shouldFilter ? filterExpression : '')">
or
<a ng-repeat="set in data | filter: (shouldFilter ? {value: search} : '')">
Simply, if shouldFilter, then give it your filter expression, otherwise give it nothing.
Using a simple ternary expression will be easier for readability.

Maybe use an ng-if?
<a ng-if="!myConditional" ng-repeat="set in data" data-id="{{set.id}}" ng-mousedown="setBox(set)" ng-mouseover="setSelected(set, $event)" ng-bind-html="set.value | trustHTML"></a>
<a ng-if="myConditional" ng-repeat="set in data | filter: { value: search }" data-id="{{set.id}}" ng-mousedown="setBox(set)" ng-mouseover="setSelected(set, $event)" ng-bind-html="set.value | trustHTML"></a>
That seems to be the solution.

Here is what I did. First, I had a select control, populated from my controller, with one static item (Select...) with a zero length string value:
<select data-ng-model="districtFilter" class="form-control" data-ng-options="d.DistrictId as d.DistrictName for d in districts | orderBy: 'DistrictName'"><option value="">Select...</option></select>
Then I applied the filter conditionally on the table. It appears when the filter is null, setting it to undefined clears it:
<tr data-ng-repeat="courtEntity in courts | filter:{ 'DistrictId' : districtFilter === null ? undefined : districtFilter}">

I had a complicated case
Have a list of records say it products
Filter by two other property prop1, prop2 selected by user from drop down
Another dropdown (prop3) have two value 1 and 2, if user select 2 filter should not be applied
Hers is my outcome:
ng-repeat="prod in filterdProd =
(products | filter : model.prop3 == 2 ? '' :
{ 'prop1': model.prop1 || 'none',
'prop2': model.prop2 }) |
orderBy: 'productrName'"
if model.prop3 == 2 ? '' filter will not apply otherwise ...
We can also use the count of records by using filterdProd

I found the following solution more easy and effective
angular.module('myApp', []).controller('namesCtrl', function($scope) {
$scope.package=[{"PkgServiceId":null,"PkgServicesName":" HB, TLC, DLC & ESR, EDTA WHOLE BLOOD, HBSAG CONFIRMATION,SERUM, Blood Cholesterol","PromoDiscount":null,"PromoID":null,"ReportPeriod":5,"ServiceDesc":null,"ServiceId":108,"ServiceName":"Family Health Package"},{"PkgServiceId":108,"PkgServicesName":null,"PromoDiscount":null,"PromoID":null,"ReportPeriod":5,"ServiceDesc":null,"ServiceId":70,"ServiceName":"HB, TLC, DLC & ESR, EDTA WHOLE BLOOD"},{"PkgServiceId":108,"PkgServicesName":null,"PromoDiscount":null,"PromoID":null,"ReportPeriod":5,"ServiceDesc":null,"ServiceId":71,"ServiceName":"HBSAG CONFIRMATION,SERUM"},{"PkgServiceId":108,"PkgServicesName":null,"PromoDiscount":null,"PromoID":null,"ReportPeriod":3,"ServiceDesc":"Its used to evaluate your overall health and detect a wide range of disorders","ServiceId":2,"ServiceName":"Blood Cholesterol"},{"PkgServiceId":null,"PkgServicesName":" MRI Test, HB, TLC & DLC, EDTA WHOLE BLOOD","PromoDiscount":null,"PromoID":null,"ReportPeriod":5,"ServiceDesc":null,"ServiceId":107,"ServiceName":"Child Health Package"},{"PkgServiceId":107,"PkgServicesName":null,"PromoDiscount":null,"PromoID":null,"ReportPeriod":5,"ServiceDesc":"Its used to evaluate your overall health and detect a wide range of disorders","ServiceId":5,"ServiceName":"MRI Test"},{"PkgServiceId":107,"PkgServicesName":null,"PromoDiscount":null,"PromoID":null,"ReportPeriod":5,"ServiceDesc":null,"ServiceId":69,"ServiceName":"HB, TLC & DLC, EDTA WHOLE BLOOD"}] ;
});
<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<body>
<div ng-app="myApp" ng-controller="namesCtrl">
<ul>
<li ng-repeat="x in package">
{{ x.ServiceName }}
</li>
</ul>
<p>Above is the total data set without filtering.</p>
<ul>
<li ng-repeat="x in package| filter :{PkgServiceId: null}">
{{ x.ServiceName }}
{{ x.PkgServiceId }}
</li>
</ul>
</div>
<p>This example displays only the packages containing the packageId "null".</p>
</body>
</html>

In HTML Template Binding
{{ dataX | date: expression ? 'dd/MM' : 'dd/MM/yyyy' }}

Make functions and combine with filterExpression.
Example I have a students list as below:
$scope.students = [
{ name: 'Hai', age: 25, gender: 'boy' },
{ name: 'Hai', age: 30, gender: 'girl' },
{ name: 'Ho', age: 25, gender: 'boy' },
{ name: 'Hoan', age: 40, gender: 'girl' },
{ name: 'Hieu', age: 25, gender: 'boy' }
];
I want to filter students via gender to be boy and filter by name of them.
The first I create a function named "filterbyboy" as following:
$scope.filterbyboy = function (genderstr) {
if ((typeof $scope.search === 'undefined')||($scope.search === ''))
return (genderstr = "")
else
return (genderstr = "boy");
};
Explaination: if not filter name then display all students else filter by input name and gender as 'boy'
Here is a demo How to use and operator in AngularJs example

Related

angular ng-repeat filter by number

I'm having some trouble filtering an list by type (number) in angular (1.5.8)
my ng repeat with filter is as following:
ng-repeat="activity in guild.history_updates | filter : {
action_type: filters.action_type,
user_id: filters.user,
}"
and my filter
<md-input-container class="md-block">
<label>Type</label>
<md-select ng-model="filters.action_type">
<md-option>
All
</md-option>
<md-option ng-repeat="action_type in guildActivityCtrl.action_types"
ng-value="action_type.type">
{{action_type.name}} ({{ action_type.type }})
</md-option>
</md-select>
</md-input-container>
When I'm filtering on action_type 1 I'm also getting action types 10
But when I chage the filter to 10, I'm getting the right results
Every other action type is filtered good as well (e.g. action_type 3)
ng-repeat="activity in guild.history_updates | filter:{action_type:filters.action_type,user_id: filters.user}:true"
Adding ":true" after your filter should change this to a strict equality check, now comparing the numeric values.
Hope this helps.
https://docs.angularjs.org/api/ng/filter/filter
The Arguments section defines the comparator.
Like Neal Hamilton said, we need to add :true to add a strict equality check.
Adding :true won't work when the output of the md-select isn't a number so we can add a filter to parse the output to a number like:
.filter('parseInt', function() {
return function(number) {
if(!number) {
return false;
}
return parseInt(number , 10);
};
})
We can add this filter to the ng-repeats' filter like:
ng-repeat="activity in guild.history_updates | filter : {
action_type: (filters.action_type | parseInt ),
user_id: filters.user,
} : true"
Edit: When you don't want to strict check every value in the filters you can use multiple filters like:
ng-repeat="activity in guild.history_updates
| filter : {
action_type: (filters.action_type | parseInt ),
} : true
| filter: {
user_id: filters.user
}"

How to pass value (true or false) for sorting tabular data in Angular JS ng-repeat

I want to determine the ascending/descending property of the ng-repeat by passing a value coming from a dropdown box.
I defined a variable called "asdc" whose value is determined by a dropdown box. This variable should determine if the table will be sorted ascending or descending. The variable already created in the AngularJS controller so I don`t post it here. I am pasting the codes below. It does not work this way. I am wondering what I am missing.
This is the table that I want to sort.
<tr ng-repeat="technology in technologies | limitTo: rowlimit | orderBy: sortby : adsc" >
<td>{{technology.name | uppercase}}</td>
</tr>
This is the drowdown box. It defines the value of adsc as true or false and passes the value to "adsc".
<select ng-model="adsc">
<option value="true">Asc</option>
<option value="false">Dsc</option>
</select>
Use ng-options to bind to something other than a string:
In controller:
$scope.directions = [
{
value: false,
label: 'Asc'
},
{
value: true,
label: 'Desc'
}
];
In view:
<select name="direction" ng-model="adsc"
ng-options="direction.value as direction.label for direction in directions">
</select>
Here's a plunkr demonstrating its use.
Also, note that passing true will sort in descending order, not ascending.

Filter ng-repeat table with multiple filter values for one column

I've generated a dynamic table from array of objects and paginated the rows with angular-ui-bootstrap. I also need to implement row filter functionality in table based on user selections. For example, I would select one or more Cities from check-boxes and only the rows with matching Cities should be shown, or user will select certain names, or a combination of both. Below is sample data:
ctrl.data = [{
id: 1,
name: 'Ram Kumar',
city: 'Delhi',
date: new Date("October 13, 2014 11:13:00")
}, {
id: 2,
name: 'Raman Kumar',
city: 'Mumbai',
date: new Date("November 13, 2014 11:13:00")
}, {
id: 3,
name: 'Raghav Kumar',
city: 'Chennai',
date: new Date("December 13, 2014 11:13:00")
}];
I've also created a metadata which will contain information about all properties of data such as property name, it's unique values, the kind of filter which this property would require i.e. check-box or a slider. Below is the metadata:
ctrl.metadata = [{
name: "ID",
values: [1, 2, 3],
type: "checkbox",
showFilter: false
}, {
name: "Name",
type: "checkbox",
values: ["Ram Kumar", "Raman Kumar", "Raghav Kumar"],
showFilter: false
}, {
name: "City",
type: "checkbox",
values: ["Delhi", "Mumbai", "Chennai"],
showFilter: true
}, {
name: "Birth Date",
type: "date",
showFilter: false
}, {
name: "Age",
type: "slider",
values: [18,26],
showFilter: false
}];
I've currently made a one-value static filter query variable for some attributes such as ID, Name and City.
<div ng-repeat="attr in metadata">
<div ng-if="attr.name === 'ID'">
Enter Filter:
<input type="textbox" ng-model="search.id" />
</div>
<div ng-if="attr.name === 'City'">
Enter Filter:
<input type="textbox" ng-model="search.city" />
</div>
<div>
And I am applying this filter to table rows in this way:
<tr ng-repeat="row in data | filter: search | pages: currentPage : itemsPerPage">
<td ng-repeat="item in row">{{ item }}</td>
</tr>
But firstly, I want the filter query variable (search) to be dynamically generated, as the data and metadata might change. Something like below if possible:
<div ng-repeat="attr in metadata">
<div ng-if="attr.name === 'ID'">
Enter Filter:
<input type="textbox" ng-model="attr.searchQuery" />
</div>
</div>
I attached the search query to metadata object property called searchQuery. But when I am trying to use it in filter, I see no result:
<tr ng-repeat="row in data | filter: metadata[$index].searchQuery | pages: vm.currentPage : vm.itemsPerPage">
<td ng-repeat="item in row">{{ item }}</td>
</tr>
Secondly, the search query can take only one string, but as I want the filter to be based on more than one value, how can I achieve it? I've researched about custom filter but the problem is, how do I 'record' the selected values, and then send it to the filter (for example, get all the selected cities from check-boxes) ? I have to make every variable dynamic or part of metadata so that it does not break when either number of properties, or name of properties or values are changed. There might be no Cities but States when data is changed, so can't make anything static.
Please suggest some ideas for implementation.
Here is a working plunker.
First you should change your <input> to:
<input type="checkbox" ng-model="vm.selected[$index]" ng-true-value="'{{value}}'" ng-false-value="" /> {{value}}
Then your ng-repeat:
<tr ng-repeat="row in vm.data | filter: vm.search | customFilter: vm.selected | pages: vm.currentPage : vm.itemsPerPage">
And finally you can implement a custom filter to achieve what you want, like this:
.filter('customFilter', function() {
return function(items, search) {
if (!search || (Object.keys(JSON.parse(JSON.stringify(search))).length === 0 && search.constructor === Object)) {
return items;
}
var selected = Object.keys(search).map(function(key) {
return search[key]
});
items = items.filter(function(value) {
return selected.indexOf(value.city) != -1;
});
return items;
}
});
Check the complete code
I hope it helps.
Tip: I don't know if you're using these things of ui-bootstrap in your real project, but they're deprecated, as you can see in console:
PaginationController is now deprecated. Use UibPaginationController
instead. pagination is now deprecated. Use uib-pagination instead.

how do i filter items by property in ng-options?

I have some data and a select control as you can see from my code sample below. It works for me but I want to be able to filter the data by type. In my code sample, I show one way I have tried to filter the data based on the type being a value of one. I am not sure if the way I am creating the select is causing this simple inline filter to not work or if I am doing something wrong?
$scope.data = [{
name : "5 play",
type : 1
}, {
name : "one on one",
type : 2}, {
name : "two on one",
type : 2}];
<select class="form-control" ng-model="selectedRule"
ng-options="item as (item.Name) for item in data track by item.Name | filter:{type:1}"></select>
As stated in angular docs, track by must always be the last expression:
Note: track by must always be the last expression
My guess is that all that follows is not considered, so that´s why your filter wasn´t applying. Moving the track by to the right place should fix it
Yes, as mentioned. Move the track by at the end and make the letter N lowercase like in the following demo or in this fiddle.
angular.module('demoApp', [])
.controller('mainController', MainController);
function MainController($scope) {
$scope.data = [{
name: "5 play",
type: 1
}, {
name: "one on one",
type: 2
}, {
name: "two on one",
type: 2
}];
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="demoApp" ng-controller="mainController">
<select class="form-control" ng-model="selectedRule" ng-options="item as item.name for item in data | filter:{type:1} track by item.name"></select>
</div>

angularJS - How to filter by one item in object only

In this JSFiddle (https://jsfiddle.net/eug0taf9/9/), I select a rating and expect an image to be displayed.
What is happening?
When I select rating 'High', 2 images show up instead of 1 because the filter is catching category 'High' and also description 'High' of an image in category 'Low'.
What do I expect to happen?
On selecting rating "High", I only want the category to be filtered. I don't need it to be filtered by description too.
Any suggestions on how to solve this?
HTML code:
<div ng-app="myApp" ng-controller="myCtrl">
<span>Select a rating</span>
<select ng-model="catselect"
ng-options="category for category in imageCategories"
ng-change="valueSelected(catselect)">
<option value>All</option>
</select>
<!--<p>Select <input ng-model="categories"/></p>-->
<ul class="photosmenu">
<li ng-repeat="image in images | filter : catselect" ng-click="setCurrentImage(image)">
<img ng-src="{{image.image}}" alt="{{image.stars}}" width="300px"/>
</li>
</ul><!-- End of List -->
</div>
Angular Code:
var mod = angular.module("myApp", []);
mod.controller("myCtrl", function($scope){
$scope.images = [
{category: 'High', image: 'img/1.png', description: 'Random Photo', stars: '4/5'},
{category: 'Medium', image: 'img/6.png', description: 'ApplePhoto', stars: '3/5'},
{category: 'Low', image: 'img/13.png', description: 'High Top Photo', stars: '2/5'},
{category: 'None', image: 'img/16.png', description: 'Kilt Photo', stars: '0/5'}];
$scope.currentImage = $scope.images[0];
$scope.imageCategories = ["High", "Medium", "Low", "None"];
$scope.valueSelected = function (value) {
if (value === null) {
$scope.catselect = undefined;
}
};
});
That is because you have a global match filter which will match on all properties, if you want to filter on specific property set your filter object accordingly. i.e
<li ng-repeat="image in images | filter :{category: catselect}"
Demo
or you could also set your ng-model to an object,
<select ng-model="catselect.category"
and do:
<li ng-repeat="image in images | filter :catselect"
Demo
Check out documentation:
string: The string is used for matching against the contents of the array. All strings or objects with string properties in array that match this string will be returned. This also applies to nested object properties. The predicate can be negated by prefixing the string with !.
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 or its nested object properties. That's equivalent to the simple substring match with a string as described above. The predicate can be negated by prefixing the string with !. For example {name: "!M"} predicate will return an array of items which have property name not containing "M".

Resources