Ionic 4: "Loading Controller"在present()之前调用dismiss()会使得spinner无法消失

38

我使用了"Ionic Loading Controller"来展示一个旋转图标,直到数据被检索出来然后调用"dismiss()"方法来关闭它。 它运行良好,但有时候当应用已经有了数据时,"dismiss()"方法会在"create()"和"present()"方法完成之前被调用,这将导致旋转图标无法关闭...

我尝试在"loadingController.present().then()"中调用数据,但这会使数据变慢...

这是一个bug吗? 如何解决这个问题?

我的代码示例:

customer: any;

constructor(public loadingController: LoadingController, private customerService: CustomerService)

ngOnInit() {
  this.presentLoading().then(a => consloe.log('presented'));
  this.customerService.getCustomer('1')
  .subscribe(customer => {
    this.customer = customer;
    this.loadingController.dismiss().then(a => console.log('dismissed'));
  }
}

async presentLoading() {
  const loading = await this.loadingController.create({
    message: 'wait. . .',
    duration: 5000
  });
  return await loading.present();
}

你解决了吗?我也遇到了同样的问题。 - Onfire
我会给出我的解决方案作为答案,但我不确定这是否是最好的方法。 - rami bin tahin
23个回答

125

这是我解决问题的方法...

我使用了一个名为 "isLoading" 的布尔变量,当调用 dismiss() 时将其更改为 false。在 present() 完成后,如果 "isLoading" === false(表示已经调用了 dismiss()),则立即关闭。

另外,我在服务中编写了代码,这样我就不必在每个页面中再次编写它。

loading.service.ts

import { Injectable } from '@angular/core';
import { LoadingController } from '@ionic/angular';

@Injectable({
  providedIn: 'root'
})
export class LoadingService {

  isLoading = false;

  constructor(public loadingController: LoadingController) { }

  async present() {
    this.isLoading = true;
    return await this.loadingController.create({
      // duration: 5000,
    }).then(a => {
      a.present().then(() => {
        console.log('presented');
        if (!this.isLoading) {
          a.dismiss().then(() => console.log('abort presenting'));
        }
      });
    });
  }

  async dismiss() {
    this.isLoading = false;
    return await this.loadingController.dismiss().then(() => console.log('dismissed'));
  }
}

那么只需从页面调用 present() 和 dismiss()。

所涉及的示例:

customer: any;

constructor(public loading: LoadingService, private customerService: CustomerService)

ngOnInit() {
  this.loading.present();
  this.customerService.getCustomer('1')
  .subscribe(
    customer => {
      this.customer = customer;
      this.loading.dismiss();
    },
    error => {
      console.log(error);
      this.loading.dismiss();
    }
  );

1
太好了,但请注意,只有在getCustomer成功时才会调用dismiss()。如果出现错误,您需要再次调用它。例如:..getCustomer.subscribe(customer => {... dismiss(); }, error => {... dismiss(); }). 可以使用alertController显示错误消息。 - rami bin tahin
1
目前还没有找到更好的东西。 - Jeremy Belolo
之前的“内容”去哪了?我想在内容上放置自己的旋转器,但现在没有内容了。 - Francisco Souza
建议 @ramibintahin,如果我们同时多次使用这个加载器,我们需要传递一个唯一标识符以避免冲突。 - Ravi Mehta
6
我喜欢这个解决方案,它非常好用。我偶尔会遇到这个控制台错误:“overlay does not exist”。我解决了这个问题,通过检查getTop()是否返回一些值来执行loadingController.getTop().then(value => value ? loadingController.dismiss() : null);。 - A. D'Alfonso
显示剩余11条评论

9

以下是我在自己的项目中解决同样问题的方法。我在HTTP拦截器中使用这个服务,为我的应用程序内的所有REST API调用显示加载程序。

loading.service.ts

import {Injectable} from '@angular/core';
import {LoadingController} from '@ionic/angular';

@Injectable({
  providedIn: 'root'
})
export class LoadingService {
  constructor(public loadingController: LoadingController) {
  }

  async present(options: object) {
    // Dismiss all pending loaders before creating the new one
    await this.dismiss();

    await this.loadingController
      .create(options)
      .then(res => {
        res.present();
      });
  }

  /**
   * Dismiss all the pending loaders, if any
   */
  async dismiss() {
    while (await this.loadingController.getTop() !== undefined) {
      await this.loadingController.dismiss();
    }
  }
}

在原问题的上下文中,它可以用于以下方式:
...
import {LoadingService} from '/path/to/loading.service';
...
customer: any;

constructor(public loadingService: LoadingService, private customerService: CustomerService)

ngOnInit() {
  this.loadingService.present({
    message: 'wait. . .',
    duration: 5000
  });
  this.customerService.getCustomer('1')
  .subscribe(customer => {
    this.customer = customer;
    this.loadingService.dismiss();
  }
}

这个拦截器内的代码是否适用于页面中的多个HTTP请求,例如4-5个? - pratik jaiswal
我认为应该可以。在我的应用程序主页面上,我有3个同时运行的请求(一个用于项目的主列表,另外两个用于下拉菜单)。 - Andrii S.

7

如果使用 Ionic 4,请查看以下解决方案

参考 链接

  import { Component } from '@angular/core';
  import { LoadingController } from '@ionic/angular';

  @Component({
    selector: 'app-home',
    templateUrl: 'home.page.html',
    styleUrls: ['home.page.scss'],
  })
  export class HomePage {

    loaderToShow: any;

    constructor(
      public loadingController: LoadingController
    ) {
    }


    showAutoHideLoader() {
      this.loadingController.create({
        message: 'This Loader Will Auto Hide in 2 Seconds',
        duration: 20000
      }).then((res) => {
        res.present();

        res.onDidDismiss().then((dis) => {
          console.log('Loading dismissed! after 2 Seconds');
        });
      });
    }

    showLoader() {
      this.loaderToShow = this.loadingController.create({
        message: 'This Loader will Not AutoHide'
      }).then((res) => {
        res.present();

        res.onDidDismiss().then((dis) => {
          console.log('Loading dismissed! after 2 Seconds');
        });
      });
      this.hideLoader();
    }

    hideLoader() {
      setTimeout(() => {
        this.loadingController.dismiss();
      }, 4000);
    }

  }

1
谢谢,官方文档在这里从未设置无限加载器的示例 https://ionicframework.com/docs/api/loading。感谢您提出。 - Benyamin Limanto
1
你声明了loaderToShow,但是你什么时候使用它? - Robert Smith

4

这样做也解决了并发API调用加载器不显示的问题。您也可以调用这些函数来形成拦截器。没有固定的持续时间,因为如果任何调用需要更多的时间,加载器将继续。但是,如果有人提供持续时间,那么如果该API在此时间内未停止,则加载器将停止。

import { Injectable } from '@angular/core';
import { LoadingController } from '@ionic/angular';

@Injectable({
  providedIn: 'root'
})
export class LoadingService {
  isLoading = false;
  loaderCounter = 0;
  loading: HTMLIonLoadingElement;

  constructor(public loadingController: LoadingController) {}

  async present() {
    this.loaderCounter = this.loaderCounter + 1;

    if (this.loaderCounter === 1) {
      this.isLoading = true;
      const { loadingDuration, loadingMessage = loadingDefaultOptions.loadingMessage, loadingCssClass } = options;
      this.loading = await this.loadingController.create({
        duration: loadingDuration,
        message: loadingMessage,
        cssClass: loadingCssClass
      });
      await this.loading.present();
    }
  }

  async dismiss() {
    this.loaderCounter = this.loaderCounter - 1;
    if (this.loaderCounter === 0) {
        this.isLoading = false;
        await this.loading.dismiss();
    }
  }
}

1
当你在拦截器中使用它时,它非常好用,而且在处理多个HTTP请求时也非常棒。即使我发送了两个请求,只有一个加载提示框出现。做得很好。 - RRGT19

3

虽然现有方案可以运行...... 但我认为最好只显示一个加载中。我的解决方案会关闭之前的加载中(如果存在),并创建新的加载中。在我的特定用例中,我通常只想一次显示一个加载中,所以这个解决方案适合我。

现有方案可能存在未使用的加载中的问题,但它是一个良好的起点。

因此,这是我提出的可注入服务(如果需要,您可以添加更多ionic设置来增强它。我不需要它们,因此在当前函数中没有添加更多,但可以根据需要添加):

import {Injectable} from '@angular/core';
import {LoadingController} from '@ionic/angular';

@Injectable({
    providedIn: 'root'
})
export class LoadingService {

    currentLoading = null;

    constructor(public loadingController: LoadingController) {
    }

    async present(message: string = null, duration: number = null) {

        // Dismiss previously created loading
        if (this.currentLoading != null) {
            this.currentLoading.dismiss();
        }

        this.currentLoading = await this.loadingController.create({
            duration: duration,
            message: message
        });

        return await this.currentLoading.present();
    }

    async dismiss() {
        if (this.currentLoading != null) {

            await this.loadingController.dismiss();
            this.currentLoading = null;
        }
        return;
    }

}

3

对我来说,使用列表的效果更好

import { Injectable } from '@angular/core';
import { LoadingController } from '@ionic/angular';

@Injectable({providedIn: 'root'})
export class LoadingService {
    private loaders = new Array<HTMLIonLoadingElement>();
    constructor(public loadingController: LoadingController) { }

    present(options?: object) {
        if (this.loaders.length === 0) {
            this.loadingController.create(options).then(loader => {
                this.loaders.push(loader);
                loader.present();
            });
        }
    }

    async dismiss() {
        if (this.loaders && this.loaders.length > 0) {
            this.loaders.forEach(async loader => {
                await loader.dismiss()
                    .then(() => {
                        loader = null;
                    })
                    .catch(e => console.log(e))
                    .finally(() => this.loaders = new Array<HTMLIonLoadingElement>());
            });
        }
    }
}

2
简单的方法是添加setTimeOut函数:

简单的方法是添加setTimeOut函数:

setTimeout(() => {
      this.loading.dismiss();
    }, 2000);

1

我知道这个问题是一年前提出的。我也遇到了同样的问题。我只想发布我的解决方案。我希望即将访问的人可以得到帮助。

async dismissLoading() {
    console.log("dismiss");
    this.isLoading = false;
  }
 private async presentLoading(msg) {
    console.log("loading");
    const loading = await this.loadingCtrl.create({
      message: msg,
    });
    await loading.present();
    var timer = setInterval(() => {
      if (!this.isLoading) {
        loading.dismiss();
        clearInterval(timer);
        console.log("set dismiss");
      }
    }, 1000);
  }
  async loadingmsg() {
    this.isLoading = true;
    await this.presentLoading("Please wait while...");
  }

这个解决方案对我有效。如果我有错误,请纠正我。


1

我对这个问题的解决方案是设置一个状态变量。代码如下:

@Injectable()
    export class LoaderSerive {
    private status: 'pending' | 'dismissed' | 'present' = 'dismissed';

    constructor(public loadingCtrl: LoadingController) {}

    public show() {
        if (this.status === 'present') {
            this.hide();
        }

        this.status = 'pending';

        this.loadingCtrl.create({
            id: 'spoon-indicator-1',
            spinner: null,
            message: `
                <div>
                    <div class="loading-indicator--position">
                        <div class="loading-indicator">
                            <div class="bowl">
                                <div class="spoon"></div>
                                <div class="bowl-content"></div>
                            </div>
                        </div>
                    </div>
                </div>`,
            duration: 6000
        })
        .then((loader) => loader.present())
        .then(() => {
            if (this.status === 'pending') {
                this.status = 'present';
            } else {
                this.hide();
            }
        });
    }

    public hide() {
        this.loadingCtrl
            .dismiss(null, undefined, 'spoon-indicator-1')
            .catch((err) => Utilities.log('Loader error!', err))
            .then(() => this.status = 'dismissed');
    }
}

1

我正在使用类似的解决方案,但依赖于加载覆盖层的Ids,并让Ionic Loading Controller管理哪个覆盖层应该在顶部。

LoadingService

import { Injectable } from '@angular/core';
import { LoadingController } from '@ionic/angular';

@Injectable({
  providedIn: 'root'
})
export class LoadingService {

  constructor(public loadingController: LoadingController) { }

  async present(loadingId: string, loadingMessage: string) {
    const loading = await this.loadingController.create({
      id: loadingId,
      message: loadingMessage
    });
    return await loading.present();
  }

  async dismiss(loadingId: string) {
    return await this.loadingController.dismiss(null, null, loadingId);
  }
}

使用LoadingService的组件/服务

import { LoadingService } from '../loading/loading.service';

@Injectable({
  providedIn: 'root'
})
export class MessagesService {

  ...

  constructor(
    protected http: HttpClient,
    protected loading: LoadingService
  ) { }

  ...

  protected async loadMessagesOverview() {
    const operationUrl = '/v1/messages/overview';

    await this.loading.present('messagesService.loadMessagesOverview', 'Loading messages...');

    this.http.get(environment.apiUrl + operationUrl)
      .subscribe((data: Result) => {
        ...
        this.loading.dismiss('messagesService.loadMessagesOverview');
      }, error => {
        ...
        this.loading.dismiss('messagesService.loadMessagesOverview');
        console.log('Error getting messages', error);
      });
  }

}


我认为这是在拦截器中使用加载器的最安全方式。根据需要显示和隐藏加载器。非常棒。 - Luckyy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接