js map function index does not increment - reactjs

probably missing something obvious but my map function's index param will not increment up its just stuck as 0 or whatever static index I set it to, maybe I'm missing something because my data structure im passing in is an array of objects like:
(20) -> [{},{},{}]
and each object has quite a few properties
I'll provide my map function below for greater context
also I can manually change my index and my code works fine grabbing an object at whatever index I specify i.e '5' or '13' and my depth is correct because its displaying the property values as it should, wondering if I need to nest something to make this work?
if I console log my state I have this structure here just an array of objects
//storing to state
componentDidMount(){
fetch(`https://api.nasa.gov/neo/rest/v1/feed?start_date=${this.state.time[0]}&end_date=${this.state.time[1]}&api_key=ipAxYzaENbqRKb7GgzFPcH6QUBsHXY3QKB7uXOf5`
)
.then(response => response.json())
.then((data) => {
let ast = []
Object.values(data.near_earth_objects).forEach((arr)=>{
//push the two arrays together
ast.push(...arr)
})
this.setState({asteroids:[ast]})
});
}
render() {
return (
<div>
<h4>Near earth objects</h4>
<table>
<tbody>
<tr>
<th>Name</th>
<th>ID</th>
<th>Magnitude</th>
<th>Hazardous</th>
<th>Sentry Object</th>
</tr>
{
this.state.asteroids.map((arr,index)=>(
//for each item (arr) there will be properties
<tr key={arr.toString()}>
<td >{arr[index].name}</td>
<td >{arr[index].id}</td>
<td >{arr[index].absolute_magnitude_h}</td>
<td >{arr[index].is_potentially_hazardous_asteroid==true? "true": "false"}</td>
<td >{arr[index].is_sentry_object==true? "true": "false"}</td>
<td > index {index}</td>
</tr>
))
}
</tbody>
</table>
</div>
)
}

The error is in componentDidMount when you setState of asteroids your taking the array and putting it inside another array
this.setState({asteroids:[ast]})
// instead it should be
this.setState({asteroids: ast })
Checkout this codesandbox with a working example of your code https://codesandbox.io/s/empty-forest-jvwin?file=/src/App.tsx
As for why the index is stuck, it's because the asteroids array in your example has only 1 element (the array that is holding all the asteroids data).

Related

Django REST Framework Render a result from an hyperlink in API response

I am building an application with a DRF/React stack, and I have a bit of an issue.
I am using a HyperlinkedModelSerializer to be able to easily make a GET request with the link given. On a less optimistic note, I am not able to query my result that easily.
Here is an example of my json object I get from my API
[
{
"url": "http://localhost:8000/session/3/",
"session_start": "01:00:17.234060",
"nb_person": 1,
[...]
"client": "http://localhost:8000/client/1/"
}
]
There is an array I want to render in my template. Althought, I wanted to show the client firstname and lastname I get from my client link.
function GetContactFromAPI(url)
{
var name = ""
axios.get(url).then(response => {
const data = response.data
name = `${data.firstname} ${data.lastnom}`
return name
})
return name
}
return (
<div>
<table>
<thead>
<tr>
<th>Client</th>
<th>Nb Pers.</th>
<th>Starting time</th>
</tr>
</thead>
<tbody>
{sessions.map((session, index) => {
return (
<tr key={index}>
<td>{GetContactFromAPI(session.client)}</td>
<td>{session.nb_person}</td>
<td>{session.session_start}</td>
</tr>
)
})}
</tbody>
</table>
</div>
);
I think I have some misunderstanding on sync/async in order to render the result I need. Do you have an idea to resolve this need?
I was thinking to get an client list and to get client I need. But I wanted to avoid this request that may bigger than needed (I have more than 40K clients).
Thanks in advance
I may need to see your serializer where the client field is located, but what you need to do is create a Nested Relationship for the client field. Try to do this:
client = ClientSerializer()
Refer to the documentation here

Iterate with ngFor on objects obtained from Firebase

I have a list of objects stored in firebase database:
I am getting this list using angular http get request. After getting it I want to iterate on a li in html template using ngFor="let subject of subjects" which gives Error:
ERROR Error: Cannot find a differ supporting object '[object Object]'
of type 'object'. NgFor only supports binding to Iterables such as
Arrays.
As I searched online, I came to know that ngFor can only be used on arrays while I am getting nested objects. Can anyone please suggest me what should I do iterate these objects.
So far I have tried Array.of which converts whole list into someArray[0]. I also tried to manually change the unique ids of objects into array indexes [0, 1, 2] which worked but when I add new subject using http post firebase automatically assigns unique id to new subject making it uniteratable.
In simple words tell me to convert nested objects into an arrayList or how can I change the firebase default behavior of assigning unique id in angular (I found something like firebase push function which I couldn't understand).
Update:
(Code from ExaminerService)
getBatchSubjects(batch){
return this.http.get(firebaseLinkGoesHere);
}
(Code from Component)
onBatchSelected(event){
this.selectedBatch = event.target.value; //getting value of a html select
this.examinerService.getBatchSubjects(this.selectedBatch)
.subscribe(
(response : Response) => {
this.selectedBatchData = response.json();
}
),(error) => {
console.log(error);
}
}
(Code from HTML Template)
<div class="batchDetails">
<table class="table table-striped">
<thead>
<tr>
<th class="text-center">S.No</th>
<th>Subject Name</th>
<th>Facilitator</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let subject of selectedBatchData.subjects; let i = index">
<td class="text-center"> {{i + 1}} </td>
<td> {{subject.name}} </td>
<td> {{subject.facilitator}} </td>
</tr>
</tbody>
</table>
</div>
You can use Object.values to turn the properties into an array. This can be done in several ways.
Method 1 - By applying the map operator to the observable data (see this stackblitz):
import { Observable } from "rxjs/Rx";
public subjects: Array<any>;
this.examinerService.getBatchSubjects(this.selectedBatch)
.map(x => (Object as any).values(x.json().subjects))
.subscribe(
(values) => { this.subjects = values; },
(error) => { console.log(error); }
);
Method 2 - With a property getter or a method of the component class (see this stackblitz):
get subjects(): Array<any> {
return (Object as any).values(this.selectedBatchData.subjects);
}
Method 3 - By converting the values in the subscribe callback (see this stackblitz):
public subjects: Array<any>;
...
this.selectedBatchData = response.json();
this.subjects = (Object as any).values(this.selectedBatchData.subjects);
Template
In the template, the ngFor directive would iterate over the subjects array:
<tr *ngFor="let subject of subjects; let i = index">
<td class="text-center"> {{i + 1}} </td>
<td> {{subject.name}} </td>
<td> {{subject.facilitator}} </td>
</tr>
try following code snippet.
ngFor="let subject of subjects|async
Update
In your ExaminerService you should import FirebaseListObservable in order to define return type FirebaseListObservable<any[]>
import { AngularFireDatabase, FirebaseListObservable } from 'angularfire2/database';
export class ExaminerService{
constructor(private db: AngularFireDatabase) {}
getBatchSubjects(batch){
return this.db.list('/subjects');
}
}
In your Component should look like this
export class ExaminerComponent implements OnInit {
movies: any[];
constructor(private examinerDb: ExaminerService) { }
ngOnInit() {
this.examinerDb.get().subscribe((snaps) => {
this.selectedBatchData = snaps;
});
}
}
Angular has pipe "keyvalue" which will let you parse your object into pairs like subject.key and subject.value

Why the function inside <td> did not run

I want to render the table data like below.
However, the second line of "Available Channel" return value is number and I want to get the string. like 0--None, 1--Web, 2--Pos....
I try to use the paymentType function to switch it but it did not work.
It seems that this function did not run at all. But I do not know why
<table>
...
<tr>
<td> { this.paymentType(id) } </td>
<tr>
</table>
And the function I wrote likes:
paymentType(id){
if(id===0) { return "None"}
else if(id===1) { return "Web"}
else if(id===2) { return "Pos"}
}
I only got a blank line.
I changed it to arrow function, still a blank line.
<table>
...
<tr>
<td> { () => this.paymentType(id) } </td>
<tr>
</table>
I bind this in the constructor method, still a blank line.
constructor(props){
super(props)
this.paymentType = this.paymentType.bind(this)
}
I wonder how to solve this problem and all your replies would be appreciated.
Just call your paymentType function directly, don't wrap it an an arrow function declaration:
{ this.paymentType(payment.payment_type_id) }
As you had it you efectively had a function declaration in the curly braces, you were not calling your helper function.

Angular2 - ngFor single object vs array

I have a situation where I receive an object of data from my database that contains records for versions of a file. There could be 5 versions (5 records) or a single version returned.
My issue is that I am using ngFor to loop over the array of data and print them to the table. In the event that there is only a single record returned, the data is no longer an array of objects and is just a single object.
<tbody class="ruleVersions">
<tr *ngFor="let m of mapData.ruleVersion.x">
<td class="small">V {{ m.RuleVersion }}.0</td>
<td [innerHtml]="m.CreatorNTID | ntidLink"></td>
<td class="small">{{ m.VersionNotes ? m.VersionNotes : 'N/A' }}</td>
<td class="small">{{ m.BasedOnRuleVersionID ? m.BasedOnRuleVersionID : 'N/A' }}</td>
<td class="small">{{ m.MetaInsertUtc }}</td>
</tr>
</tbody>
Multiple Records:
Single Record:
This poses a problem because the ngFor is set up to loop over arrays. Due to the single record not being an array of objects like when there are multiple, it throws this error:
Cannot find a differ supporting object '[object Object]' of type 'object'. NgFor only supports binding to Iterables such as Arrays.
What is the best way to solve for this? Is there anything in angular I can use to treat the data as an array even if its not, perhaps with a pipe of some sort?
The only other way I can think of is passing the entire object through a function and having it push objects into arrays if its a single record. That then poses the problem of not wanting every single record to be an array.
Just curious if there are any built in angular ways to solve for this?
Update:
I solved this by creating a pipe.
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({ name: 'isArray' })
export class IsArrayPipe implements PipeTransform {
transform(x): any {
return Array.isArray(x) ? x : [x];
}
}
<tr *ngFor="let m of mapData.ruleVersion.x | isArray: x">
When you get your response, you can check if it's array or not. If it's not an object, you can set the object in an array, so something like this in service:
getData() {
return this.http.get('url')
.map(response => {
let res = response.json();
if(!Array.isArray(res)) {
return [res]
}
return res;
})
}
So now, no matter if you get an array or an object, it's always iterable with *ngFor as the component always receives an array.

Mootools 1.3.2 Request.HTML fetch table rows

First off please don't bash me for using old technology (polling) and an old version of Mootools (1.3.2) as I don't have control over these factors.
Ok here's my problem.
I have a page which refreshes every few seconds to fetch new data from the database via AJAX. Ideally the structure of the returned value should be as such:
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
</tr>
After receiving this table row structure result, I need to append that to the current table in the page which essentially just adds a new record on the table if there are new records. If there's none, then no changes to the table are made.
Currently I am using
var req = new Request.HTML({url: url_to_get_new_rows,
onSuccess: function(html, responseHTML) {
// append table row 'html' here
}
}).send();
However, the returned value in the 'html' variable that I'm supposed to append at the end of the table only returns
1 2 3 4
This obviously is an undesired behavior as I need the tr and td elements to make it work.
I hope someone could help me with this problem.
THANKS!
Javascript:
new Request.HTML({
url:'tr.php',
onSuccess: function(responseTree, responseElements, responseHTML, responseJavaScript) {
var tbody = document.id('tbody');
tbody.set('html', tbody.get('html') + responseHTML);
// or
var tr = new Element('table', {'html': responseHTML}).getElement('tr');
tr.inject(tbody);
}
}).get();
HTML:
<table>
<thead>
<tr>
<th>1</th>
<th>2</th>
<th>3</th>
<th>4</th>
</tr>
</thead>
<tbody id="tbody">
<tr>
<td>a</td>
<td>b</td>
<td>c</td>
<td>d</td>
</tr>
</tbody>
</table>

Resources