1

I have the following service:

import { Injectable } from '@angular/core';
import { HttpClient, HttpResponse } from '@angular/common/http';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';

import { IRecordEvent } from 'app/shared/model/record-event.model';

type EntityArrayResponseType = HttpResponse<IRecordEvent[]>;

@Injectable({ providedIn: 'root' })
export class RecordsService {
   public resourceUrl = SERVER_API_URL + 'api/record-events';

   constructor(protected http: HttpClient) {}

   find(recordId: number): Observable<EntityArrayResponseType> {
      return this.http
         .get<IRecordEvent[]>(`${this.resourceUrl}/record-id/${recordId}`, { observe: 'response' })
         .pipe(map((res: EntityArrayResponseType) => this.convertDateArrayFromServer(res)));
   }

   protected convertDateArrayFromServer(res: EntityArrayResponseType): EntityArrayResponseType {
      if (res.body) {
          res.body.forEach((recordEvent: IRecordEvent) => {
            recordEvent.eventDate = recordEvent.eventDate ? moment(recordEvent.eventDate) : undefined;
          });
      }
return res;
}

And the following component (I put ellipsis to simplify code):

import { Component, OnInit } from '@angular/core';

import { IRecord } from 'app/shared/model/record.model';
import { IRecordEvent } from 'app/shared/model/record-event.model';
import { RecordsService } from './records.service';
import { HttpResponse } from '@angular/common/http';

@Component({
   ...
})
export class DetailledRecordComponent implements OnInit {
    recordEvents?: IRecordEvent[] | null;

    constructor(protected recordsService: RecordsService) {}

    ngOnInit(): void {        
       this.setRecordEvents();
    }

    setRecordEvents(): void {
        this.recordsService
          .find(this.record.id!)
          .subscribe((res: HttpResponse<IRecordEvent[]>) => (this.recordEvents = res.body));
   }
}

The service retrieves the following data from backend (8 recordEvents with same recordId):

0: {id: 2, eventDate: "2020-07-09", type: "AFF_DT_TRF_ACC_PREV", recordId: 1, recordCode: "D745/012561"}
1: {id: 3, eventDate: "2020-07-08", type: "AFF_DT_RECEV_DEM_PTF_PREV", recordId: 1, recordCode: "D745/012561"}
2: {id: 4, eventDate: "2020-07-08", type: "AFF_DT_ENV_FACT_PREV", recordId: 1, recordCode: "D745/012561"}
3: {id: 5, eventDate: "2020-07-09", type: "AFF_DT_ENV_CV_RACC_PREV", recordId: 1, recordCode: "D745/012561"}
4: {id: 6, eventDate: "2020-07-09", type: "AFF_DT_VAL_DDESC_PREV", recordId: 1, recordCode: "D745/012561"}
5: {id: 7, eventDate: "2020-07-09", type: "AFF_DT_AFFEC_CA_REA", recordId: 1, recordCode: "D745/012561"}
6: {id: 8, eventDate: "2020-07-08", type: "AFF_DT_ENV_PTF_REA", recordId: 1, recordCode: "D745/012561"}
7: {id: 9, eventDate: "2020-07-08", type: "AFF_DT_DMEO_REA", recordId: 1, recordCode: "D745/012561"}

In Google Chrome inspector I can see that it is correctly get from backend: Google Chrome inspector on service

My problem is that the component does not retrieve this data. I always get undefined:

Google Chrome inspector component

I used Observables though (Angular 2 component cannot retrieve data from service), I don't understand what I'm missing here.

I noticed something weird: when I debug the interface with Google Chrome's inspector I have this behavior:

1- In the service, the parameter (recordId) is set (with find() method in component) but res is not enter image description here

2- The component inits this.recordsEvents with undefined

3- Only after these two steps the service gets the objects from backend

Why the data is not retrieved in first step?

2 Answers 2

3

The problem that you are experiencing is because the component is rendered the first time before the observable has emmited any value.

The optimal solution would be to use the async pipe to wait for the async value.

It would look like this.

 export class DetailledRecordComponent implements OnInit {
        recordEvents?: Observable<IRecordEvent[]> = this.recordsService
          .find(this.record.id!); 
    
        constructor(protected recordsService: RecordsService) {}
}
    

And then in the .html file,

<div *ngIf="recordEvents$ | async as recordEvents"> 
    // Here you can use the recordEvents property recordEvents as desired
</div>   

Ask here, if there's something to further clarify..

Sign up to request clarification or add additional context in comments.

6 Comments

Thanks for help. It means that I'd have an Observable in my component and another one in my service? I tried to out your first code block but I have the following error: Property 'map' does not exist on type 'Observable<EntityArrayResponseType>'
If I make no change on my component and I only add the code for the html, I have this [INFO] Argument of type 'IRecordEvent[] | null | undefined' is not assignable to parameter of type 'Promise<unknown> | null | undefined'. [INFO] Type 'IRecordEvent[]' is missing the following properties from type 'Promise<unknown>': then, catch, [Symbol.toStringTag]
I don't get why it talks about Promises whereas I am using Observable
The async pipe will subscribe to an observable or a promise, that's the reason of the default error message. You should also be able to use the observable in the service directly in the .html file, but it's cleaner to add an observable in the component.
Ow ok I understand how | async works now but I don't like the fact that I have to put an Observable in my component. I found another way to manage asynchronous call, I put the answer below.
|
0

As @bernatampera pointed out, the component or the html has to manage an asynchronous call. He suggested to do so with | async in the html but I found another way that I find cleaner:

In my component, instead of assigning value directly, I call a onSuccess() method:

setRecordEvents(): void {
   this.recordsService
     .find(this.record.id!)
     .subscribe(
         (res: HttpResponse<IRecordEvent[]>) => this.onSuccess(res.body),
         () => this.onError()
     );
}

And I assign my value in this onSuccess method:

protected onSuccess(body: IRecordEvent[] | null): void {
   this.recordEvents = body;
}

I still use *ngIf="recordEvents" in my html to avoid compilation errors.

1 Comment

it would help if you also shared why this works and why it's a good solution. Might be educational for other people with the same issue

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.