【问题标题】:Wrap a LoadingController around observables将 LoadingController 包裹在可观察对象周围
【发布时间】:2019-03-29 20:41:28
【问题描述】:

我正在使用 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),我尝试将我从ItemsService 得到的Observable 传递给中间类,如下所示:

(在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');
      })
    );
  }

}

此代码似乎有效。当我在PageComponent 中调用fillData 时,我从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 */

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

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 中基于事件的编程不是很有信心,所以这可能是一个非常愚蠢的问题,但我需要一些帮助来解决这个问题。

非常感谢,

马可

【问题讨论】:

    标签: angular ionic-framework ionic4


    【解决方案1】:

    我在几个按预期工作的应用程序中使用了以下加载程序服务。

    加载器服务

    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();
    }
    

    【讨论】:

    • 这看起来不错,但我试图从组件中删除任何关于我的LoaderService 内部发生的事情的知识。在您的解决方案中,每个组件都必须知道将调用放在 try ... catch ... finally 中,show() 作为第一次调用,hide()finally 块中,我不太喜欢。
    猜你喜欢
    • 2014-10-03
    • 1970-01-01
    • 1970-01-01
    • 2015-05-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-12
    • 1970-01-01
    相关资源
    最近更新 更多