Abstract inline Angularjs filter - angularjs

New to angular!
I've set up a very basic filter of which I'm quite happy with. Yet I want to transpose the inline filter to a .filter() function - I hope to use it like filter: dataFilter, or something similar.
So, currently I have some select fields:
<select ng-model="search.foo"><!-- options --></select>
<select ng-model="search.bar"><!-- options --></select>
And an ng-repeat of the sorted data:
<div ng-repeat="data in response.data | filter:{foo: search.foo, bar: search.bar}">
<!-- display sorted data -->
</div>
Above in the ng-repeat is what I would call the inline filter of which I want to abstract.
How do I go about doing this?
All guidance appreciated.
My end game aim is to show a message when there an no results in the filter. I believe the filter needs to be "abstracted" in order for this to work? Something along the lines of:
<p ng-show="(response.data | filter:blahFilter).length == 0">No results found.</p>

I would really advice you to move this to the controller. Then inside there just use your filter as a function call
$scope.myData = $filter('blaFilter')(response.data)
then
<p ng-show="myData.length == 0">No results found.</p>

Related

How to properly select an object in an angular dropdown menu in a ionic app

I have a Ionic 1 app and I need some help to simplify my code and understand the best way to get around this thing:
I have a json like this:
[{"Cod":"A","Denumire":"production","Culoare":"#808000","ID":"1","Activ":"1"},
{"Cod":"B","Denumire":"transportation","Culoare":"#C4FFC4","ID":"2","Activ":"1"}]
and it's assigned in my controllers.js to $scope.categories
What I want is to load it in a drop-down menu and set the selected value to a variable snag.CategorieID which I have in the html page.
I want to assign it from the html and not the controller!
The problem is that snag.CategorieID is the ID from $scope.categories and it seems to me first I have to find out the index of the object $scope.categories that has the ID == snag.CategorieID.
I have managed to get it working like this:
<label class="item-borderless item-input item-select">
<div class="input-label">
Category
</div>
<div ng-repeat="c in categories">
<div ng-if="c.ID == snag.CategorieID">
<select ng-init="snaginf.category = categories[categories.indexOf(c)]" ng-model="snaginf.category" ng-options="cat.Denumire for cat in categories"></select>
</div>
</div>
</label>
The thing is even if it works I have the feeling things should be simpler.
Could anyone help me simplify it?
Thanks in advance,
Rares
I am not a fun of ng-init since the code should be in the controller, but if you must do it that way, you can use a filter to get the category using a categoryID like this:
<select ng-init="snaginf.category = (categories | filter: {ID: snag.CategorieID})[0]" ng-model="snaginf.category" ng-options="cat as cat.Denumire for cat in categories track by cat.ID"></select>
In this piece of code snaginf.category saves all the category object and assumes the ID of the categories is unique.
The filter takes the categories array and returns a new array with those categories which their ID equals snag.CategorieID. Since it's supposed to return only one category inside the array, we just get that element using [0].
Why don't you keep just the selector in place.
<select ng-init="snaginf.category = categories[0]" ng-model="snaginf.category" ng-options="cat.Denumire for cat in categories"></select>
Live Sample:
http://play.ionic.io/app/c0a0efd8603b

FirebaseListObservable filter

I am looking for a way to filter a firebaselistobservable. I have code like the following
<div>
<a *ngFor="let lis of list | async" (click)="onSelect(lis)" >
{{lis.name}}
</a>
</div>
this.list = this.fb.getList('Instructors');
What i want to be able to do is once the list of instructors is loaded be able to type into a text box and as i type the names of the instructors will filter. I am having a lot of trouble figuring out a way to filter a firebaselistobservable, which in this case is my list. If anyone has a method to achieve this it would be greatly appreciated.
Thanks!
If you want to do the filtering on the client, just add a filter pipe to your ngFor directive. In angular2 it is not offered out of the box, but there are plenty available.

Pre-count filtered results in an ng-repeat

I have a long list of products that are displayed using ng-repeat and I'm creating a filter on the left to pare down the list in the main content well.
I have a requirement to pre-count the results of each possible filter such that the text of the link of that filter button will show the results you will receive if you turn that filter on, just like happens in an e-commerce site.
I'm wondering how to do this with AngularJS. I'm hoping there's an easier way to do this other than creating a custom filter on the scope for each possible filter.
Note that I don't have to worry about chaining filter values- just the results of selecting that one filter.
Thanks, Scott
Not knowing exactly what the data model looks like I can't say if you need a custom filter. But for a simple data model you should be able to use a simple filter like so:
<ul>
<li ng-repeat="item in items">
<a ng-click="filter(item.type)">{{item.name}} {{(allData | filter:item.type).length}}</a>
</li>
</ul>
Here is a working example http://codepen.io/mkl/pen/GqpqYN

angularjs unnest ng-repeats into one query

<div ng-repeat="blub in filtered = (blubs | filter:tag)">
<span ng-repeat="tag in blub.tags" class="tag-box">{{tag}}</span>
</div>
How can I unnest it into one query. I imagined it like this:
<span ng-repeat="tag in blub.tags in filtered = (blubs | filter: tag)>{{tag}}</span>
Object blub has more than one tag, there are more than one blub objects.
EDIT:
Plunker:
http://plnkr.co/edit/mWjyryOsV5zZJisZzV3D
GOAL: Reduce 2 times ng-repeat into one ng-repeat which shows the same tags.
In that situation I would recommend you first to create one collection instead of doing double looping.
<span ng-repeat="tag in getBlupList(filtered)" class="tag-box">{{tag}}</span>
Where getBlupList(filtered) are JS method. Some kind of reducer that creates one list instead of what you have.
I dont know how your collection look like but personally for me for such situation the best solution is to use Underscore.js lib. ".flatten()" or ".pluck()"

Referencing scope children in filters (AngularJS)

I'm an AngularJS newbie, and am putting together a pretty basic proof-of-concept for my boss. It's listings for car hire, with a results list in the main area of the view populated via some external JSON, and a filters panel down the side. You can see the Plunker I've created here:
http://plnkr.co/lNJNYagMC2rszbSOF95k
I've been able to successfully reference child objects/values in my ngRepeat:
<article data-ng-repeat="result in results | filter:search" class="result">
<h3>{{result.carType.name}}, {{result.carDetails.doors}} door, £{{result.price.value}} - {{ result.company.name }}</h3>
<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>
...however, I've so far been unable to access the 2nd level child objects in my search filter. So, for example, where I'm filtering by 'car type' (see below), I'd like to be able to use 'search.carType.name' as my ngModel, to be as specific as possible - but this doesn't work, although just using 'search.carType' works fine. Can anyone advise on what I'm doing wrong?
<h4>Car type:</h4>
Compact <input type="checkbox" data-ng-model="search.carType" ng-true-value="Compact" ng-false-value="" /><br>
Intermediate <input type="checkbox" data-ng-model="search.carType" ng-true-value="Intermediate" ng-false-value="" /><br>
Premium <input type="checkbox" data-ng-model="search.carType" ng-true-value="Premium" ng-false-value="" /><br>
Your search object is being populated correctly, but filter isn't consuming it in the way you expect. Looking at the implementation of filter (https://github.com/angular/angular.js/blob/master/src/ng/filter/filter.js), it appears to only go one layer of child-properties deep when it is given an object as a filter definition.
Ajay's suggestion will work, but you would then need to chain additional filters to accomodate your other parameters. You could change both car type and company to specify x.name in the ng-model and then alter the filter to filter:search.carType.name|filter:search.company.name. If you were only going to have a small number of parameter types, I'd handle it this way.
On the other hand, the nice thing about your current approach is that it's transparent. There's no need to the filter call to be changed if the number of parameters changes elsewhere. If you might have a relatively large number of those, or if they were dynamic, I would take a more scalable approach. Write a filter FUNCTION that consumes the search object, and goes more than one level deep in comparing the children to the filtered data.
Here is a nice post by Anton Kropp on deep object filtering: FILTER ON DEEP OBJECT PROPERTIES IN ANGULARJS
The relevant code:
function initFilters(app){
app.filter('property', property);
}
function property(){
function parseString(input){
return input.split(".");
}
function getValue(element, propertyArray){
var value = element;
_.forEach(propertyArray, function(property){
value = value[property];
});
return value;
}
return function (array, propertyString, target){
var properties = parseString(propertyString);
return _.filter(array, function(item){
return getValue(item, properties) == target;
});
}
}
And the HTML
<ul>
only failed: <input type="checkbox"
ng-model="onlyFailed"
ng-init="onlyFailed=false"/>
<li ng-repeat="entry in data.entries | property:'test.status.pass':!onlyFailed">
<test-entry test="entry.test"></test-entry>
</li>
</ul>
And a JSFiddle here

Resources