AngularJS : Custom filters and ng-repeat - angularjs

I'm an AngularJS newbie and I'm building up a small proof-of-concept car hire listings app that pulls in some JSON and renders out various bits of that data via an ng-repeat, with a couple of filters:
<article data-ng-repeat="result in results | filter:search" class="result">
<header><h3>{{result.carType.name}}, {{result.carDetails.doors}} door, £{{result.price.value}} - {{ result.company.name }}</h3></header>
<ul class="result-features">
<li>{{result.carDetails.hireDuration}} day hire</li>
<li data-ng-show="result.carDetails.airCon">Air conditioning</li>
<li data-ng-show="result.carDetails.unlimitedMileage">Unlimited Mileage</li>
<li data-ng-show="result.carDetails.theftProtection">Theft Protection</li>
</ul>
</article>
<h2>Filters</h2>
<h4>Doors:</h4>
<select data-ng-model="search.carDetails">
<option value="">All</option>
<option value="2">2</option>
<option value="4">4</option>
<option value="9">9</option>
</select>
<h4>Provider:</h4>
Atlas Choice <input type="checkbox" data-ng-model="search.company" ng-true-value="Atlas Choice" ng-false-value="" value="Atlas Choice" /><br>
Holiday Autos <input type="checkbox" data-ng-model="search.company" ng-true-value="Holiday Autos" ng-false-value="" value="Holiday Autos" /><br>
Avis <input type="checkbox" data-ng-model="search.company" ng-true-value="Avis" ng-false-value="" value="Avis" /><br>
Now I want to create a custom filter in my controller, that can iterate over the items in my ng-repeat and return only the items that meet certain criteria - for example, I might create an array of values based on which 'provider' checkboxes are checked, then evaluate each ng-repeat item against that. I just can't work out how I'd do that though, in terms of the syntax - can anyone help?
Here's my Plunker:
http://plnkr.co/edit/lNJNYagMC2rszbSOF95k?p=preview

If you want to run some custom filter logic you can create a function which takes the array element as an argument and returns true or false based on whether it should be in the search results. Then pass it to the filter instruction just like you do with the search object, for example:
JS:
$scope.filterFn = function(car)
{
// Do some tests
if(car.carDetails.doors > 2)
{
return true; // this will be listed in the results
}
return false; // otherwise it won't be within the results
};
HTML:
...
<article data-ng-repeat="result in results | filter:search | filter:filterFn" class="result">
...
As you can see you can chain many filters together, so adding your custom filter function doesn't force you to remove the previous filter using the search object (they will work together seamlessly).

If you still want a custom filter you can pass in the search model to the filter:
<article data-ng-repeat="result in results | cartypefilter:search" class="result">
Where definition for the cartypefilter can look like this:
app.filter('cartypefilter', function() {
return function(items, search) {
if (!search) {
return items;
}
var carType = search.carType;
if (!carType || '' === carType) {
return items;
}
return items.filter(function(element, index, array) {
return element.carType.name === search.carType;
});
};
});
http://plnkr.co/edit/kBcUIayO8tQsTTjTA2vO?p=preview

You can call more of 1 function filters in the same ng-repeat filter
<article data-ng-repeat="result in results | filter:search() | filter:filterFn()" class="result">

One of the easiest ways to fix this is to use the $ which is the search all.
Here is a plunker that shows it working. I have changed the checkboxes to radio ( because I thought they should be complementary )..
http://plnkr.co/edit/dHzvm6hR5P8G4wPuTxoi?p=preview
If you want a very specific way of doing this ( instead of doing a generic search ) you need work with functions in the search.
The documentation is here
http://docs.angularjs.org/api/ng.filter:filter

Related

AngularJS - Populating HTML Drop-Down with JSON from REST API (without $scope)

A lot of solutions on Stack Overflow in relation to populating drop down menus include $scope.
My second drop-down depends on the value of my first drop-down therefore I use ng-changeon the first HTML select to parameter pass the model ID into the 2nd drop-down's function.
1st Drop-Down HTML and Angular JS:
<select data-ng-controller="addAssetController as addAssetCtrl" id="functionalOrg" data-ng-model="addAssetFormCtrl.functionalOrg.id" ng-change="addAssetCtrl.getLocations(addAssetFormCtrl.functionalOrg.id)">
<option data-ng-repeat="functionalOrg in addAssetCtrl.functionalOrgs | orderBy:'id' track by $index" value="{{functionalOrg.id}}">
{{functionalOrg.id}} - {{functionalOrg.account}}
</option>
</select>
Hence ng-change:
ng-change="addAssetCtrl.getLocations(addAssetFormCtrl.functionalOrg.id)"
-
var vm = this;
functionalOrganisationRepository.getFunctionalOrganisation().then(function (results) {
vm.functionalOrgs = results;
}, function (error) {
vm.error = true;
vm.errorMessage = error;
});
The 2nd Drop-Down HTML and Angular:
<select data-ng-controller="addAssetController as addAssetCtrl" id="location" data-ng-model="addAssetFormCtrl.location.id">
<option data-ng-repeat="location in addAssetCtrl.locations | orderBy:'id' track by $index" value="{{location.id}}">
{{location.id}} - {{location.address6}}
</option>
</select>
-
vm.getLocations = function(id) {
console.log("Functional org ID:" + id);
locationRepository.getLocation(id).then(function (results) {
vm.locations = results;
}, function (error) {
vm.error = true;
vm.errorMessage = error;
});
}
Assuming my service layer is fine and brings back a JSON object with everything I require, what could the problem be? The vm.getLocations function is definitely getting called because my console log is being printed. The service layer is also fine because a JSON object to being logged to my command prompt.
My question is how do I populate my second drop-down from whatever JSON is returned by getLocations? Please hence I do not want to make use of $scope in Angular.
The "ng-controller" attribute is repeated on each select. Put the attribute only one time on a parent element!
<div data-ng-controller="addAssetController as addAssetCtrl">
<!-- Drop Down 1 and 2 here -->
</div>
If you dont share scope you can't do what you want to do. Meaning if you dont have a parent vm or pass something to your directive = you can't tell what is selected.
Pull up your controller a level and share it among selected or you are going to have to either watch a shared variable in a service, or rely on $on & $broadcast to communicate.
Pretty sure that this is because they have different scopes and different controllers.
Assuming that the actual HTML looks like the following:
<select data-ng-controller="addAssetController as addAssetCtrl" id="location" data-ng-model="addAssetFormCtrl.location.id">
<option data-ng-repeat="location in addAssetCtrl.locations | orderBy:'id' track by $index" value="{{location.id}}">
{{location.id}} - {{location.address6}}
</option>
</select>
<select data-ng-controller="addAssetController as addAssetCtrl" id="functionalOrg" data-ng-model="addAssetFormCtrl.functionalOrg.id" ng-change="addAssetCtrl.getLocations(addAssetFormCtrl.functionalOrg.id)">
<option data-ng-repeat="functionalOrg in addAssetCtrl.functionalOrgs | orderBy:'id' track by $index" value="{{functionalOrg.id}}">
{{functionalOrg.id}} - {{functionalOrg.account}}
</option>
</select>
Then, to the best of my knowledge, each select will get a different instance of the controller, each with different scopes. So you've effectively got a controller addAssetCtrl1 and addAssetCtrl2, so setting data on 1 does not set it on 2.
The solution would be to set the data on a parent controller, or, probably more simply, to do the following:
<div data-ng-controller="addAssetController as addAssetCtrl">
<select id="location" data-ng-model="addAssetFormCtrl.location.id">
<option data-ng-repeat="location in addAssetCtrl.locations | orderBy:'id' track by $index" value="{{location.id}}">
{{location.id}} - {{location.address6}}
</option>
</select>
<select id="functionalOrg" data-ng-model="addAssetFormCtrl.functionalOrg.id" ng-change="addAssetCtrl.getLocations(addAssetFormCtrl.functionalOrg.id)">
<option data-ng-repeat="functionalOrg in addAssetCtrl.functionalOrgs | orderBy:'id' track by $index" value="{{functionalOrg.id}}">
{{functionalOrg.id}} - {{functionalOrg.account}}
</option>
</select>
</div>

How to create custom filter in angular js

I have table with rows filled up using ng-repeat. I also have an input box to search in the table. I don't want to use in build filter. I am confused with how to write a custom filter which will search in the search box and return the table rows. Also, I need search such that it match's first 4 letters if not the display whatever matches in the string. For example Search box say's "a124" then it should return all the table rows having the a124 as the first four characters if not then the default case would be search anywhere in the string.
<tr ng-repeat ="data in fetchedData></tr>
<th>{{data.name}}</th>
<th>{{data.id}}</th>
<th>{{data.receivedTime}}</th>
Input box
<input class="form-control" type="text" placeholder="Search..." ng-change="filterColumn()">
Controller code (I am confused what should go in )
$scope.filterColumn = function {
}
You don't need a custom filter for this. The normal angular filter should do fine.
Change your input to bind to search:
<input class="form-control" type="text" placeholder="Search..." ng-model="searchText">
And use that search filter to filter your ng-repeat
<tr ng-repeat ="data in fetchedData | filter:searchText"></tr>
Ok so this the solution which worked for me!! I got help from many AngularJS blog posts. Thanks to all of them. `
$scope.filter = function () {
var query = $scope.query.toLowerCase();
$scope.details = $filter('filter')($scope.data, function(entry) {
var concatString = '';
angular.forEach(entry,function(key ,value){
concatString += key + " ";
});
var pattern = new RegExp(query, 'i');
return (pattern.test(concatString));
});
};`
HTML code:<input ng-model="query" id="filter" class="form-control" type="text" placeholder="Search" ng-change="filter()">
<tr ng-repeat ="data in details"></tr>
#Priya i can see in your code, you used ng-change to call the filter method. This looks very normal way of calling a method from ancient javascript methodology.
Im suggesting you to use the way how angularjs will be using it.
For Example,
<input type="text" ng-model="letter">
<ul>
<li ng-repeat="friend in person.friends | startsWithLetter:letter">
{{ friend }}
</li>
</ul>
Here you see the ng-model="letter", this name is bind with filter called startsWithLetter. So whenever there is a change in input box then the filter will be triggered.
Here i wrote an article to create custom filters using angularjs in various types.
This will be useful for others who read this Q&A section in future.

Filter certain column with ng-repeat

I try to filter an array of objects using ng-repeat on a specific key or you might even call it column.
The key/column on which the filter should be applied on, comes from an selectbox.
data-ng-model="selectedValue" // -> item.name
The term which should be searched for comes from an input field and is decalred as following:
<input type="text" data-ng-model="q">
<div data-ng-repeat="item in items |filter:{selectBoxValue:q}">{{item.name}}</div>
The above does not work, sadly.
But when I use item.name instat of selectBoxValue in my filter, it works great.
In your controller:
$scope.search = {}
In your markup
<input ng-model="colName"> //colName
<input ng-model="search[colName]"> //colvalue
<div data-ng-repeat="item in items |filter:search">{{item.name}}</div>
Found a solution.
Now you can filter on specific column, in case anyProperty.
<input type="button" ng-click="changeFilter('anyProperty')">
<input type="text" ng-model="q[filter]">
<div ng-repeat="item in items | filter:q">
$scope.changeFilter = function(q){
$scope.filter = q;
}

How to differentiate the call from filter angularjs

<li>
<p>By Member Status</p>
<p><label><input type="checkbox" ng-model="filter['top']"> top</label></p>
<p><label><input type="checkbox" ng-model="filter['low']"> low</label></p>
</li>
<li>
<p>By ADMIN Status</p>
<select class="form-control" ng-init="selectedSearch = 'default'">
<option value="default">Select Status</option>
<option value="A" ng-model="filter['a']">A</option>
<option value="B" ng-model="filter['b']">B</option>
</select>
</li>
<div class="complete-profile" ng-class="{active:show}" ng-repeat="mylist in mylist | filter:filterByCategory"></div>
$scope.filterByCategory = function (stat) {
return $scope.filter[stat.state] || noFilter($scope.filter); // checkbox
};
$scope.filterByCategory = function (stat) {
return $scope.filter[stat.drop] || noFilter($scope.filter); // dropdown
};
i need to apply both filter on mylist but somehow one one filter works either for checkbox or dropdown both never works.
You should update your filtering logic. Code here
...
$scope.filter = {
top: false,
low: false,
byAdminStatus: 'A'
}
...
You declared $scope.filterByCategory twice. Because JavaScript variable names must be unique, the first declaration will be overwritten by the second.
I suggest giving each filter its own meaningful name. Then, you can chain the filters together to produce the composite filtering:
<div ng-repeat="item in mylist | filter:filterByState | filter:filterByDrop">
Here is an example of using multiple filters on a ng-repeat: http://jsfiddle.net/2671uggu/

Filtering by multiple checkboxes in AngularJS

first time I've posted a question on here, so apologies in advance if I breach any Stack Overflow etiquette! :-)
I'm having a go at some AngularJS for the first time in order to create a proof-of-concept for my boss. It's a basic car hire listings app, with a list of results in the main column, and a filter panel down the side. I've managed to pull in the results from a JSON object, and apply a basic filter, as below;
<article data-ng-repeat="result in results | filter:search" class="result">
<h3>{{result.carType.name}}, £{{result.price.value}}</h3>
<img class="car-type" alt="{{result.carType.name}}" src="{{result.carType.image}}" />
<ul class="result-features">
<li>{{result.carDetails.hireDuration}} day hire</li>
<li data-ng-show="result.carDetails.airCon">Air conditioning</li>
<li data-ng-show="result.carDetails.unlimitedMileage">Unlimited Mileage</li>
<li data-ng-show="result.carDetails.theftProtection">Theft Protection</li>
</ul>
</article>
Filters
<fieldset>
Doors:
<select data-ng-model="search.carDetails">
<option value="">All</option>
<option value="2">2</option>
<option value="4">4</option>
</select>
</fieldset>
...one thing I haven't been able to work out yet though, is how to add a group of checkboxes to apply a filter, for say, 'car type' which would have options like 'mini', 'compact', 'family' and so on - and the user would be able to filter by one or more option at a time. I know I need to use 'ng-model', and perhaps 'ng-change', I just don't know how to apply it to a group of checkboxes...?
Update: I've created a plunker so you can see where I'm up to:
http://plnkr.co/edit/lNJNYagMC2rszbSOF95k?p=preview
I would bind all the checkboxes to one object say:
app.js
$scope.cartypes = {mini: false, compact:false};
index.html
<input type="checkbox" data-ng-model="cartypes.mini"> Mini
<input type="checkbox" data-ng-model="cartypes.compact"> Compact
And then create a custom filter function which returns whether the object contains all (I assume thats what you want) of the checked options.
app.js
app.filter('myfilter', function() {
return function(items, options ) {
// loop over all the options and if true ensure the car has them
// I cant do this for you beacause I don't know how you would store this info in the car object but it should not be difficult
return carMatches;
};
});
Then you can add it to your template like this:
index.html
<article data-ng-repeat="result in results | filter:search | myfilter:cartypes" class="result">
I have implemented this solution like this:
#myapp.filter "storeFilter", ->
(stores, type) ->
_.filter stores, (store) -> type[store.name]
and in the view i have passed it like that:
store in stores | storeFilter:store_type

Resources