Worker.onmessage 后的 angular2 变化检测无法按预期工作
angular2 change detection after Worker.onmessage does not work as expect
我在我的angular2应用中遇到一个很有意思的,我这里简化一下情况。
这是 AppComponnet
export class AppComponent {
tabs: any = [];
viewModes = [
{ label: "列表查看"},
{ label: "树状图" },
];
constructor(private changeRef: ChangeDetectorRef) {}
addTab() {
var worker = new Worker("worker.js");
worker.postMessage("");
worker.onmessage = (event) => {
this.tabs.push({
label: 'label'
})
this.changeRef.detectChanges();
}
}
currentMode: any;
selectViewMode(mode: any) {
if (this.currentMode) {
this.currentMode.selected = false;
}
mode.selected = true;
this.currentMode = mode;
}
}
这是模板
<div *ngFor="let tab of tabs">
<ul>
<li *ngFor="let item of viewModes">
<a (click)="selectViewMode(item)" [style.background]="item.selected ? '#f69c55' : ''"> {{item.label}} </a>
</li>
</ul>
</div>
<button (click)="addTab()">Add Tab</button>
我在 Worker 完成后将一个新标签推送到标签。而且angular2不会在Worker onmessage回调后做变化检测,我必须手动触发变化检测。
但是之后,添加的tab中的list的背景在点击后不会发生变化(回调函数被执行了,所以变化检测没有起作用)。
如果我再次单击“添加选项卡”按钮等其他操作,更改检测将起作用并按预期设置列表背景。
如果我不手动触发更改检测。按下选项卡后,执行其他操作(例如再次单击按钮)以触发更改检测。现在列表会显示出来了,背景变化效果很好。
我知道关键是在Worker执行后手动调用变化检测,但我不知道具体发生了什么导致列表后台变化检测不起作用。
NgZone
修补异步 API,如 setTimeout
或 addEventListener
。当调用此类 API 的回调时,Angular 会识别它并 运行 进行更改检测。
显然 worker.onmessage
未包含在 NgZone
中,因此未调用更改检测。
调用变化检测的方法有多种
detectChanges()
仅调用当前元素的更改检测。
如果模型更改具有更广泛的影响(在您的组件之外),那么您可能需要 运行 应用程序范围的更改检测(例如,如果您从 运行 外部的回调中 rn router.navigate()
Angulars 区域(未被 NgZone
覆盖。
在这种情况下,您可以将区域内的代码 运行 设为
ngZone.run(_ => { router.navigate(); }
每次调用添加选项卡功能时,您都在调用一个新工作器。
addTab() {
var worker = new Worker("worker.js");
相反,将 worker on top 声明为此 Class 的范围绑定变量并在函数中使用它。
worker : any = new Worker("worker.js");
addTab() {
this.worker.postMessage("");
this.worker.onmessage = (event) => {
this.tabs.push({
label: 'label'
})
我在我的angular2应用中遇到一个很有意思的,我这里简化一下情况。
这是 AppComponnet
export class AppComponent {
tabs: any = [];
viewModes = [
{ label: "列表查看"},
{ label: "树状图" },
];
constructor(private changeRef: ChangeDetectorRef) {}
addTab() {
var worker = new Worker("worker.js");
worker.postMessage("");
worker.onmessage = (event) => {
this.tabs.push({
label: 'label'
})
this.changeRef.detectChanges();
}
}
currentMode: any;
selectViewMode(mode: any) {
if (this.currentMode) {
this.currentMode.selected = false;
}
mode.selected = true;
this.currentMode = mode;
}
}
这是模板
<div *ngFor="let tab of tabs">
<ul>
<li *ngFor="let item of viewModes">
<a (click)="selectViewMode(item)" [style.background]="item.selected ? '#f69c55' : ''"> {{item.label}} </a>
</li>
</ul>
</div>
<button (click)="addTab()">Add Tab</button>
我在 Worker 完成后将一个新标签推送到标签。而且angular2不会在Worker onmessage回调后做变化检测,我必须手动触发变化检测。
但是之后,添加的tab中的list的背景在点击后不会发生变化(回调函数被执行了,所以变化检测没有起作用)。
如果我再次单击“添加选项卡”按钮等其他操作,更改检测将起作用并按预期设置列表背景。
如果我不手动触发更改检测。按下选项卡后,执行其他操作(例如再次单击按钮)以触发更改检测。现在列表会显示出来了,背景变化效果很好。
我知道关键是在Worker执行后手动调用变化检测,但我不知道具体发生了什么导致列表后台变化检测不起作用。
NgZone
修补异步 API,如 setTimeout
或 addEventListener
。当调用此类 API 的回调时,Angular 会识别它并 运行 进行更改检测。
显然 worker.onmessage
未包含在 NgZone
中,因此未调用更改检测。
调用变化检测的方法有多种
detectChanges()
仅调用当前元素的更改检测。
如果模型更改具有更广泛的影响(在您的组件之外),那么您可能需要 运行 应用程序范围的更改检测(例如,如果您从 运行 外部的回调中 rn router.navigate()
Angulars 区域(未被 NgZone
覆盖。
在这种情况下,您可以将区域内的代码 运行 设为
ngZone.run(_ => { router.navigate(); }
每次调用添加选项卡功能时,您都在调用一个新工作器。
addTab() {
var worker = new Worker("worker.js");
相反,将 worker on top 声明为此 Class 的范围绑定变量并在函数中使用它。
worker : any = new Worker("worker.js");
addTab() {
this.worker.postMessage("");
this.worker.onmessage = (event) => {
this.tabs.push({
label: 'label'
})