tomi lui
tomi lui

Reputation: 29

Unable to assign data to class variable from the httpCilent Module in Angular 11 ( eg. inside http.get().subscribe(...))

this is my first Stack Overflow post so bare with me if I formatted anything incorrectly.

This is my first Angular project and I can't assign data retrieved from the http.get().subscribe(...) method to my component class variable.

My component:

...
export class LeafletMapComponent implements OnInit, AfterViewInit {

  data

  constructor(private ps: PeopleService, private NgZone: NgZone) {}


  ngAfterViewInit(): void { 

       console.log(this.allData) **DOESN'T WORK (logs undefined)**

  }


  ngOnInit(): void {

    this.ps.httpGetData().subscribe( data => {

      this.data= data
      console.log(this.data) ***WORKS (logs all my data) BUT...***
    })
    
    console.log(this.data) ***THIS DOESN'T WORK... WHY? (logs undefined)***
    
  }
}

My Service:

...

@Injectable({
  providedIn: 'root'
})
export class PeopleService implements OnInit {
  
  ...

  httpGetData() {
    return this.http.get<any>(this.URL) **WORKS**
  }

  ...
}

My question is: In this piece of code

ngOnInit(): void {

    this.ps.httpGetData().subscribe( data => {

      this.data= data
      console.log(this.data) ***WORKS (logs all my data) BUT...***
    })
    
    console.log(this.data) ***THIS DOESN'T WORK... WHY? (logs undefined)***    
}


Because I have already assigned my data to a class variable (this.data = data) inside the

.subscribe( data => {this.data = data})

method, I assume that I would be able to console.log(this.data) right after the subscribe method inside the ngOnInit() function, I would assume that this.data has already been initialized and can be accessed anywhere

I want to retrieve data as soon as my component loads, assign it to my class variable so my variable can be used in the ngAfterViewInit() function. However, inside the .subscribe() method, I am able to assign the returned data to my class variable "this.data" and log it, but, console.log(this.data) only works inside the .subscribe() method, nowhere else.

Also, does NgAfterViewInit() run before or after ngOnInit()? I am trying to access my class variable (this.data) from the NgAfterInit() function, but I must get the data from my service before I can do that...

Does this have to do with modifying Observable objects?

Edit: console.log(this.data) now works inside the ngOnInit function when I changed to a async function

async ngOnInit(): Promise<void>{
    const res = await this.ps.httpGetData().toPromise()
    this.data = res
}

But ngAfterViewInit won't detect the change because it is still logging undefined for the value this.data.

Please help and thanks in advance!

Upvotes: 0

Views: 570

Answers (2)

Ritik Mishra
Ritik Mishra

Reputation: 718

Based on the code you've written, this is probably the sequence of events:

  1. ngOnInit in LeafletMapComponent is called
    • It makes the HTTP request
    • It runs console.log(this.data) which prints undefined
  2. ngAfterViewInit is called
    • It runs console.log(this.data) which again prints undefined
  3. The HTTP request finishes
    • the subscribe callback is called
      • the field this.data is set
      • it prints some non-undefined value

The solution to this is to make sure that the HTTP request finishes in ngOnInit. You can achieve this like so:

async ngOnInit(): Promise<void> {
    this.data = await this.ps.httpGetData().toPromise();
    console.log(this.data)
}

ngAfterViewInit() will get called after ngOnInit() is called according to the Angular documentation about lifecycle hooks. However, if ngOnInit() is an async function, then it may complete after ngAfterViewInit() because Angular will not await the Promise returned by ngOnInit(). This means that you have to store a Promise or an Observable as your class variable instead of just the raw data in order to keep track of the fact that its an async value.

Upvotes: -1

Wisam Shaker
Wisam Shaker

Reputation: 199

Your code assigns the data correctly, however, it seems like you aren't clear on the concept of asynchronous code.

When you call subscribe on your observable, that's an asynchronous call, meaning your code continues execution without waiting for the execution of the observable to complete. In other words, the code sends a request and continues execution without waiting for the response. Once the response is available, the callback function you supply to the subscribe method as an argument is executed. That's why the console.log() you call after subscribe logs nothing.

As for your question about ngAfterViewInit, it executes after ngOnInit.

Read more about this in Angular Docs

Upvotes: 0

Related Questions