How to convert an Observable return into an array of objects of my class in Typescript?

Viewed 348

I have an endpoint that returns an Observable<Operacao[]>, but I need an array of Operacao[]. How to convert this return to get an Operacao array?

This is my endpoint:

    obterOperacoes(): Observable<Operacao[]> {
        let operacoes = this.http
            .get<Operacao[]>(this.UrlServiceV1 + 'operacoes', super.ObterAuthHeaderJson())
            .pipe(catchError(super.serviceError));
        return operacoes;
    }

Ps: Operacao is a simple Typescript class.

When I inspect what's in operacoes, what I find is this:

enter image description here

3 Answers

You need to subscribe to your observable first. Next, assign the the array you get from the http get, to your variable operacoes and then return variable operacoes.

This is a bad solution but it is the solution that you want. Why is it bad? Because you might get an empty array due to asynchronicity in JavaScript as pointed out by @Carsten below.

obterOperacoes(): Operacao[] {
    let operacoes = new Array<Operacao>();
    this.http.get<Operacao[]>(
      this.UrlServiceV1 + 'operacoes',
      super.ObterAuthHeaderJson()
    )
      .pipe(catchError(super.serviceError))
      .subscribe((res) => {
        operacoes = res;
      });
    return operacoes;
}

I suggest you put your api calls inside a service to return the observable and then subscribe the service from your component.ts file. See sample below:

sample.service.ts

import { Injectable } from '@angular/core';
...
@Injectable()
export class SampleService {
    constructor() { }

    obterOperacoes(): Operacao[] {
        return this.http.get<Operacao[]>(
          this.UrlServiceV1 + 'operacoes',
          super.ObterAuthHeaderJson()
        )
          .pipe(catchError(super.serviceError));
    }
}

component.ts

export class MyComponent  {
  operacoes: Operacao[] = [];
  constructor(private sampleSrvc: SampleService ){
    this.myServices.sayHello();
  }

  getOperacao() {
    this.sampleSrvc.obterOperacoes().subscribe((res) => {
      this.operacoes = res;
    });
  }
}

Do these things

Step 1 - Mark the class @Injectable()

@Injectable()
export class Operacao  {

Step 2 - Inject that dependency in your component's constructor()

export class MyComponent implements OnInit {
  // ...
  operacoes: Operacao[] = [];
  constructor(private operacao: Operacao) {}
  // ...
}

Step 3 - Subscribe to the Observable in the component like this

this.operacao.obterOperacoes().subscribe((result) => {
  this.operacoes = result;
});

Documentation: Dependency injection in Angular

Subscribing to the observable is the correct solution. However care should be taken to avoid memory leaks. This happens when allowing a component to be destroyed without cleaning up existing subscriptions.

If you are using the results of a subscription in a template, you can use the async pipe in order to automatically subscribe and then automatically destroy the subscription on component destroy

<div *ngFor="let obterOperaco of obterOperacoes | async">
  {{ obterOperaco }}
</div>

If you prefer to use this in the component ts file, you will need to handle the destruction manually.

obterOperacoes().pipe(
  takeUntil(/*an observable that is complete onNgDestroy*/)
).subscribe(obterOperaco => {})

Further reading on unsubscribing here: https://blog.bitsrc.io/6-ways-to-unsubscribe-from-observables-in-angular-ab912819a78f

I like to use a library to handle the boilerplate of destroy and recommend using https://www.npmjs.com/package/@ngneat/until-destroy

Related