无法找到正确的Angular生命周期挂钩

时间:2018-02-23 18:32:14

标签: angular angular5

我正在使用Angular 5.2.1。我有一个主页面,它有两个组成部分:

<proposal-expansion [proposallist]="proposalList" ... ></proposal-expansion>
<proposal-dashboard ... ></proposal-dashboard>

来自服务器的所有数据都是在主页面呈现时加载的。最初扩展为空(proposalList为[])。仪表板显示5个提案的缩写版本。请注意,即使显示屏显示提案的最小数据,所有数据仍然存在。然后,我在仪表板组件上有一个按钮,单击该按钮会隐藏仪表板并发出包含所有提案数据的事件。

expandRecent(): void {
    console.log("expand clicked...");
    this.spinnerService.spinner(true);
    this.showRecent.emit(this.propList);
}

在主页面组件中,这是发出该事件时调用的函数:

showRecent(event) {
    this.proposalList = event;
    this.showDashboard = false;
    this.spinnerService.spinner(false);
}

那时,扩展组件从proposalList获取数据并尝试渲染。在扩展组件中呈现了许多子组件,并且在实际显示扩展组件之前需要4-5秒。请记住,当时没有与服务器的交互 - 4-5秒都是Angular。

鉴于显示扩展组件需要很长时间,我希望有一个微调器,以便用户知道发生了什么。我有一个微调器服务,通过传入true来显示微调器或false来隐藏它。一旦点击仪表板上的按钮(true),我就会将expandRecent()传递给服务。这是我遇到问题的地方。

即使打开微调器是第一件事(除了console.log()之外,它实际上并没有先发生。最初,我认为微调器正在立即打开和关闭。但是,我发现如果我从未关闭微调器,它仍然会等到扩展组件在微调器进来之前加载。此外,我发现如果我在单击按钮之前手动打开微调器,那么微调器在根据需要加载扩展之前,它不会关闭。所以我似乎需要弄清楚何时何地转动旋转器。

基于Angular文档,ngOnChanges

  

当Angular(重新)设置数据绑定输入属性时响应。

基于此,我在扩展组件中添加了这个,认为只要proposalList更新,就会调用它:

ngOnChanges() {
    this.spinnerService.spinner(true);
}

我还删除了在expandRecent()方法中将微调器设置为true的行。但是,当我完成所有这些操作后,在加载扩展组件之后仍然没有旋转器,更糟糕的是 - 在ngOnChanges打开它之前将其关闭。所以它没有显示4-5秒的旋转器,然后它就会无限期地运行。

我应该使用哪些合适的生命周期钩子,以及我应该使用哪些组件,以使微调器按预期运行?

谢谢!

最新的尝试更新:

我现在已经开始使用路由器和服务来完成此任务,但仍然遇到同样的问题。

expandRecent(): void {
    console.log("expand recent");
    this.spinnerService.spinner(true);
    this.changeDetectorRef.detectChanges();
    // essentially this just passes the data from one list to another in the service
    this.proposalService.proposalShortList$.take(1).subscribe(
        list => this.proposalService.setProposalList(list)
    );
    // this navigates to the ProposalExpansion component
    this.router.navigate(['../display'], { relativeTo: this.route });
}

然后我尝试将其添加到ProposalExpansion组件中:

ngAfterViewChecked() {
    this.spinnerService.spinner(false);
}

现在,当expandRecent()运行时,这就是我在控制台中看到的(数字是每个组件运行ngOnInit时的时间戳。):

expand recent
Should change spinner to true
Set up proposal list in expansion component
1520359500144
Initiate proposal for 2924
1520359500342
Inititiating revision for -
1520359500417
Initiating item 1
1520359500537
Initiate proposal for 2923
1520359500718
...
Initiating item 1
1520359502082
Should change spinner to false (8 x)
Should change spinner to false
Should change spinner to false (8 x)

基于控制台,人们会认为它正在运行。然而,旋转器从未如实际可见。但是,如果我将router.navigate置于setTimeout函数中,则微调器的工作方式与预期完全相同。 (当时间设置为100毫秒或更长时,setTimeout工作。它在10毫秒或更短时间内无效。)虽然这是我可以使用的解决方法,但我真的很想了解发生了什么,并且它我似乎不应该使用那种解决办法。

我很想在Plunker中使用它,但是在添加所有代码之前我甚至无法使用Angular Material,所以这将是一个重要的时间投资,我宁愿避免。

这是微调代码:

spinner.component.html

<div *ngIf="(spinner$ | async)">
    <i class="fas fa-spinner fa-pulse fa-2x fa-fw"></i>
</div>

spinner.component.ts

import { Component, OnInit } from '@angular/core';
import {Observable} from 'rxjs/Observable';

import { SpinnerService } from './spinner.service';

@Component({
  selector: 'spinner',
  templateUrl: './spinner.component.html',
  styleUrls: ['./spinner.component.scss']
})
export class SpinnerComponent implements OnInit {
    spinner$: Observable<boolean>;

    constructor(private spinnerService: SpinnerService) { }

    ngOnInit() {
        this.spinner$ = this.spinnerService.spinner$;
    }

    // to show the spinner
    showSpinner() {
        this.spinnerService.spinner(true);
    }

    // to hide the spinner
    hideSpinner() {
        this.spinnerService.spinner(false);
    }
}

spinner.service.ts

import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';

@Injectable()
export class SpinnerService {
    private spinnerSubject = new BehaviorSubject<boolean>(false);
    spinner$: Observable<boolean> = this.spinnerSubject.asObservable();

    constructor() {}

    spinner(spin:boolean) {
        console.log("Should change spinner to "+spin);
        this.spinnerSubject.next(spin);
    }
}

3 个答案:

答案 0 :(得分:2)

使用路由时,您可以按如下方式打开/关闭微调器:

应用组件

import { Component } from '@angular/core';
import { Router, Event, NavigationStart, NavigationEnd, NavigationError, NavigationCancel } from '@angular/router';

@Component({
    selector: 'mh-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css']
})
export class AppComponent {
    loading: boolean = true;

    constructor(private router: Router) {
        router.events.subscribe((routerEvent: Event) => {
            this.checkRouterEvent(routerEvent);
        });
    }

    checkRouterEvent(routerEvent: Event): void {
        if (routerEvent instanceof NavigationStart) {
            this.loading = true;
        }

        if (routerEvent instanceof NavigationEnd ||
            routerEvent instanceof NavigationCancel ||
            routerEvent instanceof NavigationError) {
            this.loading = false;
        }
    }
}

应用模板

<span class="glyphicon glyphicon-refresh glyphicon-spin spinner" *ngIf="loading"></span>
<router-outlet></router-outlet>

您可以为微调器定义任何所需的图像/图形。只需根据加载标志打开/关闭它。

答案 1 :(得分:1)

虽然这并没有解释为什么为什么微调器没有像我预期的那样工作,但它确实能够让它按照预期工作。基本上,我必须将导航放在setTimeout函数中:

expandRecent(): void {
    console.log("expand recent");
    this.spinnerService.spinner(true);
    this.proposalService.proposalShortList$.take(1).subscribe(
        list => this.proposalService.setProposalList(list)
    );
    let myrouter = this.router;
    let myroute = this.route;
    setTimeout(function() {
        myrouter.navigate(['../display'], { relativeTo: myroute });
    },100);
}

答案 2 :(得分:0)

虽然这也没有回答这个问题,但它确实为用户交互的主要问题提供了潜在的解决方案。根据材料文档:

  

延迟渲染

     

默认情况下,即使在初始化时,也会初始化扩展面板内容   小组关闭。相反,将初始化推迟到面板   是开放的,内容应作为ng-template提供:

<mat-expansion-panel>
  <mat-expansion-panel-header>
    This is the expansion title
  </mat-expansion-panel-header>

  <ng-template matExpansionPanelContent>
    Some deferred content
  </ng-template>
</mat-expansion-panel>

因此,不会尝试一次渲染所有扩展面板,而只会初始渲染标头。这大大减少了加载时间,从而减少了对旋转器的需求。 (如果使用setTimeout技巧使微调器显示,它几乎不会闪烁。)虽然展开扩展面板时需要更长的时间,但它仍然只有几分之一秒,因此用户的整体体验更好,在我看来。