Angularjs: Ng-repeat and unique filter in array - angularjs

I have a set of data like this
$scope.students = [
{ name:"John", courses:["Math", "Physics", "Chemistry"] },
{ name:"Paul", courses:["Economics", "Math", "Sociology"] }
]
I would like a way to filter using angular-filter so that I can get a list of all the subjects without repetition.
I've been trying to use unique filter, but I cannot get it to work since I try to iterate like
<ul ng-repeat="student in students">
<li ng-repeat="x in student.courses | unique:courses"></li>
{{x}}
</ul>
My desired output of the first ng-repeat would be an array like this:
["Math", "Physics", "Chemistry", "Economics", "Sociology"]
so I could iterate through it in the second one.
I have achieved this throught making a new scope with just the desired array, but then I cannot bind it properly, so I would like to achieve it through filtering. Thanks in advance.

I would really recommend you using the libraries Lodash or Underscore for this kind of problems. Learning to master these has helped me a lot!
Of course, you can create your own angular filter using one of them. The method you would like to use is union:
_.union(_.flatten(_($scope.students).pluck("courses")))
I use pluck to get out the courses arrays from the studens object, then I flatten the result (to get rid of the array that it comes nested in), and then i use union to get each subject only once.

If the requirement actually is to:
...get a list of all the subjects without repetition.
Then I would make a separate array for subjects:
$scope.courses = [];
// There are many ways to do skin this cat... (prograde does have a point!)
$scope.students.forEach(function(student) {
student.courses.forEach(function(course) {
if ( $scope.courses.indexOf(course) === -1 ) {
$scope.courses.push(course);
}
})
});
HTML
<ul>
<li ng-repeat="course in courses">
{{ course }}
</li>
</ul>
And if the students change I would recreate the courses-array.
But it sounds as if you are trying to do something else.
But it doesn't give the desired output.
It would help if you tell us what that desired output is!
http://plnkr.co/edit/bZNjIEFznvuyTU3zxAp1?p=preview

Related

Angular orderby sorting in loaded orders

By using ng-repeat i'm getting values but i need to sort it in specific order.
that is 'Portfolio', 'Sourcing', 'Delinquency'
can you please help me by providing sorting order.
HTML:
ng-repeat="row in DashboardFilter2" | orderBy: ['Portfolio', 'Sourcing', 'Delinquency']
js:
App.createList({
"qDef": {
"qFieldDefs": %Type
},
"qInitialDataFetch": [
{
"qHeight": 20,
"qWidth": 1
}
],
},function Show2(reply, app){
$scope.DashboardFilter2 = app.field("%Type").getData();
});
ng-repeat looks correct to me.
Would be good to see app.field("%Type").getData() result.
But to be honest, I would not use orderBy with strong reason since it is a filter and evaluates on every $digest/$apply.
Instead, I'd sort as I want in JS before putting it into template using Array.sort or similar method (lodash, underscore).
If the array changes after initialization then just make a function sortArrayYouWant and call every time it changes.
The global idea is NOT TO USE ORDERBY since it is extremely inefficient

ng-repeat over array of objects vs. object containing objects

In almost all the examples of ng-repeat I've seen, the data is structured like so:
$scope.dataCollected = [{name: bob, data: '10-12-12'}, {name:joe, data: '09-13-13'}];
However, the only way I can get ng-repeat to work is if I structure the data like this:
$scope.dataCollected = {bob: {name: bob, data: '10-12-12'}, joe: {name:joe, data: '09-13-13'}};
Structuring it as an array causes the ng-repeat to do absolutely nothing. It doesn't even give an error. Structuring it as an object containing objects works, but I'd like to use an array because I understand it's the only way to use a filter on ng-repeat.
I'm calling the ng-repeat like this:
<div class="list-row" ng-repeat="data in dataCollected">
<h3 class="name"> {{data.name}} </h3>
</div>
What am I missing?
Sorry guys, thank you for the help. The issue was that in an attempt to make my data easier to read, I had assigned names to the keys of the array using bracket notation as seen in the answer here: stackoverflow.com/questions/12244483/… ng-repeat does not like that at all. It seems the default keys are necessary.

Display one item from array in AngularJS without using ng-repeat

I have JSON array like:
var data = [
{
"name": "Jim",
"age" : 25
},
{
"name": "Jerry",
"age": 27
}
];
in HTML:
<ul>
<li ng-repeat="val in data | filter:{age: age}:true">{{val.name}}</li>
</ul>
The above code is working fine as i want the output, but i don't want to use ng-repeat where it creates loop and then i filter.
is there any another way where i can directly select the DATA from array where age = age ??
Filters can be used in any angular expression (there is nothing ngRepeat-specific about them).
(They can be even used in JavaScript.)
E.g. the following expression displays the data associated with the first user (limitTo:1) that has an age of 25 (filter:{age: 25}), formatted as JSON (json):
{{data | filter:{age: 25}:true | limitTo:1 | json}}
See, also, this short demo.
As mentioned by #link in comments, there is no way to pluck the object with the required age from the array without looping through it. However, you're right in thinking that using ng-repeat here is not appropriate if you only want to display a single object from the array, so ideally the required object should be stored in your controller.
One approach would be to use $filter in your controller instead:
$scope.obj_with_correct_age = $filter("filter")(data, {age: age});
<li>{{ obj_with_correct_age.name }}</li>
I realize this is an old thread but for completeness.
This works:
{{data.Systems[data.Detail.SystemId].System}}
This way, you don't need to filter the data.
If you want to parse single value of name. like {"name":"value"}.
so after $http post, i am assuming that your response in var data= response.
Now find, like console.log(data[0].name);
Here, result will be Jim

Angular: Getting list with ng-repeat with dividers / separators

Still pretty new with Angular, just finding my way around.
I'm using ng-repeat to output an alphabetised list of names. I'd like to add dividers within this list that act as labels.
Example:
--------
A
--------
Author 1
Author 2
--------
B
--------
Author 3
Author 4
etc
My thinking is to use nested ng-repeats to loop through the alphabet, getting an object with the authors for that specific letter with a second ng-repeat. Here's what I have so far:
<div data-ng-repeat="letter in alphabet">
<div class="item item-divider">
{{letter}}
</div>
<ul>
<li data-ng-repeat="speaker in GetSpeakers(letter)" type="item-text-wrap" href="#/speaker/{{speaker.ID}}">
{{speaker.title}}
</li>
</ul>
</div>
Controller code:
.controller('SpeakersCtrl', function($scope, $routeParams, StorageHandler) {
$scope.GetSpeakers = function(letter) {
// Get list of authors for that letter
console.log('test '+letter);
};
$scope.alphabet = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'];
})
Fiddle: http://jsfiddle.net/t6Xq8/
I have a couple of questions.
In general, is using a nested ng-repeat a good approach for this
problem, or does Angular have built-in specifically for this purpose? Some sources also say using a function in ng-repeat is a bad idea. But it does work so I'm confused as to why I shouldn't use this.
When looking at the console, GetSpeakers gets called twice in this example and I can't figure out why?
How should I return an object to the scope within the GetSpeakers function, while preventing overloading the $scope?
I think you can do this in a much simpler way without:
having to do a nested ng-repeat
writing out the whole alphabet
needing to handle letters which have no author
manipulating your original array of objects or copying it
writing much fewer lines of code
by using $index.
so that would look something like this:
<div ng-repeat="speaker in speakers | orderBy : 'name'">
<div class="item item-divider" ng-if="firstLetter(speaker.name) != firstLetter(speakers[$index-1].name)">
{{firstLetter(speaker.name)}}
</div>
{{speaker.name}}
</div>
This would need a simple function to get the first letter such as:
function firstLetter(name) {
return name && name.charAt(0);
}
So what this does is it compares the first letter of whatever you passed to the previous object's first letter and if they are different it adds the divider with that letter. Pretty clean and simple :)
Check out this working JsFiddle
You can obviously improve on that code to handle upper/lowercase (i.e. always capitalize before comparing) as well as extract the comparison into a function for cleaner code.
Likely better to map the data into a single object where the object keys are the letter. I'll assume you have objects like:
{id:123, firstName:'Frank',lastName :'Enstein'}
and want the letter to represent last names
var tmp={};
for(i=0;i<authourArray.length;i++){
var letter=authourArray[i].lastName.charAt(0);
if( tmp[ letter] ==undefined){
tmp[ letter]=[]
}
tmp[ letter].push( authourArray[i] );
}
/* likely want to loop over all the arrays now and sort unless already sorted from server*/
$scope.repeaterObject=tmp;
Now in markup will ng-repeat all the letters in the object keys, and within that loop, do an ng-repeat of the authors array for that letter
<div data-ng-repeat="(letter, authors) in repeaterObject">
<div class="item item-divider">
{{letter}}
</div>
<ul>
<li ng-repeat="author in authors">{{author.firstName}} {{author.lastName}}</li>
</ul>
</div>
Resulting object will look like:
{
A:[.....],
......
E:[ {id:123, firstName:'Frank',lastName :'Enstein'}, /* other E author objects*/ ],
......
}
I believe that using nested ng repeats is perfectly fine. Im not familiar with a better way that angularjs provides to iterate over multi dimensional arrays/data structures.
The reason you should avoid using functions in ng repeats is that angularjs will call that function each time it will create the element in the repeat directive. As charlie suggested above it would be better to order the authors once and use the resulting array each time, rather than ordering the authors each time you display them. This has the added benefit of being able to reuse the array

AngularJS - ng-repeat is not working for inner properties

Here is an object that I'd like to use with ng-repeat, but it's not able to see the inner firstlang property:
$scope.school{
name : "stackoverflow",
sub :{
firstlang : "kannada"
}
}
Here is my ng-repeat:
<ul>
<li ng-repeat="index in school.sub">
first language is = {{index.firstlang}}
</li>
</ul>
The desired result is: first language is = kannada
Looking at your example it is not very clear how the school.sub data structure should look like in the end... Is it supposed to be an object (as in the example data structure) or an array (as you ngRepeat seems to suggest).
Provided that the school.sub is an object this would work: http://jsfiddle.net/pkozlowski_opensource/WXsFD/1/
<li ng-repeat="(key, value) in school.sub">first language is = {{value}}</li>
On the other hand, if you plan your sub structure to be an array the proper approach would be:
http://jsfiddle.net/pkozlowski_opensource/WXsFD/2/
<li ng-repeat="index in school.sub">first language is = {{index.firstlang}}</li>
Hope that the above jsFiddles clarify how to approach both situations.

Resources