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