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

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.

Related

Angularjs: Ng-repeat and unique filter in array

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

angularjs - track by in a for loop

I have a problem with the refresh button in my Angular application:
I have a json object with two arrays in it -> data = {array1: [], array2: []}
I want to loop both arrays and print the related values. To achieve that, I use the Angular ng-repeat directive like this:
ng-repeat="index in [] | index:getTotalNumberOfObjectsInArray1()"
...
<span ng-bind="data.array1[index]"></span>
<span ng-bind="data.array2[index]"></span>`
My Problem is, that on every click on the refresh-button these data will be recreated on the dom.
I learned the documentation about "track by" but i understand it only on objects (for example: ng-repeat="a in objects track by a.id")
How can I use this track by filter for my ng-repeat above, to prevent the recreation of my data?
You can use $index that is defined in a ng-repeat (cf.doc) but you have to have the two array with the same length.
Another solution could be to merge your arrays together and loop on it but I am not sure if it answer to your question.

Iterate over array which includes objects

I am wondering how can I view different objects within an object using ng-repeat?
This is the following code that I have so far, to iterate over the $ID of the objects in array:
<div ng-repeat="message in messages"> {{message.$id}} </div>
This is how my array looks like: http://imgur.com/8quH0iW
As you can see, the array structure looks like that:
--Item[0]:
-----$Id:xxxxx
-----Message1(unique ID):
-------------:date: "XX-XX-XXXX"
-------------:message: "whatever"
-------------:sender: "xxxxxxxxxxxxxxxxxx"
-----Message2(unique ID):
-------------:date: "XX-XX-XXXX"
-------------:message: "whatever"
-------------:sender: "xxxxxxxxxxxxxxxxxx"
--Item[1]:
-----$Id:xxxxx
-----Message1(unique ID):
-------------:date: "XX-XX-XXXX"
-------------:message: "whatever"
-------------:sender: "xxxxxxxxxxxxxxxxxx"
-----Message2(unique ID):
-------------:date: "XX-XX-XXXX"
-------------:message: "whatever"
-------------:sender: "xxxxxxxxxxxxxxxxxx"
Etc.
TLDR: How can I use ng-repeat to repeat over the objects within an item of an array? Also, can I use ng-repeat to get the last object in an item? For example, the last Message sent?
Thanks!
You don't mention whether the items in your array are arrays or JSON objects. If you have an array of JSON objects then this will work
vm.objectsTest = [{$id : 123}, {$id: 234}];
<ul ng-repeat="o in vm.testObjects"><li ng-bind="o.$id"></li></ul>
If your items are arrays then you will need to access the object within the array, like this:
vm.objectsTest = [[{$id : 123}], [{$id: 234}]];
<ul ng-repeat="o in vm.testObjects"><li ng-bind="o[0].$id"></li></ul>
Alternately if you need to iterate over the items of your nested array, then you may need to have nested ng-repeats. However the best option would be to change your data structure as using ng-repeat excessively can create performance issues.
See this post for accessing the last element of ng-repeat
Different class for the last element in ng-repeat

AngularJS: nested ng-repeat (array in object) only works if there is one item in array, not when multiple

Okay, I have a weird issue. I have an array of objects. Each object contains another array (of strings). I loop over the array of objects using ng-repeat. Within the repeated code I ng-repeat over the array of strings. For some reason, this nested ng-repeat only works when the array of strings contains but one (1) item. When there are more items, it simply doesn't work.
Code
Result of <pre>{{ answer.value | json }}</pre>
[
"Apothekerskast",
"Apothekerskast",
"Koelkast ombouw"
]
The view (in a gist because posting here causes issues with markdown): https://gist.github.com/fabdrol/898e4ac9760fc358ce81
The data (in JSON), for easy reading: https://gist.github.com/fabdrol/089467fa09dad6e89e81
It doesn't like duplicates, use track by $index:
ng-repeat="val in vals track by $index"
JSFiddle: http://jsfiddle.net/nedq13q2/

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

Resources