Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Casting a type (interface) via map to observable

I've been learning TypeScript with Angular. And currently I stuck as this moment. Previously I used subscribed method and everything works flawlessly, but not I decided to rewrite the code using async pipe and it just does't work....

  1. RestAPI retursn requests in particular format

export interface responseFormat {
  success: boolean;
  data: any;
}
  1. returned data can be in different types. In this particular case data returns restaurant object... It also might be arrays of different objects...

export interface restaurant {
  _id: string;
  name: string;
  description: string;
  images: file[];
  cuisines: cuisine[];
  blocked: boolean;
  created: Date;
  business_hours: object;
}
  1. Previously I used the following code (it works):

/* Provider */

getRestaurant(): Observable<responseFormat> {
  return this.http.get<responseFormat>(environment.api + "/restaurant");
}

/* Component */

private restaurant: restaurant;

getRestaurant() {
  this.restaurantProvider.getRestaurant().subscribe(res => {
    if (res.success) this.restaurant = res.data;
  });
}

as a result the type of the variable is assigned.... But I don't want to keep the record of subscriptions thus I wanted to convert the code using Angular async pipe... This is what I've done... it doesn't works

/* Provider */
getRestaurant(): Observable<responseFormat> {
  return this.http.get<responseFormat>(environment.api + "/restaurant");
}

/* Component */
private restaurantObservable: Observable<restaurant>;

getRestaurant() {
  this.restaurantObservable = this.restaurantProvider.getRestaurant().map(res => <restaurant>res.data);
  // or //
  this.restaurantObservable = this.restaurantProvider.getRestaurant().map(res => res.data as restaurant);
}

But the above code doesn't cast the type of the response to the interface...

What am I doing wrong???

In other words I can get the Observable variables in template (with https://angular.io/api/common/AsyncPipe> like that:

/* Provider */
getRestaurant(): Observable<responseFormat> {
  return this.http.get<responseFormat>(environment.api + "/restaurant");
}

/* Component */
private restaurantObservable: Observable<restaurant>;

getRestaurant() {
  this.restaurantObservable = this.restaurantProvider.getRestaurant().map(res => res.data as restaurant);
}

/* Template */
<div class="name">{{ (restaurantObservable | async)?.name }}</div>
// or //
<div *ngIf="restaurantObservable | async as restaurant">
  <div>{{ restaurant.name }}</div>
</div>

But I can't get the property of the restaurant observable in component... For instance this.restaurant.cuisines throw the error "property cuisines does not exist on type Observable"

like image 393
Timothy Avatar asked Apr 03 '18 09:04

Timothy


2 Answers

Why not directly return an Observable<restaurant> from your service?

getRestaurant(): Observable<restaurant> {
  return this.http.get<responseFormat>(environment.api + "/restaurant")
    .map((response: responseFormat) => response.data as restaurant);
}

Then on component side:

getRestaurant() {
  this.restaurantProvider.getRestaurant().subscribe((res: restaurant) => {
    this.restaurant = res;
  });
}

The error handling (success flag, HTTP errors, etc) could be handled via an HTTP Interceptor.

like image 161
Jeto Avatar answered Oct 22 '22 22:10

Jeto


The Angular async template pipe subscribes to the Observable, that is how it is able to access the restaurant object and its properties in the template.

If you want to access to these same thing in the component controller then you need to make a subscription in the component controller.

like image 1
Sámal Rasmussen Avatar answered Oct 22 '22 22:10

Sámal Rasmussen