【问题标题】:Angular - It is sending requests from the service better than from the component?Angular - 从服务发送请求比从组件发送请求更好?
【发布时间】:2019-09-14 01:44:18
【问题描述】:

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

第一种方法:

RestaurantService.ts

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

Restaurants.component.ts

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

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

第二种方法:

RestaurantService.ts

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

Restaurants.component.ts

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

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

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

【问题讨论】:

    标签: javascript angular typescript service async-await


    【解决方案1】:

    正如 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 非常强大,您可以始终根据其他 observables 获得最新信息。看看吧,它可能会给你一些想法...

    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),
        );
      }
    }
    

    【讨论】:

    • 如果你有一个优秀的后端开发人员,你不需要实现像return of([]); 这样的变通方法。大声笑
    • @DmitryGrinko 听说过隧道吗?一旦你进去,一个人经常失去他的互联网连接,然后你会得到一个错误......另外,一个没有身份验证的用户怎么样......处理HTTP时可能会出现很多问题
    • 问问自己——如果我的后端工作不正常,为什么我需要空数组?
    • @DmitryGrinko 嗯,你没有,但重点是你可以......想象一下你有元数据,比如awaitingNewValuessuccessitems,你可以轻松地填充元数据并让应用程序显示错误。后端根本不是问题。
    • @DmitryGrinko 看看添加的例子,这是catchError的一些智能用法...
    【解决方案2】:

    最佳实践是使用 Observable

    RestaurantService.ts

    getRestaurants(): Observable<_Restaurant[]> {
        return this.get('/restaurants');
    };
    

    Restaurants.component.ts

    import { Subscription } from 'rxjs';
    
    sub: Subscription;
    
    loadRestaurants(): void {
        this.sub = this.restaurantService.getRestaurants()
            .subscribe(result => {
                this.restaurants = result;
            }, exception => {
                console.log(exception);
            });
    }
    
    ngOnDestroy() {
        this.sub.unsubscribe();
    }
    

    如果您需要修改响应,您应该在您的服务中使用pipe 方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-03-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-02-20
      相关资源
      最近更新 更多