AngularJS filter with multiple perms - angularjs

i have a users like this
[{id:1,name:'name',parent_id:0,type:1},
{id:2,name:'name2',parent_id:0,type:2},
{id:3,name:"name1 child",parent_id:1,type:1}]
and i am trying to display parents and child users based on parent_id and type id so for example id 3 is child of id 1 and i am trying to display them like this
http://jsfiddle.net/U3pVM/689/
thanks for help

I fixed up your fiddle:
http://jsfiddle.net/eQP8S/
<div ng-controller="MyCtrl">
<ol>
<li ng-repeat="user in users | filter:{parent_id:0}">
{{user.name}}
<ol>
<li ng-repeat="child in users | filter:{parent_id:user.id, type:user.type}">
{{child.name}}
</li>
</ol>
</li>
</ol>
</div>
Your main problem was that you were trying to use the "filter" filter with a function, which is okay, but that function isn't allowed to take any parameters. If you actually need to create a filter that takes parameters you have to write a custom filter: http://docs.angularjs.org/guide/dev_guide.templates.filters.creating_filters.
But in your case, you can use the standard "filter" filter and pass it objects that will do what you want.
Also, curse you Angular developers for creating an abstraction called filter and then creating a "filter" filter. I mean, how confusing is that?

That is not the correct way to use filters. If you check the documentation, you have three options:
use a string. It will be searched deeply in the object. Any object with some deep value containing the string will match
use a function. It will be called for elements of the array. You should provide any parameter in the function's scope, not as a parameter to the filter.
use an object. It will work like the string match, but for specific properties.
If you use the third case
<li ng-repeat="child in users | filter:{parent_id: user.id, type: user.type}">
and moreover print the child's name
{{ child.name }}
then you'll see the correct results.
http://jsfiddle.net/waAc7/

Related

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

Sorting ng-repeat with alternate ordering

I am trying to sort a client list using ng-repeat in Angular so that the column with ratings "Hot", "Warm", and "Cold" presented in that order. Currently when it sorts the Rating column it puts them in alphabetical order of "Cold", "Hot", and "Warm". How would I sort it non-alphabetically like that? Is it something with using a custom directive to create a custom attribute related to the rating value and then using "track by"?
Here is a CodePen with a working example of how it sorts alphabetically:
http://codepen.io/MattSchultz/pen/VeBpaQ
<li id="listCont" ng-repeat="client in clients | orderBy:col:reverse as results" ng-class-even="'even'">
<ul class="clients">
<li>{{client.Name}}</li>
<li>{{client.AnnualRevenue | currency}}</li>
<li ng-class="classy('{{client.Rating}}')">{{client.Rating}}</li>
</ul>
</li>
If you pass a string (i.e. an object property name) as the first argument to the orderBy filter, as you are doing in your example, it will attempt to sort alphabetically. To change this behavior, you need to pass a custom sort function instead.
For example:
function ratingSort(val) {
return ['Cold', 'Warm', 'Hot'].indexOf(val.Rating);
};
This function returns a number between -1 (i.e. val.Rating value is not in the array) and 2 (i.e. 'Hot', which is at index 2 in the array). The number returned is used to determine the sort order.
Here is a working example: CodePen

Filtering an Angular 1.2 ng-repeat with "track by" by a boolean property

I'm trying to filter some list items based on the value of a boolean property, but no matter what I do the entire list is always displayed. A few of the the things I've tried have been so broken that nothing displays, but that's neither here nor there. I can't get my filtering working as desired:
$scope.attendees = [
{"firstname":"Steve", "lastname":"Jobs", "arrived":true, "id":1}
,{"firstname":"Michelle", "lastname":"Jobs", "arrived":false, "id":2}
,{"firstname":"Adam", "lastname":"Smith", "arrived":true, "id":3}
,{"firstname":"Megan", "lastname":"Smith", "arrived":false, "id":4}
,{"firstname":"Dylan", "lastname":"Smith", "arrived":false, "id":5}
,{"firstname":"Ethan", "lastname":"Smith", "arrived":false, "id":6}
];
Using the following ng-repeat filtering:
<ul>
<li ng-repeat="person in attendees track by person.id | filter:arrived:'false'">
{{person.lastname}}, {{person.firstname}}
</li>
</ul>
I feel like I've tried every permutation that I can find referenced, most of which came from various StackOverflow search results:
filter:'arrived'
filter:arrived
filter:'person.arrived'
filter:person.arrived
filter:{arrived:true}
filter:{arrived:'true'}
filter:{person.arrived:true}
filter:{person.arrived:'true'}
I've also tried creating a custom filter function:
$scope.isArrived = function(item) {
return item.arrived;
};
And applying it thusly:
filter:isArrived
filter:'isArrived'
filter:{isArrived(person)}
filter:isArrived(person)
filter:'isArrived(person)'
None of these seems to work. What am I missing?
Here's a plnkr that demonstrates my problem.
The track by needs to be at the end of the expression:
<li ng-repeat="person in attendees | filter: {arrived: false } track by person.id">
#Gruff's answer is right, but just to give an answer from an official source:
From the Angular ng-repeat docs:
Note: track by must always be the last expression:
<div ng-repeat="model in collection | orderBy: 'id' as filtered_result track by model.id">
{{model.name}}
</div>
It also appear on the "Arguments" part of the docs:
Note that the tracking expression must come last, after any filters,
and the alias expression.

AngularJS not able to include markup in IDs?

I have an array of 3 items and want them to be "ng-repeated"
<li ng-repeat="item in obj.items id="testobj{{testobj.number}}">
</li>
When I look at the page, it appears that the id of the "li" is just "testobj" for all 3 items and not testobj1 testobj2 testobj3 like I was expecting. What is the issue?
Your ng-repeat attribute is missing a final ".
The {{ }} binding is probably coming back with no data and so being treated as if it was an empty string. I see no reference to testobj (the scope variable) anywhere outside of your binding. Is this defined, or should your id read id="testobj{{item.number}}" or something similar?

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