How to keep RxJs observable from completing on error?

≯℡__Kan透↙ 提交于 2019-12-13 02:58:02

问题


I have an angular reactive form that makes a simple GET request. If for example, I should get some sort of HTTP error. The observable todo$ has now completed I can no longer change my search options and click to retry searching. I've made a stackblitz demo, in the demo I've added my subscription inside an initializer and if there is an error on the stream I catch it. After I catch it, I call the initializer again. This works but seems like the wrong way to handle errors.

I've set up the form so that I can cancel previous HTTP requests.

export class AppComponent implements OnDestroy {
  todos;
  loading = false;
  useFakeURL = false;
  todoSub$: Subscription;
  todo$: BehaviorSubject < string > = new BehaviorSubject < string > ('');

  @ViewChild('searchInput') searchInput: ElementRef;

  constructor(private provider: ExampleService) {
    this.init();
  }

  ngOnDestroy() {
    this.todoSub$.unsubscribe();
  }

  search() {
    const value = this.searchInput.nativeElement.value;
    this.todo$.next(value);
    this.useFakeURL = !this.useFakeURL;
  }

  private init(): void {
    this.todoSub$ = this.todo$.pipe(
        filter(val => !!val),
        tap(() => {
          this.loading = true;
        }),
        switchMap(() => this.provider.query(this.todo$.getValue(), this.useFakeURL)),
        catchError(error => {
          this.todo$.next('');
          this.init();
          return of([]);
        }),
      )
      .subscribe(
        todos => {
          this.loading = false;
          this.todos = todos;
        },
        err => {
          this.loading = false;
          this.todos = err;
        }
      );
  }
}

export class ExampleService {

  constructor(private http: HttpClient) {}

  query(todo, useFakeURL: boolean) {
    if (todo === 'all') {
      todo = '';
    }
    const url = useFakeURL ? 'poop' : `https://jsonplaceholder.typicode.com/todos/${todo}`;
    return this.http.get(url);
  }
}
<div class="container">
  <input #searchInput type="text" placeholder="Enter a number or enter 'all' to get all todos">
  <button (click)="search()">Get Todos</button>
</div>
<ul *ngIf="!loading && todos && todos.length">
  <li *ngFor="let todo of todos">
    <pre>
      {{todo | json}}
    </pre>
  </li>
</ul>
<pre *ngIf="!loading && todos && !todos.length">
  {{todos | json}}
</pre>
<div *ngIf="loading" class="loader">... LOADING</div>

回答1:


Angular's Http requests are immutable. Observable returned from client execute actual call when you subscribe to it. As many times you subscribe as many requests will be made. That is so called "Cold Observable"

If you need to change request, you need a brand new observable from HttpClient.




回答2:


As mentioned by @kos I was catching the error in the wrong place. Since I switched to a new observable I should be catching the error inside switchMap. UPDATED-DEMO

search(term: Observable < string > ) {
  return term.pipe(
    filter(val => !!val),
    distinctUntilChanged(),
    debounceTime(500),
    // attached catchError to the inner observable here.
    switchMap(term => this.searchTodo(term).pipe(catchError(() => {
      return of({
        error: 'no todo found'
      });
    }))),
  );
}


来源:https://stackoverflow.com/questions/55386182/how-to-keep-rxjs-observable-from-completing-on-error

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!