我正在使用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);
}
}
答案 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技巧使微调器显示,它几乎不会闪烁。)虽然展开扩展面板时需要更长的时间,但它仍然只有几分之一秒,因此用户的整体体验更好,在我看来。