0

I've a problem trying to get data in ngOnInit from a http service. I need to set my data in a var when the component is fired, so I can use it in another functions.

I've achieved this by doing async ngOnInit, and receiving the response from a Promise function, but I dont think its a good practice ? How can I improve it?

Here is my code:

COMPONENT.TS



     async ngOnInit() {
        this.username = localStorage.getItem('username');
        this.segment.value = 'FUNCIONAL';
        this.categoria = this.segment.value;
        this.listadoClases = await this.claseServicio.obtenerListadoClases(this.categoria); // **Need to set this variable at onInit**


SERVICE.TS


    getClases(actividad) {
        return this.http.get(`https://anders-test.herokuapp.com/clases/obtenerPorCategoria/${actividad}`);
      }


      obtenerListadoClases(categoria) {
        return new Promise(resolve => {
          this.getClases(categoria).subscribe(data => {
            if (data) {
              resolve(data)
            }
          })
        })
      }

Im doing this because Im trying to learn/understand async/await. I want to level up my code a little bit.. But Im not getting the idea after reading a lot..

Thanks in advance guys!

Nikssj_
  • 187
  • 2
  • 3
  • 11
  • 1
    It's basically fine, although there's an easier way to convert an observable into a promise: just `return this.getClases(cetegoria).toPromise()`. – mbojko Oct 16 '19 at 06:41

4 Answers4

2

Nyco, why on earth do you make the things so complex?

The service

  getClases(actividad) {
        return this.http.get(`https://anders-test.herokuapp.com/clases/obtenerPorCategoria/${actividad}`);
      }

The component

//inject the service in constructor
constructor(private service:Service){}

ngOnInit() {
    this.username = localStorage.getItem('username');
    this.segment.value = 'FUNCIONAL';
    this.categoria = this.segment.value;
    this.service.getClases(this.categoria).subscribe(res=>{
        this.categorias=res
    })
}

You should avoid subscribe in services except for some special function. Think that Angular is make over Observables. If you need convert an Observable in Promise, should be a good reason for this

Eliseo
  • 50,109
  • 4
  • 29
  • 67
  • Gotcha! Can you please add how I can make it if I use observables? I just want to get the whole idea of how to work my services – Nikssj_ Oct 16 '19 at 07:05
  • Nico, this.http.get return an Observable in self. In the guide: https://angular.io/guide/http you has info about httpClient. Yes, we can create an Observable using 'rxjs' "creations": https://www.learnrxjs.io/operators/creation/, but httpClient methods **return Observables** too, see https://angular.io/api/common/http/HttpClient. The 'rxjs' creations, are utils when we want,e.g. not return a value else an observable of the value -use 'of'-, or when we want create a timer -use `timer`-,etc – Eliseo Oct 16 '19 at 07:14
  • Excellent explanation! Thanks for your time :) – Nikssj_ Oct 16 '19 at 07:19
0

HttpClient returns an Observable object in angular. You can just subscribe to it and will give you the response and works on multithread. In this, you will avoid the user to wait until the response comes. It is a good practice and meanwhile, you could do other operations independent of the api response data.

But if you want to wait until the data are fetched from api then you can use Promise and it can be handled by keyword then. In modern Javascript, you can handle it using async/await. Which is more look like synchronous code and easy to read and understand.

Please follow this

 ngOnInit() {
    this.username = localStorage.getItem('username');
    this.segment.value = 'FUNCIONAL';
    this.categoria = this.segment.value;
    this.obtenerListadoClases(); 

it is not a good practice to add async before ngOnInit

 async obtenerListadoClases() {
    try {
           this.listadoClases = await this.claseServicio.obtenerListadoClases(this.categoria);
        }).catch((e) => {
           console.log(e);
        });
  }

As HttpClient returns an Observable object in angular 5* in that case you have to make the method asynchronous which will return a promise. That you can do the way you have done.

return new Promise((resolve, reject) => {
      this.getClases(categoria).subscribe((res: any) => {
                resolve({status: 200, data: res});
            }, (error) => {
                resolve({status: 400, data:[]});
            },
            () => {
                resolve({status: 201, data:[]});
            });
    })

For more please check this out Using async-await feature in Angular

sibabrat swain
  • 1,277
  • 8
  • 20
  • "it is not a good practice to add async before ngOnInit" - care to explain, why? – mbojko Oct 16 '19 at 06:39
  • I've done like you told me but on console.log it still shows undefined ngOnInit() { this.obtenerListadoClases(); console.log(this.listadoClases, 'listadoclases'); // Undefined – Nikssj_ Oct 16 '19 at 06:41
  • @mbojko Please have a look at this https://stackoverflow.com/a/39492163/9739044 – sibabrat swain Oct 16 '19 at 06:43
  • @NicoAybar please import it like this `import {Component, OnInit} from '@angular/core';` and implement it like this `export class YourrComponent implements OnInit { }` – sibabrat swain Oct 16 '19 at 06:44
  • @sibabratswain I've that :/ – Nikssj_ Oct 16 '19 at 06:46
  • @NicoAybar Please add a `try catch` block to check what is the error it return from the Promise. It might be you are not getting naything in data and in else condition you are not resolving the promise in your service. – sibabrat swain Oct 16 '19 at 06:51
  • @sibabratswain sorry, the link you provided doesn't state that "it is not a good practice to add async before ngOnInit". No wonder, considering it's an answer from September 2016, when there was no such thing as `async-await` in JavaScript. – mbojko Oct 16 '19 at 06:52
  • @NicoAybar please follow the updated answer. Use the update Promise Code and let me know if it works. – sibabrat swain Oct 16 '19 at 06:59
  • 1
    @sibabratswain, an Observables is async – Eliseo Oct 16 '19 at 07:01
0

In order to get the data and store it in a variable, the cleanest solution I could think about is to fire an http request OnInit lifecycle method and store is in Observable.

For example:

constructor(private http: HttpClient) {}

ngOnInit() {
    this.response$ = this.http.get('http://...');
}

Later on, you can turn the response$ into promise with toPromise method and use async/await with it.
Also, be careful if you use the response$ multiple times, because it will trigger multiple http requests, you can use the shareReplay operator like this:

ngOnInit() {
    this.response$ = this.http.get('http://...').pipe(shareReplay(1));
}
Tal Ohana
  • 1,128
  • 8
  • 15
0
/* On SERVICE */

 getOffice(id: string) {
    return this.firestore.collection('offices').doc(id).valueChanges();
 }

/* On COMPONENT */

 ngOnInit(): void {
    let id = this.route.snapshot.params['id'];
    this.service.getOffice(id).subscribe((val) => 
      this.officeDetails = val;
      console.log(this.officeDetails);
    });
 }
Vivek Jain
  • 2,730
  • 6
  • 12
  • 27