0

i'm working on a project and i need to retrieve data from a custom server. I made an httpclient service for handle requests and i encountered this problem: when i try to make the subscription for pass data to the component, nothing happens, but if i do the subscription in the component, evetything works fine. I reassume the code below:

The component:

import { HttpClient } from '@angular/common/http';
import { Component, OnInit } from '@angular/core';
import { BehaviorSubject, catchError, map, Observable, of} from 'rxjs';
import { Food } from '../api/api-models';
import { ApiService } from '../api/api.service';

@Component({
  selector: 'app-in-home-list',
  templateUrl: './in-home-list.component.html',
  styleUrls: ['./in-home-list.component.css']
})
export class InHomeListComponent implements OnInit {

  res: Food[];
  error: string;

  constructor( private api: ApiService, private http: HttpClient){
  }

  ngOnInit() {
    //this.http.get<Food[]>('https://localhost:5001/').pipe(catchError(this.api.handleError)).subscribe({next: r => this.res = r, error: e => this.error = e});
    [this.res, this.error] = this.api.getInHomeList();
  }
}

The commented line is the one that works here, but not in the service, the one not commented is what i want to properly work.

The service:

import { Injectable} from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Food } from './api-models';
import { catchError, throwError } from 'rxjs';

@Injectable({
  providedIn: 'root'
})

export class ApiService {

  private result: Food[];
  private error: string;

  constructor( private http: HttpClient) {

  }

  getInHomeList(): [Food[], string] {

    this.http.get<Food[]>('https://localhost:5001/').pipe(catchError(this.handleError)).subscribe({next: r => this.result = r, error: e => this.error = e});

    return [this.result, this.error];
  }

  handleError(error: any) {
    let errorMessage = "";
    if (error.error instanceof ErrorEvent) {
        // client-side error
        errorMessage = `Error: ${error.error.message}`;
    } else {
        // server-side error
        errorMessage = `Error Code: ${error.status}\nMessage: ${error.message}`;
    }
    console.log(errorMessage);
    return throwError(() => new Error(errorMessage))
  }
}

The @NgModule in app.module.ts:

@NgModule({
  declarations: [
    AppComponent,
    InHomeListComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    HttpClientModule,
  ],
  providers: [
    ApiService
  ],
  bootstrap: [AppComponent]
})

I'm kinda new to Angular so maybe i'm missing something, or i didn't understand well how really Observable works.

Mattia
  • 3
  • 1
  • 2
  • 1
    It's asynchronous, the subscription callback won't be called before you return. – jonrsharpe Nov 27 '21 at 22:03
  • Hi, thank you for answering. Can you explain me better? I red on the Angular documentation that the call to the server is called when the method .subscribe() is executed. Is that correct? So you mean that the return happens before the http request ends? – Mattia Nov 27 '21 at 22:08
  • Thank you for sharing that, that clarified me better the point. – Mattia Nov 27 '21 at 22:52
  • Thank you very much! That may do the thing! – Mattia Nov 27 '21 at 23:27

1 Answers1

0

What the comment above means is that the content inside of the "subscribe" method is asynchronous, which means your getInHomeList() method does not necessarily run in order it is written. Meaning, the inside of your "subscribe()" method may take 5 seconds (or more or less) before it gets a response, but your "return" line is going return right away, without data.

The common way to have a http data-service is something like this, return the objservable itself to the component:

    public getInHomeList(): Observable<Food[]> {
        return this.http.get<Food[]>('https://localhost:5001/')
        .pipe(catchError(this.handleError));
      }

      handleError(error: any) {
        let errorMessage = "";
        if (error.error instanceof ErrorEvent) {
            // client-side error
            errorMessage = `Error: ${error.error.message}`;
        } else {
            // server-side error
            errorMessage = `Error Code: ${error.status}\nMessage: ${error.message}`;
        }
        console.log(errorMessage);
        return throwError(() => new Error(errorMessage))
      }

Then subscribe to the observable and get the results in any component you need them:

ngOnInit() {
    this.api.getInHomeList()
        .subscribe({next: r => this.res = r, error: e => this.error = e});
  }

The inside of the "subscribe" method is asynchronous, so it may take longer to set the value of "R". But regardless of how long it takes to get the response, the rest of the ngOnInit() method is going to continue to run.

MDave
  • 1,245
  • 13
  • 29