【问题标题】:Show activity Indicator while loading a lazy loaded Module in Angular 2在Angular 2中加载延迟加载的模块时显示活动指示器
【发布时间】:2017-08-16 07:23:21
【问题描述】:

我的场景如下。我有一个菜单,有多个选项。每个菜单应根据用户权限显示(已解决),大多数菜单项都封装为模块,并且大多数模块都是延迟加载的,因此当用户第一次单击菜单项时,它会加载(到这里所有效果很好),现在我的要求是,为了提供更好的用户体验,我需要在加载延迟加载模块时在用户单击菜单项后显示活动指示器。

到目前为止,我尝试使用 Angular Router 中的 canActive、canLoad、canActivateChild 接口,但没有成功。

有什么想法吗?

【问题讨论】:

    标签: angular lazy-loading angular2-routing


    【解决方案1】:

    你可以只使用 CSS !

    <routler-outlet></routler-outlet>
    <div class='.loader>
      Just, wait a sec ! I'm loading
    </div>
    

    在您的模板中

    router-outlet + .loader {
      opacity : 1;
    }
    
    .loader {
      opacity : 0;
    }
    

    然后你可以创建fancy spinners with HTML/CSS

    【讨论】:

    • 您能否详细说明您的答案。我的意思是,我有一个根模块和一个功能模块来管理所有菜单,然后当我单击一个菜单项时,它会加载一个延迟加载的模块,所以在加载时我想显示活动指示器,但是一旦加载惰性模块,隐藏活动指示器。我看不到如何应用它。
    • 我猜你有一个路由器插座来加载你的组件。当组件未加载时,&lt;router-outlet&gt; 将为空。这将触发container:empty + .loader 选择器并显示您的加载程序。加载数据时。 将不再为空,然后container:empty + .loaderselector 将不再处于活动状态,您的加载程序将返回到 opacity 为 0
    • 实际上是的,我有一个路由器插座。事实上,我有一个应用程序组件,其根出口有一个路由器插座,还有另一个模块负责管理所有菜单,因此它还有另一个路由器插座。我刚刚尝试了将类容器放在我的“子路由器插座”中的答案,但即使在模块加载后它也会显示。
    • @vimac 哎呀,我的错,我知道出了什么问题。我假设组件加载 IN 路由器插座,但实际上它们加载 AFTER 插座。我编辑了我的代码。现在应该没问题了。
    • 我有一个带有路由器插座和 IndexComponent 的应用程序组件,用于显示通过 oauth 进行身份验证的按钮,一旦身份验证完成,用户将再次重定向到 IndexComponet,但这次已记录是真的,所以我检测到并将用户重定向到主页,该主页是另一个功能模块,带有 MenuBarComponent 来管理所有菜单,它的模板如下,样式也在样式标签内。
      等一下!我正在加载
    【解决方案2】:

    你可以这样做

    1. 在 app.component.html 中
    <div class="main-loader" *ngIf="loading">
      <div class="cssload-container" >
          <div class="cssload-whirlpool"></div>
      </div>
    </div>
    
    1. 在 app.component.ts 中

      import { Router, NavigationStart, NavigationEnd } from '@angular/router';
      
      
      loading:boolean = false;
      constructor(private router:Router) { 
        router.events.subscribe(event => {
          if(event instanceof NavigationStart) {
            this.loading = true;
            console.log("event started")
          }else if(event instanceof NavigationEnd) {
            this.loading = false;
            console.log("event end")
          }
          // NavigationEnd
          // NavigationCancel
          // NavigationError
          // RoutesRecognized
        });
      
      }
      
    2. 在css中任意加载动画

    希望这对您有用。谢谢

    【讨论】:

    • 您无需订阅两次。就像 Daniel Crisp 的回答一样,你可以做一次。它可以工作,而且最好订阅一次。
    • 如果您希望在显示加载状态之前有一个延迟(即仅在慢速加载时显示加载),您可以使用过滤器和 switchMap 仅在延迟后设置 this.loading = true。灵感来自stackoverflow.com/a/51998101/3370010
    【解决方案3】:

    你可以监听两个路由器事件:

    • RouteConfigLoadStart
    • RouteConfigLoadEnd

    在加载延迟加载的模块时它们会触发。与NavigationStart 等标准路由器事件相比,使用这些事件的优势在于它们不会在每次路由更改时触发。

    在您的根 AppComponent 中收听它们以显示/隐藏您的微调器。

    app.component.ts

    import { Router, RouteConfigLoadStart, RouteConfigLoadEnd } from '@angular/router';
    
    ...
    
    export class AppComponent implements OnInit {
    
        loadingRouteConfig: boolean;
    
        constructor (private router: Router) {}
    
        ngOnInit () {
            this.router.events.subscribe(event => {
                if (event instanceof RouteConfigLoadStart) {
                    this.loadingRouteConfig = true;
                } else if (event instanceof RouteConfigLoadEnd) {
                    this.loadingRouteConfig = false;
                }
            });
        }
    }
    

    app.component.html

    这里只是一个简单的字符串,但您可以使用微调器组件。

    <router-outlet></router-outlet>
    
    <ng-container *ngIf="loadingRouteConfig">Loading route config...</ng-container>
    

    我在 Angular v4.2.3 中使用这种方法

    【讨论】:

    • 它的工作原理是它显示文本,但如果你使用微调器或带有 CSS 动画的东西,一旦模块开始加载,动画就会停止(虽然显示动画组件本身,但它会冻结在一个框架)。请注意,这不是微调器本身的问题 - 只有在加载模块时。
    • 在这种情况下有没有办法使用 CSS 动画?
    • 如果您使用的是 PreloadAllModules 策略,那么这也会显示所有模块在后台加载。在这种情况下,最好同时捕获 NavigationStart 和 NavigationEnd,并在这些事件之外忽略 RouteConfigLoadStart 和 RouteConfigLoadEnd。
    • 可以添加else if (event instanceof NavigationCancel) { this.isLoading = false },用于在完成延迟加载之前移动到另一个路由/模块的情况。
    【解决方案4】:

    可以在加载第一个时单击指向其他延迟加载路由的链接。这就是为什么我们需要计算正在加载的路由:

    app.component.ts

    ```

    export class AppComponent { 
    
      currentlyLoadingCount = this._router.events.scan((c, e) => this._countLoads(c, e), 0);
    
      constructor(private _router: Router) { }
    
      private _countLoads(counter: number, event: any): number {
        if (event instanceof RouteConfigLoadStart) return counter + 1;
        if (event instanceof RouteConfigLoadEnd) return counter - 1;
        return counter;
      }
    

    ```

    app.component.html <ng-container *ngIf="currentlyLoadingCount | async">Loading route config...</ng-container>

    【讨论】:

    • 我已经有一段时间了,正在做我的项目。我刚刚解决了一个共享服务,该服务在启动一些 http 请求时触发,它部分解决了您的问题,但我尽快尝试一下。谢谢。
    【解决方案5】:

    对于那些需要与 IE11 兼容(和处理)的人,接受的答案不起作用,因为从一个模块切换到另一个模块时 IE11 不会呈现加载器,所以我做了一个(不是很优雅但有效)解决方法:

    在我的 menuItem 切换点击事件中:

      public navigate(url: string) {
        this.configurationService.loading = true; //service variable linked with html loader
    
        //let's give time to tortoise IE11 to render loader before navigation...
        let obj = this;
        setTimeout(function() 
        {
          obj.router.navigateByUrl(url);
        }, 1);
      }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-02-22
      • 1970-01-01
      • 1970-01-01
      • 2019-01-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多