简体   繁体   English

Angular2 异步管道的错误处理

[英]Error handling with Angular2 async pipe

I am using the Angular2 async pipe to stream values into the DOM.我正在使用 Angular2 异步管道将值流式传输到 DOM 中。 Here's a real simple example:这是一个非常简单的例子:

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>

What I would like to do is to have the sequence of 1-5 displayed, but on the error item (3), somehow populate the #error div with the error message.我想要做的是显示 1-5 的序列,但是在错误项 (3) 上,以某种方式用错误消息填充#error div。

This seems to require two things: first is the ability of the Angular async pipe to do something intelligent with errors, which I see no sign of.这似乎需要两件事:首先是 Angular 异步管道能够用错误来做一些智能的事情,我看不到任何迹象。 Looking at the source code, apparently it throws a JS exception, which doesn't seem too friendly.查看源码,显然是抛出了一个JS异常,看起来不太友好。

Second is the ability to restart or continue the sequence after the error.其次是在错误发生后重新启动或继续序列的能力。 I have read about catch and onErrorResumeNext and so on, but they all involve another sequence which will be switched to on an error.我已经读过catchonErrorResumeNext等等,但它们都涉及另一个将在错误时切换到的序列。 This greatly complicates the logic of generating the stream, on which I would just like to put a series of numbers (in this simple example).这使生成流的逻辑变得非常复杂,我只想在其上放置一系列数字(在这个简单的示例中)。 I have the sinking feeling that once an error occurs the game is over and the observable is completed and can only be "restarted" with a different observable.我有一种下沉的感觉,一旦发生错误,游戏就结束了,可观察对象已完成,只能用不同的可观察对象“重新启动”。 I'm still learning observables;我还在学习 observables; is this in fact the case?事实上是这样吗?

So my question is twofold:所以我的问题是双重的:

  1. Can Angular2's async pipe do something intelligent with errors? Angular2 的异步管道可以用错误做一些智能的事情吗?
  2. Do observables have some simple way to continue after an error? observables 是否有一些简单的方法可以在错误后继续?

Yes you're right regarding the catch operator and the ability to do something after errors occur...是的,您对 catch 运算符以及在发生错误后执行某些操作的能力是正确的...

I would leverage the catch operator to catch the error and do something:我会利用catch运算符来捕获错误并做一些事情:

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

and in the template:并在模板中:

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

To be able to go on the initial observable, you need to create a new one starting at the point where the error occurs:为了能够继续最初的 observable,您需要从发生错误的地方开始创建一个新的 observable:

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

and use it in the catch callback:并在catch回调中使用它:

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

These two questions could help you:这两个问题可以帮到你:

1) no, The async pipe subscribes and unsubscribes and returns the events it receives. 1) 不, async管道订阅和取消订阅并返回它接收到的事件。 You would need to handle the errors before they receive the async pipe.您需要在它们收到async管道之前处理错误。

2) You can use the catch operator and when it returns an observable then its value(s) is emitted by the .catch(err => Observable.of(-1)) instead of the error. 2)您可以使用 catch 运算符,当它返回一个可观察对象时,它的值由.catch(err => Observable.of(-1))而不是错误发出。

You could use this to emit a special "error" value and then use something like *ngIf="num === -1 to show the error value in some special way.您可以使用它来发出一个特殊的“错误”值,然后使用诸如*ngIf="num === -1类的东西以某种特殊方式显示错误值。

You can find more information on this https://blog.thoughtram.io/angular/2017/02/27/three-things-you-didnt-know-about-the-async-pipe.html您可以在此https://blog.thoughtram.io/angular/2017/02/27/three-things-you-didnt-know-about-the-async-pipe.html上找到更多信息

@Thierry Templier answer was correct but is now a bit outdated. @Thierry Templier 的答案是正确的,但现在有点过时了。 Here's how to do it with the latest RXJS.以下是如何使用最新的 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
)

then HTML as normal:然后 HTML 正常:

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

Note $ at end of Observable name is Angular recommended way to denote an Observable.注意 Observable 名称末尾的 $ 是 Angular 推荐的表示 Observable 的方式。

I was facing a similar issue and came up with another approach.我遇到了类似的问题,并想出了另一种方法。 I do not know if it's a good way of doing it, but it works.我不知道这是否是一个好方法,但它有效。

template where you want to show the result of your observable:要在其中显示可观察结果的模板:

<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>

The loading component: 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 {
  }

}

loader template:装载机模板:

<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>

I am not perfectly sure if its a good approach to subscribe to the observable twice, as subscribing is done in the original component that needs the data and in the loader, but otherwise this seems to work properly.我不确定它是否是订阅两次可观​​察对象的好方法,因为订阅是在需要数据的原始组件和加载器中完成的,但除此之外,这似乎可以正常工作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM