Filtering by multiple checkboxes in AngularJS - 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

Related

Angular change detection ngFor

I have an array called peoples on my tab view. I need to change the array items based on a change function. While the change function is working successfully and printing the array differently after each change on the console, the view itself is not changing from the initial value assigned from ngInit();
ngOnInit(){
this.someservice.loadallpeople().subscribe(data=>{
this.peoples=data.Data;
});
}
loadpeople(category:any){
this.someservice.getpeoplebycat(category).subscribe(data=>{
this.peoples=data.Data;
});
}
<select [(ngModel)]="category.name"
(ngModelChange)="loadpeople(category.name)">
<option *ngFor="let cat of category">{{category.name}} </option>
</select>
<div *ngFor="let people of peoples">
<span>{{people.name}}</span>
</div>
I have used some methods but none of them seems to work. Any small help will be appreciated.
Some things are not clear on what you show.
First you use [(ngModel)]="category.name" and (ngModelChange)="loadpeople(category.name)" on category.name, but next you iterate over category and you get cat.name, so there cannot be a category.name element if it's an array.
Second I will just use (change) instead of [ngModel] + (ngModelChange) according to what you show, because as said before, category.name cannot return anything.
So with all this updates, I will do something like this
ngOnInit(){
this.someservice.loadallpeople().subscribe(data=>{
this.peoples=data.Data;
});
}
loadpeople(event){
this.someservice.getpeoplebycat(event.target.value).subscribe(data=>{
this.peoples=data.Data;
});
}
<select (change)="loadpeople($event)">
<option *ngFor="let cat of category">{{cat.name}} </option>
</select>
<div *ngFor="let people of peoples">
<span>{{people.name}}</span>
</div>

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>

Filter with function parameter ignores return value

I have this bit of markup:
<option
ng-repeat="item in data track by item.id | filter:dropdownFilter($index,item)">
</option>
And this part inside the controller:
$scope.dropdownFilter=function(index,item)
{
console.log(index,item);//Works, outputs each item and its index
return false;//Doesn't work, all options are available
}
Notes:
I know I can write a custom filter, as in module(...).filter(...).
I know how to use ngOptions, but let's focus on ngRepeat for now.
What am I doing wrong?
Check out the documentation on ngRepeat.
http://docs.angularjs.org/api/ng.directive:ngRepeat
Relevant snippit:
For example: item in items | filter:searchText track by item.id is a
pattern that might be used to apply a filter to items in conjunction
with a tracking expression.
I think you should try changing your ng-repeat statement to this:
<option
ng-repeat="item in data | filter:dropdownFilter($index,item) track by item.id">
</option>

Angularjs filtering values based on selected option

Hi I am trying to display certain values based on option selected in a drop down.
My code is
data is
[{"city":"New York","location":"123"},
{"city":"Chicago","location":"953"}
{"city":"New York","location":"788"}
{"city":"Chicago","location":"853"}]
Code is
<form name="test" class="form-horizontal">
<div class="from-group container">
<label for="abc" class="col-sm-2">City</label>
<div class="col-sm-10">
<select class="form-control" ng-model="cc">
<option ng-repeat="city in cities" value="city.value">{{city.name}}</option>
</select>
</div>
<ul>
<li ng-repeat="att in cities">{{att.locations | filter:cc}} ></li>
</ul>
</div>
</form>
With above code I have two problems.
1. As I select different options in the drop down it wont filter for that city name.
2. When the file is first loaded it will display all the locations
Please let me know how to fix this code so when I select an option it lists all the locations filtering based on the city name.
Thanks
angular has a really cool feature where you can filter directly in your ng-repeat.
I've set up a fiddle where you can see this in action. Basically you set an object (cc) based on the select and then use:
<div ng-repeat="city in data | filter:cc">
Hope this helped.

AngularJS : Custom filters and ng-repeat

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

Resources