我在angular> = 2中有一个使用signalr的简单应用程序。我有一个问题,你理解如何确定天气将在ngzone或外部执行某些事情。如果我在这条路线上重新加载页面,下面的代码将用于所有信号器的东西在ngzone之外。如果我在另一条完全为空的路线中加载页面&从那里导航到下面的代码,它将在ngzone内执行。
import { Component, OnInit, OnDestroy, NgZone } from '@angular/core';
import { Http } from '@angular/http';
import { Subscription } from 'rxjs/Subscription';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
@Component({
selector: 'app-data-page',
templateUrl: './data-page.component.html',
styleUrls: ['./data-page.component.less']
})
export class DataPageComponent implements OnInit {
constructor(private http: Http) { }
public title = 'Bug report!';
public data = new Array<any>();
private subscription: Subscription;
private connection: SignalR.Hub.Connection;
private counter:number = 1;
private secretData = new Map<number, any>();
private dataSubject = new BehaviorSubject(this.secretData);
ngOnInit() {
console.log(`ngOnInit: ${NgZone.isInAngularZone()}`);
this.subscription = this.dataSubject.subscribe((data) => {
console.log(`subscribe: ${NgZone.isInAngularZone()}`);
this.data = new Array<any>();
data.forEach((d) => {
this.data.push(d);
});
});
let uri = 'http://localhost:8080/fake/signalr';
let c = $.hubConnection(uri, { useDefaultPath: false });
this.connection = c;
c.logging = true;
let hub = c.createHubProxy('myHub');
hub.on('tick', () => {
console.log(`tick: ${NgZone.isInAngularZone()}`);
this.generateData();
});
c.start().always(() =>
{
console.log(`Calling getData: ${NgZone.isInAngularZone()}`);
hub.invoke('getData')
.always(() => {
console.log(`getData: ${NgZone.isInAngularZone()}`);
this.generateData();
});
});
// this.http.get("http://localhost:8080/fake/api/getData").subscribe(() => {
// this.generateData();
// }, (error) => {
// this.generateData();
// });
// setTimeout(() => {
// this.generateData();
// }, 100);
}
ngOnDestroy() {
this.subscription.unsubscribe();
this.connection.stop();
}
private generateData() {
let end = this.counter+2;
for(let i = this.counter; i < end; i++) {
var d = { id: this.counter, name: "" + this.counter };
this.secretData.set(d.id, d);
this.counter++;
}
this.dataSubject.next(this.secretData);
}
}
问题的第二部分是什么是适当的解决方案(有多种变更检测机制)?我的实际代码是服务而不是组件和我想正确的解决办法是注入一个区域&amp;在某个地方使用zone.run - 因为服务可能不应该触发gui检测更新。
示例角度前端项目: http://www.filedropper.com/angular-app
编辑运行:
npm安装
服务
编辑:
如果使用setTimeout运行创建,例如2000毫秒它会在里面。所以我想解释某种类型的计时问题。
setTimeout(() =>
{
// Works everything will be inside zone
this.connect();
}, 0);
// callbacks in the connect will be outside
this.connect();
从上面重构连接的地方
private connect() {
let uri = 'http://localhost:8080/fake/signalr';
let c = $.hubConnection(uri, { useDefaultPath: false });
this.connection = c;
c.logging = true;
let hub = c.createHubProxy('myHub');
hub.on('tick', () => {
console.log(`tick: ${NgZone.isInAngularZone()}`);
this.generateData();
});
c.start().always(() =>
{
console.log(`Calling getData: ${NgZone.isInAngularZone()}`);
hub.invoke('getData')
.always(() => {
console.log(`getData: ${NgZone.isInAngularZone()}`);
this.generateData();
});
});
}
答案 0 :(得分:2)
当Angular Component使用计时器(setTimeout
)来调用函数时,NgZone
会在Angular Zone中捕获它以触发更改检测。同样,如果在Angular声明中创建一个WebSocket连接,它将在Angular Zone中捕获。
在组件方法中同步连接SignalR不会使其在Angular Zone内运行的原因是SignalR使用真实,即非Zone
d {{1}或其他一些异步通信通道,而不是在NgZone中注册的通道。
如果您熟悉AngularJS,则相当于您的Angular Component使用window.WebSocket
,而SignalR客户端本身使用的是$timeout
。
虽然您可以在Angular区域内运行SignalR消息,但似乎runnning every async message from a server push source (like WebSocket and SignalR) in the Angular Zone can easily result in performance issues。
如果您想了解有关Zone.js和NgZone的更多信息,请参阅以下资源:
run*
methods of NgZone
在Angular API docs setTimeout
test suite在Zone.js