我试图在websocket事件返回更新的数据后更新我的视图。
我注入了一个服务到我的应用程序和调用getData()方法的服务。此方法发出一个套接字。io事件到我的NodeJS服务器,该服务器反过来执行外部api调用并解析一些数据。然后,NodeJS服务器发出一个成功事件,其中包含我在服务中侦听的新数据。当成功事件返回时,我就更新视图中引用的服务的属性。
然而,无论我尝试什么,我都无法获得数据显示,一旦属性更新。
我已经搜索了几天了,我发现的所有博客文章都说这个变化应该是无缝的,或者我需要以某种方式合并zone.js,或者使用表单尝试相同的逻辑(然而我试图在没有用户交互的情况下这样做)。似乎什么都不适合我,我有点沮丧。
例如:
假设我收到一个字符串数组,我想用它创建一个无序列表。
app.ts
import {Component, View, bootstrap, NgFor} from 'angular2/angular2';
import {MyService} from 'js/services/MyService';
// Annotation section
@Component({
selector: 'my-app',
viewInjector: [MyService]
})
@View({
templateUrl: 'templates/my-app.tpl.html',
directives: [NgFor]
})
class MyComponent {
mySvc:MyService;
constructor(mySvc:MyService) {
this.mySvc = mySvc;
this.mySvc.getData();
}
}
bootstrap(MyComponent, [MyService]);
MyService.ts
let socket = io();
export class MyService {
someList:Array<string>;
constructor() {
this.initListeners();
}
getData() {
socket.emit('myevent', {value: 'someValue'});
}
initListeners() {
socket.on('success', (data) => {
self.someList = data;
});
}
}
my-app.tpl.html
<div>
<h2>My List</h2>
<ul>
<li *ng-for="#item of mySvc.myList">Item: {{item}}</li>
</ul>
</div>
有趣的是,我发现如果我在我的组件中加入一个超时,在someelist属性从成功回调中更新后,更新我在视图上设置的一些任意属性,那么两个属性值将同时正确更新。
例如:新app.ts import {Component, View, bootstrap, NgFor} from 'angular2/angular2';
import {MyService} from 'js/services/MyService';
// Annotation section
@Component({
selector: 'my-app',
viewInjector: [MyService]
})
@View({
templateUrl: 'templates/my-app.tpl.html',
directives: [NgFor]
})
class MyComponent {
mySvc:MyService;
num:Number;
constructor(mySvc:MyService) {
this.mySvc = mySvc;
this.mySvc.getData();
setTimeout(() => this.updateNum(), 5000);
}
updateNum() {
this.num = 123456;
}
}
bootstrap(MyComponent, [MyService]);
新my-app.tpl.html <div>
<h2>My List {{num}}</h2>
<ul>
<li *ng-for="#item of mySvc.myList">Item: {{item}}</li>
</ul>
</div>
那么我该如何让angular2在'success'事件后不更新其他属性的情况下识别数据已经更改?
是有什么我错过了使用NgFor指令?
所以我终于找到了一个我喜欢的解决方案。在谷歌事件监听器触发后,如何在angular2中更改后更新视图,我在zone.run()中更新了myList,现在我的数据像预期的那样在我的视图中更新。
MyService.ts
/// <reference path="../../../typings/tsd.d.ts" />
// Import
import {NgZone} from 'angular2/angular2';
import {SocketService} from 'js/services/SocketService';
export class MyService {
zone:NgZone;
myList:Array<string> = [];
socketSvc:SocketService;
constructor() {
this.zone = new NgZone({enableLongStackTrace: false});
this.socketSvc = new SocketService();
this.initListeners();
}
getData() {
this.socketSvc.emit('event');
}
initListeners() {
this.socketSvc.socket.on('success', (data) => {
this.zone.run(() => {
this.myList = data;
console.log('Updated List: ', this.myList);
});
});
}
}
移动你的插座。io初始化到Service构造函数,它会工作的。
看一下这个例子:
import {Injectable} from 'angular2/core';
@Injectable()
export class SocketService {
socket:SocketIOClient.Socket;
constructor(){
this.socket = io.connect("localhost:8000");
}
public getSocket():SocketIOClient.Socket{
return this.socket;
}
}
现在,每当你将这个服务注入到组件并使用套接字时,你的视图就会自动更新。但是如果你把它留在全局作用域中,你将不得不与一些东西交互来强制视图更新。
下面是一个使用此服务的示例组件:
export class PostsComponent {
socket: SocketIOClient.Socket;
posts: Array<Post> = [];
constructor(private _socketService:SocketService){
this.socket.on('new-post', data => {
this.posts.push(new Post(data.id, data.text));
});
}
一个非常简单的方法就是在你想要更新的变量中运行。
zone.run(()=>{
this.variable = this.variable;
});
看起来不那么容易,但是如果在zone中运行,只要将它赋值给自身就会更新它。我不知道这个问题在angular2中是否仍然存在,因为我运行的是旧版本
UPDATE
我链接的柱塞提供了一个基本的例子,但它让我沮丧的是,我不能显示一个完整的"工作示例"。因此,我创建了一个github仓库,您可以下拉查看我下面讨论的部分的完整工作示例。
所以在你的实际问题中有两个问题。你在"MyService"的原始代码中有一个错别字。ts"文件
self.someList = data;//should be this.someList, self is the browser window object
另一个问题是Angular2不能像你期望的那样识别变更检测。如果它被设置为'this',我仍然不认为它会更新你的组件视图。
在你的回答中,它确实有效,但你似乎在以错误的方式回避这个问题。你应该在你的服务中实现一个Observable。
当您将这两个特性结合在一起时,您可以以相当直接的方式实现帆。我已经创建了一个示例plunker,但请记住,它实际上并没有连接到帆服务器,因为plunker需要https,我不打算为我的本地机器购买ssl证书。这些代码确实反映了你应该如何在Angular2中实现与风帆的通信。
基本思想可以在src/io.service中找到。ts文件
constructor() {
this._ioMessage$ = <Subject<{}>>new Subject();
//self is the window object in the browser, the 'io' object is actually on global scope
self.io.sails.connect('https://localhost:1337');//This fails as no sails server is listening and plunker requires https
this.listenForIOSubmission();
}
get ioMessage$(){
return this._ioMessage$.asObservable();
}
private listenForIOSubmission():void{
if(self.io.socket){//since the connect method failed in the constructor the socket object doesn't exist
//if there is a need to call emit or any other steps to prep Sails on node.js, do it here.
self.io.socket.on('success', (data) => {//guessing 'success' would be the eventIdentity
//note - you data object coming back from node.js, won't look like what I am using in this example, you should adjust your code to reflect that.
this._ioMessage$.next(data);//now IO is setup to submit data to the subscribbables of the observer
});
}
}
如果您感兴趣,我建议使用ngrx/store与OnPush
更改检测。我也遇到过类似的问题,在Angular之外发生了一些事情(不管这到底意味着什么),我的视图没有反映出变化。
使用带有事件分派器的Redux模式和保存我的数据的单个状态以及OnPush
更改检测已经为我解决了这个问题。我不知道为什么或如何解决这个问题。Cum grano salis.
我也有同样的问题,问题是:
我正在使用:"angular2 2.0.0-beta.1"似乎有一个bug,因为更新后:"angular2 2.0.0-beta.15"
我希望这对你有帮助,我吸取了教训