transforming data returned from firebase - angularjs

What I'm trying to do: receive data from fire base, and display some values from that data to the screen.
How I've gone about doing it: I have created an Angular SPA and the data I return from Firebase is as follows:
Returned Data from Firebase
Error I'm receiving:
I then use an *NgFor loop in the html portion of the component as follows:
<div class="row">
<ul class="list-group" *ngFor = "let guardian of guardians">
<li class="list-group-item">{{ guardian.firstname }}</li>
</ul>
</div>
ERROR Error: Error trying to diff '[object Object]'. Only arrays and iterables are allowed.
I have looked for solutions to this and the common answer is to transform the received data into an array. I have attempted to do that with the following:
for (const guardian in data) {array.push(guardian);}
However, the array I receive back is missing all of my values. It comes back in this form:
["-L3n95t-rxA4-bOgc8fz", "-L3nF0h5EEKtwAiZv0Q7",
"-L3oWoBmoXK3-5XBkx9i", "-L3oWxEhAcUGQX2P4yES", "-L41_cK3KD6DMduhG3P3",
"-L4CbTtNqGuVyT3hzY-R", "-L4CfKsBxfQxSKd2PR4s", "-L4EDFkbsWMrT61fLjhD"]
This allows me to use my *NgFor statement, but this data is useless to me now. Any knowledge on how to transform this data?
After three days of banging my head against this one any help is appreciated! :)

The data in that array is actually each key within the guardians object. Now what you want to do is utilize those keys. Lets assume you gave your key array a value such as this.guardianKeys = array
Then you can iterate through the keys and access the original guardians objects like so:
<div class="row">
<ul class="list-group" *ngFor = "let key of guardianKeys">
<li class="list-group-item">{{ guardians[key].City }}</li>
<li class="list-group-item">{{ guardians[key].Phone }}</li>
<li class="list-group-item">{{ guardians[key].State }}</li>
...
</ul>
</div>

This solution solved my problem.
for (const guardian in data) {array.push(data[guardian]);}
by pushing this into an empty array I was then able to iterate through the array with *NgFor.

Related

How to run a *ngFor loop inside other loops

I get a lot of data coming from an API, and among that data, I have these images:
Photos: "uploads/60bcb8f0244b2.png,uploads/60bcb8f025427.png"
I used *ngFor to present all the data that exists, "Photos" is one of them. My question is, how can I separate the link from this "Photos" field? Note that it currently has a comma separator. I can't use split(',') inside *ngFor. What would be the solution?
You can try split(",") as below
<ul>
<li *ngFor="let item of data">
{{item.text}}
<div *ngFor="let p of item.photos.split(',')">
{{p}}
</div>
</li>
</ul>
Here is example for you: https://stackblitz.com/edit/angular-ivy-i92axf?file=src/app/app.component.html

Looping in AngularJS not working

I'm trying a simple AngularJS looping using 'ng-repeat' directive as below :
<div ng-app="" ng-init="numbers=[1,3,5,2]">
<ul>
<li ng-repeat="item in numbers">{{ item }}</li>
</ul>
</div>
The result of this is as below, which is perfect
1
3
5
2
However, when I change the 'numbers' array like this
<div ng-app="" ng-init="numbers=[1,3,5,2,2]">
being the rest as is, it does not work.
The only change I have made is that I've added one more item in the 'numbers' array '2'. The issue I figured out is whenever an item is repeated in the array ( '2' in this case ), the problem occurs.
The console log I noticed is like below
Error: [ngRepeat:dupes] http://errors.angularjs.org/1.3.14/ngRepeat/dupes?p0=item%20in%20numbers&p1=number%3A2&p2=2
at Error (native)
at http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js:6:417
at http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js:232:494
at Object.fn (http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js:122:53)
at l.$get.l.$digest (http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js:123:138)
at l.$get.l.$apply (http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js:126:58)
at http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js:17:479
at Object.e [as invoke] (http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js:36:315)
at d (http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js:17:400)
at tc (http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js:18:179)
Also, if the array is of string type values, the same problem persists too.
For example, <div ng-app="" ng-init="names=['Bishnu', 'Sagar', 'John', 'Bishnu']">
in this case also I'm facing the same issue.
This behavior of AngularJS is very strange.
Does anyone know the reason, and how to resolve?
Try this...
The ngRepeat directive instantiates a template once per item from a collection. Each template instance gets its own scope, where the given loop variable is set to the current collection item, and $index is set to the item index or key.
ngRepeat makes the corresponding changes to the DOM
When an item is added, a new instance of the template is added to the DOM.
When an item is removed, its template instance is removed from the DOM.
When items are reordered, their respective templates are reordered in the DOM.
By default, ngRepeat does not allow duplicate items in arrays. This is because when there are duplicates, it is not possible to maintain a one-to-one mapping between collection items and DOM elements.
If you do need to repeat duplicate items, you can substitute the default tracking behavior with your own using the track by expression
<div ng-repeat="n in [42, 42, 43, 43] track by $index">
{{n}}
</div>
Refer:https://docs.angularjs.org/api/ng/directive/ngRepeat
As per the Angular Docs Duplicates are not allowed. You need to use 'track by' expression to specify unique keys.
Created this Plnkr for your reference
<div ng-app="" ng-init="numbers=[1,3,5,2,2]">
<ul>
<li ng-repeat="item in numbers track by $index">{{ item }}</li>
</ul>
</div>
You need to use track by $index to iterate through duplicate entry as well.
you can try like this
<div ng-repeat="value in [4, 4] track by $index">{{value}}</div>

ng-repeat doesn't work on multidimonsional array

I have the following array and following code but im stuck. The ng-repeat is not working on this array(for other scopes it works good). I am missing something, but i cant figure out what it is.
Example msgArray (array is created dynamically, i copied this from browser console)
[john: Array[0], Carl: Array[2]]
html Code
<ul>
<li ng-repeat='(k, v) in msgArray'>{{ k }}</li>
</ul>
This is not an array, but an object with array properties. The (k, v) syntax of ng-repeat iterates over each property of the object with k as the key and v as the value. If you want to display the first element of each value, you would do something like:
<li ng-repeat="(k, v) in msgArray">{{ v[0] }}</li>
You can also ng-repeat over v (or "messages" if you want to be more explicit) if you need to do that as well. Note that you only need the (k, v) syntax if you actually want to use the key.
<li ng-repeat="messages in msgArray">
<span ng-repeat="msg in messages">{{msg}}</span>
</li>
http://plnkr.co/edit/jnnvYxVCiHOEnkU4b2Iv?p=preview
Also see the ng-repeat documentation on this

Angularjs : how to use key of object to retrieve value of another json object

i have an array of json objects
$scope.arrary = [{"a":"value"},{"b":"value2"},{"c":"value3"}];
$scope.secondObje = {"a":"this is updated value","b":"this is new value"}
i can iterate through 'array' using
<div ng-repeat="(key,value) in array">
now how can i access value of second object using key
I want something like {{secondObje.key}}, how can i do this in angular.
so that i get "this is updated value" and "this is new value" printed.
Please help me.
Your code fails because $scope.arrary is an array, therefore, key in <div ng-repeat="(key,value) in arrary"> gives you indexes starting from 0 (i.e. 0, 1, 2) instead of alphabets (a, b, c) which you intended.
Option 1:
One way to handle this is to change the format of $scope.arrary so that you have
$scope.arrary = [{"key":"a", "value":"value"},{"key":"b", "value":"value2"},{"key":"c", "value":"value3"}];
You can then use the keys on the second object as
<div ng-repeat="a in arrary">
{{ secondObje[a.key] }} <br>
</div>
Option 2:
However, if you want to keep $scope.arrary the way it is, you can use keys on the second object as
<div ng-repeat="a in arrary">
<div ng-repeat="(key, value) in a">
{{ secondObje[key] }} <br>
</div>
</div>
DEMO
Here is a working demonstration of these: http://jsfiddle.net/5NzZX/119/
I prefer the first option.

correct strategy to generate html dynamically with angular

I have a huge JSON object tree with two levels. First level has around 500 elements, and each element contains an average of 100 child elements.
I want to display the first level of the tree and I am doing it with a simple ng-repeat. When the user clicks on the element I want to display the child elements of that element. If I use a span ng-switch or a ng-show to show/hide child elements when the page first renders it freezes for around 10 seconds while generating all the HTML.
It doesn't sound like the right solution. There must be a different way of doing it, but I can't figure out. Anyone knows?
I have explained most in my comment, and here is a working plunker:
http://plunker.co/edit/RSZwfLlsCJ68MUkACbdp?p=preview
the new ng-if directive will do what you want
<h1>ng-if</h1> <h5>Click on the level to expand</h5>
<div class="well">
<ul class="nav nav-list" ng-repeat="(attr,element) in tree">
<li ng-click="expand=!expand" ng-class="{'active':expand}"><a>{{element.name}}</a></li>
<ul ng-if="expand" class="nav nav-list">
<li ng-repeat="item in element.items">{{item.name}}</li>
</ul>
</ul>
</div>
you can also do this in the "old-way" with ng-show using new ternary operator or its alternative expr && if_true || if_false
<h1>old-way</h1> <h5>Click on the level to expand</h5>
<small>use ternary operator or <pre>expand && element.items || []</pre></small>
<div class="well">
<ul class="nav nav-list" ng-repeat="(attr,element) in tree">
<li ng-click="expand=!expand" ng-class="{'active':expand}"><a>{{element.name}}</a></li>
<ul ng-show="expand" class="nav nav-list">
<li ng-repeat="item in (expand ? element.items : [])">{{item.name}}</li>
<!--<li ng-repeat="item in (expand && element.items || [])">{{item.name}}</li>-->
</ul>
</ul>
</div>
See this answer on ng-repeat performance. Essentially, it just takes a long time since Angular's ng-repeat, and basically all other directives, are set up to always look for updates in the whole JSON structure. So if you have lots of data and don't need live updates in the HTML view when changing the JSON, I wouldn't recommend using AngularJS. Generally, AngularJS performance also depends a lot on the browser and its JavaScript engine.
Alternatively, you could divide your JSON into subparts and then use pagination to display it.
I would recommend to fetch the data gradually from the server. Use server-side pagination and retrieve only the fields that you are going to display. Then, when a user clicks on one of the first level items, you can do another XHR to the server with the new data. I had similar requirements for a project and that solved the latency issue.
Regards,
Agustin.

Resources