将LoadingController包装在可观察对象周围

时间:2019-03-29 08:46:45

标签: angular ionic-framework ionic4

我使用Angular7 / Ionic4重建的旧应用程序有问题。

在尝试调用远程服务时,我试图使用LoadingController来显示指示器,但是我遇到了一些麻烦。

我目前的情况如下。我有一个带有简单加载方法的PageComponent,类似:

export class PageComponent {

  constructor(private itemsService: ItemsService) { }

  fillData() {
    this.itemsService.getItems().subscribe(items => {
      this.items = items;
    });
  }

}

ItemsService也很简单:

export class ItemsService {

  constructor(private http: HttpClient) { }

  getItems() {
    let url = 'http://example.org/items/endpoint';

    return this.http.get(url).pipe(
      map(this.buildItems.bind(this)
    );
  }

  private buildItems(items: any[]) {
    // convert remote data format to internal one
  }
}

现在,我想通过使呼叫者和被呼叫者基本上都不知道实际发生的事情,在两者之间加上LoadingController

所以我的想法是创建一个从页面类调用的中间服务类。为了使其具有足够的灵活性以用于任何类型的服务(我拥有超过ItemsService的服务),我尝试将与Observable类似的ItemsService传递给中间类,例如这个:

(在PageComponent中)

fillData() {
  this.loadingService.loadData(this.itemsService.getItems()).subscribe(items => {
    this.items = items;
  });
}

我定义了这样的新服务:

export class LoadingService {

  loadData(observable: Observable<any>): Observable<any> {
    return observable.pipe(
      finalize(() => {
        console.log('after data received');
      })
    );
  }

}

此代码似乎有效。当我在fillData中调用PageComponent时,我从ItemsService中得到了可观察对象,并向其中添加了一个finalize()运算符,最后(在fillData()中)我订阅了可以观察到,调用了真正的远程请求。

这样,如果我有许多形状像ItemsService的服务,我就可以做到

this.loadingService.loadData(this.service1.getData()).subscribe( /* ... */ );
this.loadingService.loadData(this.service2.getData()).subscribe( /* ... */ );
this.loadingService.loadData(this.service3.getData()).subscribe( /* ... */ );
/* ... and so on */

但是,一旦我在LoadingController中添加LoadingService,事情就会变得很奇怪。这就是我更改服务的方式:

export class LoadingService {
  private loader = null;

  constructor(private loadingController: LoadingController) { }

  loadData(observable: Observable<any>): Observable<any> {
    this.openLoader();
    return observable.pipe(
      finalize(() => {
        this.closeLoader();
      })
    );
  }

  private async openLoader() {
    if (! this.loader) {
        this.loader = await.loadingController.create();
    }
    await this.loader.present();
  }

  private async closeLoader() {
    if (this.loader) {
      await this.loader.dismiss();
      this.loader = null;
    }
  }
}

现在,我得到的是加载控制器实际上显示了指示器,并且通常在远程呼叫结束后不会将其关闭。但是,有时我会得到正确的行为:指示器显示,呼叫开始,呼叫结束,指示器消失。

我对JS中基于事件的编程不是很自信,因此这可能是一个非常愚蠢的问题,但是我需要一些帮助来解决此问题。

非常感谢

马可

1 个答案:

答案 0 :(得分:0)

我已经在几个按预期运行的应用程序中使用了以下加载程序服务。

加载程序服务

private loader;
constructor(private loaderCtrl: LoadingController) {
  this.loader = null;
}

async show(message?: string) {
  if (this.loader !== null) {
    this.loader.dismiss();
    this.loader = null;
  }

  this.loader = await this.loaderCtrl.create({
    spinner: 'crescent',
    message: message ? message : 'Please wait...',
  });
  return this.loader.present();
}

hide() {
  if (this.loader !== null) {
    this.loader.dismiss();
    this.loader = null;
  }
}

这是调用API的脚本-

组件ts

try {
  await this.loaderSvc.show();
  this.service1.getData().subscribe((res) => {
  }, err => {
    throw "Error calling API";
  });
} catch (error) {
  console.log(error);
} finally {
  this.loaderSvc.hide();
}