尝试使用 Ionic 3 中的 switchMap 取消现有的 http 请求

Trying to cancel existing http request with new one using switchMap in Ionic 3

我的目标是在 Ionic 3 项目中使用 RxJs switchMap,在服务器离线时取消任何正在进行的登录尝试,并且只使用最新的请求。否则我以后可能会得到一些不需要的异步副作用。

在我的视图层中,我使用 submitStream$ 来捕获按钮点击流,然后将其作为方法参数传递给服务层。 在服务层,我通过 switchMap 创建 response$,将传递的参数 submitStream$ 与从 this.webServiceUtil.testLogin() 返回的 Observable<Response> 相结合。

我尝试实现此代码:

HTML 标记

<button #submit type="submit">Submit</button>

打字稿:视图层

import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/fromEvent';
import { ElementRef, ViewChild } from '@angular/core';
export class LoginPage {
  @ViewChild('submit') button: ElementRef
  submitStream$:any;
  ...
  ionViewDidLoad() {
    this.submitStream$ = 
    Observable.fromEvent(this.button.nativeElement, 'click')
  }
  ...
}

Typescript:服务层(submitStream$ 作为方法参数出现)

this.response$ = submitStream$.switchMap(click =>
    this.webServiceUtil.testLogin())

我也试过在 ngAfterViewInit()

中创建 this.submitStream$

当事情不起作用时,我也尝试将其附加到 Observable.fromEvent:

 .subscribe(ev => {
    console.log('LoginPage: ionViewDidLoad() submitStream$.subscribe(): ev:', ev);}
  )

如何克服 'ERROR TypeError: Invalid event target'?

看来您必须做一些时髦的事情才能使您订阅的内容在 RxJs 眼中看起来正确...

这就是我为解决错误类型错误所做的工作:无效的事件目标:

  @ViewChild('submit') button: any;

    this.submitClickStream$ = 
      Observable.fromEvent(this.button._elementRef.nativeElement  , 'click')
      .subscribe(ev => {
        console.log('ev: ', ev);},
        (err) => {console.error(err);}
      );

AuthService 中,传递 submitClickStream$ 或来自 @ViewChild 的按钮元素,并在 AuthService 中对其执行 Observable.fromEvent,我是无法通过事件的控制台日志记录获得订阅,无法触发。

所以,我引入了一个 BehaviourSubject 中介。

我可以在 LoginPage.html<button> 元素中再次使用常规 (click)='onSubmit()'

LoginPage.ts的onSubmit()方法中,我可以直接使用this.clickStream$.next('');的BehaviourSubject。

这消除了 Observable.fromEvent 和 ViewChild 的复杂性。

switchMap 终于成功了,取消了之前正在进行的请求,没有混乱的退订逻辑。

LoginPage.ts

import { BehaviorSubject }     from 'rxjs/BehaviorSubject';
import { AuthService }         from './../../providers/auth-service';
import { LoginCredentials }    from './../../model/loginCredentials';
export class LoginPage {

  private clickStream$:BehaviorSubject<any>;

  constructor(
   private authService:AuthService,
  ) {
     this.clickStream$ = new BehaviorSubject('');
  }

  onSubmit():void {
    console.log('LoginPage: onSumbit()');
    this.clickStream$.next('');
    this.authService
      .login(this.prepareCredentials(), 
             this.clickStream$
            );
  }

}

AuthService

import { BehaviorSubject }      from 'rxjs/BehaviorSubject';
import { LoginCredentials }     from './../../model/loginCredentials';
import { WebServiceUtil }       from './web-service-util';

@Injectable()
export class AuthService {

 login(
    loginCredentials: LoginCredentials, 
    clickStream$: BehaviorSubject<any>
  ) {
     this.response$ = clickStream$.switchMap(click =>  {
                      console.log('AuthService: login() : click: ', click);
                      return this.webServiceUtil.testLogin();
                                                       }
                                            );
     this.response$.subscribe(
         (response:Response) => {....},
          (err) => {console.error(err); ....}

  }
}

WebServiceUtil

@Injectable()
export class WebServiceUtil {

  testLogin(
  ): Observable<Response> {
  ...
  }

}

你们非常亲密。请使用如下所示的开关图。希望对您有所帮助!

this.submitStream$ = Rx.Observable.fromEvent(this.button._elementRef.nativeElement, 'click');

this.submitStream$
      .switchMap(click => {
        return this.dummyAPIResponse(); // this should return observalbe(i.e new observalbe)...
      })
      .subscribe((res) => {
        console.log(res);
      });


dummyAPIResponse(){
 return Rx.Observable.create(observer => {
      setTimeout(() => {
        observer.next([1, 2, 3, 4]);
      }, 10000);
    });
}