首页 > 解决方案 > Angular - 从服务发送请求比从组件发送请求更好?

问题描述

我想知道我是否应该从角度服务发送请求?还是我应该直接从组件发送它?

第一种方法:

餐厅服务.ts

  getRestaurants(): Promise<_Restaurant[]> {
    return this.get("/restaurants").toPromise();
  };

餐厅.component.ts

  loadRestaurants = async () => {
    try {
      this.restaurants  = await this.restaurantService.getRestaurants();
    } catch (exception) {
      console.log(JSON.stringify(exception, null, 2));
    }
  }

这意味着请求是通过组件触发的。

第二种方法:

餐厅服务.ts

  async getRestaurants(): Promise<_Restaurant[]> {
    try {
      const response: _Restaurant[] = await this.get("/restaurants").toPromise() as _Restaurant[];
      return response;
    } catch (exception) {
      throw new Error(exception);
    }
  };

餐厅.component.ts

  loadRestaurants = async () => {
    try {
      this.restaurants  = await this.restaurantService.getRestaurants();
    } catch (exception) {
      console.log(JSON.stringify(exception, null, 2));
    }
  }

这意味着请求是从服务中触发的,然后将响应作为承诺返回

那么最好的方法是什么?如果是第二种方法,是否可以从服务中捕获错误并将其扔给组件?

标签: javascriptangulartypescriptserviceasync-await

解决方案


正如 Angular 文档所说,最好在服务中包含该逻辑,看看这个:

class Service {
  constructor(public http: HttpClient) { }

  getRestaurants(): Observable<Restaurant> {
    return this.http.get<{ /* Specify HTTP response schema */ }>(url).pipe(
      // Transformation to actual Restaurants at one place
      map(data => data.map(restaurant => new Restaurant()),
      // Handle error
      catchError(err => {
        logError(err);
        throw err;
        //  Or...
        return of([]); // Mock data not to crash app
      }),
      // If multiple subscription are made to same source, it won't do multiple http calls
      shareReply(1),
    );
  }
}
class Component {
  restaurants: Restaurant[] = [];

  ngOnInit(): void {
    // Prefered way
    this.restaurants$ = this.service.getRestaurants().pipe(
      // If, you pass error down, you'll be able to hendle it here...
      catchError(err => {
        return of([]);
      }),
    );
    // Alternative
    this.cleanUp = this.service.getRestaurants().subscribe(restaurants => {
      this.restaurants = restaurants;
    });
  }

  ngOnDestroy(): void {
    this.cleanUp.unsubscribe();
  }
}

HTML

<!-- Observable -->
<div *ngFor="let restaurant of restaurants$ | async">
  {{restaurant | json}}
</div>

<!-- Non-Observable -->
<div *ngFor="let restaurant of restaurants">
  {{restaurant | json}}
</div>

我已将您的代码从 promise 转换为 observables,因为 observables 是使用 Angular 的最大好处之一。Observables 可以被取消,在模板中可读性很好,还有很多其他我可能有一天会想到的东西。


Observables 非常强大,您可以始终拥有基于其他 observable 的新鲜信息。看看吧,也许会给你一些想法……

interface ApiResponse<type> {
  awaitingNewValues: boolean;
  error: null | any;
  response: type;
}

class Service {
  currentRestaurantID = new BehaviourSubject(1);

  currentRestaurantInfo: Observable<ApiResponse<Restaurant>>;

  constructor(private http: HTTPClient) {
    let latestRestaurants: ApiResponse<Restaurant | undefined> = {
      awaitingNewValues: true,
      error: null,
      response: [],
    };
    currentRestaurantInfo = this.currentRestaurantID.pipe(
      switchMap(restaurantID => {
        return concat(
          // This will notify UI that we are requesting new values
          of(Object.assign({}, latestRestaurants, { awaitingNewValues: true })),
          // The actual call to API
          this.http.get(`${apiUrl}/${restaurantID}`).pipe(
            // Wrap response in metadata
            map(restaurant => {
              return {
                awaitingNewValues: false,
                error: null,
                response: restaurant,
              }
            }),
            // Notify UI of error & pass error
            catchError(err => {
              return of({
                awaitingNewValues: true,
                error: err,
                response: undefined,
              });
            }),
          ),
        );
      }),
      // Save last response to be used in next api call
      tap(restaurants => this.latestRestaurants = restaurants),
      // Prevent calling API too many times
      shareReplay(1),
    );
  }
}

推荐阅读