【问题标题】:Error handling with Angular2 async pipeAngular2异步管道的错误处理
【发布时间】:2016-11-08 20:49:43
【问题描述】:

我正在使用 Angular2 异步管道将值流式传输到 DOM。这是一个非常简单的例子:

const stream = Observable.interval(1000)
  .take(5)
  .map(n => { if (n === 3) throw "ERROR"; return n; });

<div *ngFor="for num of stream | async">
  {{num}}
</div>

<div id="error"></div>

我想做的是显示 1-5 的序列,但是在错误项 (3) 上,以某种方式使用错误消息填充 #error div。

这似乎需要两件事:首先是 Angular 异步管道能够智能地处理错误,我没有看到任何迹象。看源码,显然是抛出了一个JS异常,看起来不太友好。

其次是错误后重新启动或继续序列的能力。我已经读过catchonErrorResumeNext 等等,但它们都涉及另一个序列,该序列将在错误时切换到。这极大地复杂了生成流的逻辑,我只想在上面放置一系列数字(在这个简单的例子中)。我有一种下沉的感觉,一旦发生错误,游戏就结束了,observable 完成了,只能用不同的 observable 来“重新启动”。我还在学习 observables;真的是这样吗?

所以我的问题是双重的:

  1. Angular2 的异步管道可以在错误的情况下执行智能操作吗?
  2. observables 是否有一些简单的方法可以在出错后继续?

【问题讨论】:

    标签: error-handling angular rxjs


    【解决方案1】:

    是的,关于 catch 运算符和发生错误后执行某些操作的能力是正确的......

    我会利用catch 运算符来捕捉错误并做一些事情:

    const stream = Observable.interval(1000)
      .take(5)
      .map(n => {
        if (n === 3) {
          throw Observable.throw(n);
        }
        return n;
      })
      .catch(err => {
        this.error = error;
        (...)
      });
    

    在模板中:

    <div>{{error}}</div>
    

    为了能够继续最初的 observable,您需要从发生错误的地方开始创建一个新的 observable:

    createObservable(i) {
      return Observable.interval(1000)
        .range(i + 1, 5 - i)
        .take(5 - i)
      });
    }
    

    并在catch 回调中使用它:

      .catch(err => {
        this.error = error;
        return this.createObservable(err);
      });
    

    这两个问题可以帮助你:

    【讨论】:

    • 我的理解是我们需要使用异步管道,只有在我们确定没有错误的情况下,是正确的吗?
    【解决方案2】:

    1) 不,async 管道订阅和取消订阅并返回它接收到的事件。您需要在错误收到async 管道之前对其进行处理。

    2) 您可以使用 catch 运算符,当它返回一个 observable 时,它​​的值由 .catch(err =&gt; Observable.of(-1)) 发出,而不是错误。

    您可以使用它来发出一个特殊的“错误”值,然后使用*ngIf="num === -1 之类的东西以某种特殊方式显示错误值。

    你可以找到更多关于这个https://blog.thoughtram.io/angular/2017/02/27/three-things-you-didnt-know-about-the-async-pipe.html的信息

    【讨论】:

      【解决方案3】:

      @Thierry Templier 的答案是正确的,但现在有点过时了。以下是使用最新 RXJS 的方法。

      this.myObservable$ = this.myService.myFunc().pipe(
        catchError(() => of([])) // this will emit [] if the request fails - u could handle this [] emit on error in the service itself
      )
      

      然后 HTML 正常:

      <div *ngFor="let xxx of (myObservable$ | async)">
      </div>
      

      注意在 Observable 名称末尾的 $ 是 Angular 推荐的表示 Observable 的方式。

      【讨论】:

        【解决方案4】:

        我遇到了类似的问题并想出了另一种方法。我不知道这是否是一种好方法,但它确实有效。

        要显示可观察结果的模板:

        <div *ngIf="tableData$ | async as tableData; else loader" class="mt-4">
          <!-- do something with tableData -->
        </div>
        
        
        <ng-template #loader>
          <loading [target]="tableData$"></loading>
        </ng-template>
        

        loading 组件:

        export class LoadingComponent implements OnInit {
        
          private _errorMessageSubject : Subject<string> = new Subject<string>();
          
          private _errorMessage$ : Observable<string> = this._errorMessageSubject.asObservable();
          public get errorMessage$() : Observable<string> { return this._errorMessage$; }
        
          private _target : Observable<any> | null = null;
          public get target() : Observable<any> | null { return this._target }
          
          // this input does nothing except catch the error and feed the
          // message into the errorMessage subject.
          @Input() public set target(o: Observable<any> | null) { 
            if(o == null) { return; }
            this._target = o.pipe(
              catchError((error, _) => {
                this._errorMessageSubject.next(error);
                return of(null);
              }),
            );
          };
            
          
          constructor() { }
        
          ngOnInit(): void {
          }
        
        }
        

        加载器模板:

        <div *ngIf="target && target | async;">
        </div>
        
        <div *ngIf="errorMessage$ | async as error; else loading">
          <p class="text-danger">{{ error }}</p>
        </div>
          
        <ng-template #loading> <!-- simply a spinner icon -->
          <div class="d-flex justify-content-center">
            <fa-icon [icon]="['fas', 'spinner']" size="6x" [spin]="true"></fa-icon>
          </div>
        </ng-template>
        

        我不确定它是否是订阅 observable 两次的好方法,因为订阅是在需要数据的原始组件和加载器中完成的,但除此之外这似乎可以正常工作。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2019-04-12
          • 1970-01-01
          • 2017-02-22
          • 2017-09-18
          • 1970-01-01
          • 1970-01-01
          • 2017-02-14
          • 1970-01-01
          相关资源
          最近更新 更多