Alex Bryanskiy
Alex Bryanskiy

Reputation: 435

Angular 8. How to assign observable value to service property in constructor?

In my angular application I have service for making requests to rest API. Before make any call I need to get unique guid from backend and pass it as parameter to every call. So I thought I can make request inside constructor and assign result to property like this:

export class BaseApiService<T> implements OnDestroy {

  private subscription: Subscription;
  protected tenantGuid: string;

  constructor(private http: HttpClient) {
     this.subscription = this.getGuid().subscribe(data => {
        this.tenantGuid = data;
     });
  }

  private getGuid(): Observable<string> {
    return this.http.get<string>(`${this.baseUrl}/api/Tenants`);
  }
}

But property tenentGuid is still undefined after constructor executed in derived service.

export class TasksService extends BaseApiService<TaskModel> {
  constructor(private httpClient: HttpClient) {
    super(httpClient);
  }

  public list(): Observable<TaskModel[]> {

   return this.httpClient.get<TaskModel[]>(this.generateUrl(), {params: {tenantguid: this.tenantGuid}})
  }

}

I understand that this associated with asynchronous, but I don't find out how to achieve result in another way.

Would be appreciated for any advise)

Upvotes: 0

Views: 1278

Answers (2)

DeborahK
DeborahK

Reputation: 60528

Maybe something more like this:

NOTE: NOT syntax checked.

Base class

export class BaseApiService<T> implements OnDestroy {

  protected tenantGuid$ = this.http.get<string>(`${this.baseUrl}/api/Tenants`).pipe(
     shareReplay(1)
  );
}

Class

export class TasksService extends BaseApiService<TaskModel> {
  constructor(private httpClient: HttpClient) {
    super(httpClient);
  }

  public list$ = this.tenantGuid$.pipe(
        switchMap(tenantguid => 
        this.httpClient.get<TaskModel[]>(this.generateUrl(), {params: {tenantguid: this.tenantGuid}})
  ))
 }

Using a declarative approach, you can define the stream that provides the GUID. Pipe it through shareReplay so the value is replayed to any other stream that uses it.

Then in the class, when a GUID value is emitted, get the related data. If your UI is then bound to list$ using an async pipe, it will automatically update with the retrieved data when it is returned.

UPDATE:

You could also use a routing resolver to ensure your GUID is retrieved before routing to your first route. But that may cause a delay in the startup of your application.

Upvotes: 1

Amit Chigadani
Amit Chigadani

Reputation: 29715

You can make use of Subject from rxjs to deal with asynchronous call. You have to call list() once the guid is received

export class BaseApiService<T> implements OnDestroy {

  private subscription: Subscription;
  protected tenantGuid: string;

  protected guidChange: Subject<void> = new Subject<void>()

  constructor(private http: HttpClient) {
     this.subscription = this.getGuid().subscribe(data => {
        this.tenantGuid = data;
        this.guidChange.next(); // emit guid received event
     });
  }

  private getGuid(): Observable<string> {
    return this.http.get<string>(`${this.baseUrl}/api/Tenants`);
  }
}

derived class

export class TasksService extends BaseApiService<TaskModel> {
  constructor(private httpClient: HttpClient) {
    super(httpClient);
    this.guidChange.subscribe(()=> {
      this.list(); // by now guid should be set
    })
  }

  public list(): Observable<TaskModel[]> {

   return this.httpClient.get<TaskModel[]>(this.generateUrl(), {params: {tenantguid: this.tenantGuid}})
  }

}

Upvotes: 1

Related Questions