0

I am working on a project where we are building a website using the MEAN stack. I am currently working on the front end and am attempting to read in data from an API and storing that data within an array that I can then access and use. I am running into a very odd issue.

I am aiming to load in an array of data containing latitude and longitude coordinates of all the countries in the world.

I have a service that looks like this:

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { Country } from '../models/country';

@Injectable({
  providedIn: 'root'
})
export class DataService {

  REST_API_SERVER = 'http://localhost:3000/countries/getCountries';

  constructor(private httpClient: HttpClient) { }

  getCountryData() : Observable<Country[]>{

    return this.httpClient.get<Country[]>(this.REST_API_SERVER);
      
  }
}

Where Country is a class with certain attributes.

I am then accessing this function and using it by doing the following in my component.ts file:

import { Component, OnInit, AfterViewInit, ViewChild, ElementRef, HostListener, Host } from '@angular/core';
import { Country } from '../models/country';
import { DataService } from '../services/data.service';

export class GlobeComponent implements AfterViewInit {

  listOfCountries!: Country[];

  constructor(private countryService : DataService) {
  }

ngOnInit() {
   
    this.countryService.getCountryData().subscribe((countries) => {
      this.listOfCountries = countries;
    });
} 

I then try to access listOfCountries but am unable to. For example, if I say:

 for (let i = 0; i < this.listOfCountries.length; i++) 

I get the following error:

ERROR TypeError: Cannot read property 'length' of undefined

However, if in ngOnInit() function I include the following line:

console.log("Country printed in ngOnInit : " + this.listOfCountries[0].Entity);

It magically starts working. BUT... I get a new error:

ERROR TypeError: Cannot read property '0' of undefined at GlobeComponent.ngOnInit

I am really confused as to why this is happening. Why does console.log populate the array? But why does it continue to claim that the array is undefined even though it is working? After including the console.log, I can access and use the array as normal. But I do not want to keep this console.log, so any help as to where I am going wrong would be much appreciated!

13
  • Please do some basic debugging. Use console.log() to make sure your variables contain what you expect them to contain. Commented May 6, 2021 at 16:58
  • Please, show the full component's code. I'd bet that you have problems with asynchrony. Commented May 6, 2021 at 17:00
  • @ChrisG I have tried using console.log but the issue is that the array is not being recognised as an array. Commented May 6, 2021 at 17:13
  • @Lynx242 thank you for the help, here is the full component code: github.com/jess-mw/desk23/blob/threejs/src/app/globe/… Commented May 6, 2021 at 17:13
  • the array is not being recognised as an array then it's not an array. Let's be clear: loading something from the server takes a bit, and the data is loaded asynchronously. Which means whether this.listOfCountries is populated or not depends on when exactly you're trying to access it. ngOnInit kicks off the request, but your app will render for the first time before the countries are loaded. Commented May 6, 2021 at 17:25

2 Answers 2

1

Okay, referring to your provided code here, you have 2 options:

Option 1

Create a method that is called when the value is present. Do not rely on AfterViewInit.

listOfCountries: Country[] = [];

ngOnInit() {
   this.countryService.getCountryData().subscribe((countries) => {
     this.listOfCountries = countries;

     this.initDependencies();
   });
 }

 initDependencies() {
   this.setScene();
   this.setCamera();
   this.setRenderer();
   this.setControls();

   this.createLightGroup();
   this.createGlobe();

   this.animate();
   this.setAllPoints(this.currentYear);
 }

Option 2

Only call this.setAllPoints(this.currentYear); separately, when the value is present.

listOfCountries: Country[] = [];

ngOnInit() {
   this.countryService.getCountryData().subscribe((countries) => {
     this.listOfCountries = countries;
     
     this.setAllPoints(this.currentYear);
   });
}

ngAfterViewInit() {
   this.setScene();
   this.setCamera();
   this.setRenderer();
   this.setControls();

   this.createLightGroup();
   this.createGlobe();

   this.animate();
}
Sign up to request clarification or add additional context in comments.

5 Comments

It works! Thank you so much! This makes so much more sense now. I think I need to revisit how the subscribe works and fully understand it.
What's with all the scene / camera / etc. stuff? This answer is helpful but will confuse newbies for sure in its current state.
@Chris G: I adjusted my answer. Now there is a reference to the link Hamza Qureshi provided. Sorry, I thought everybody had seen this in the conversation above.
@ChrisG it has to do with three js. I am building a globe to represent the earth and plotting coordinate points so that countries are clickable etc. Feel free to check out the github where we are currently in the process of documenting everything: github.com/jess-mw/desk23
@Hamza Qureshi: You're welcome. Glad to hear that it works. 🙂
0

You see the same error in both cases.

It happens because you initialize listOfCountries with an empty value, the value will be assigned to the listOfCountries property only when the subscription is called (after the countryService responded).

So, you need either put a default value listOfCountries: Country[] = []; or check if the listOfCountries is array before read the value.

3 Comments

Thanks for the response! So when I initialise it as an empty array listOfCountries: Country[] = []; I then get 0 items in my array. So the data isn't loaded in. In another tutorial I followed, I didn't have to initialise but it worked anyway. I'm not sure what the issue is here.
Depends on the time when you try to access the data from the array. If you wait until the response is processed and the subscription is called then it will work, if not you'll get an error from your question.
So I am currently trying to print a value of the array within ngoninit right after I load in the data from the service into the array this.listOfCountries. I am not sure if this is an appropriate time? I feel like since it is right after, the response must have been processed by then

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.