我试图在websocket事件返回更新数据后更新我的视图。
我在我的应用程序中注入了一个服务,并在服务上调用了getData()方法。此方法向我的NodeJS服务器发出一个socket.io事件,该事件又执行外部api调用并解析一些数据。然后NodeJS服务器发出一个成功事件,其中包含我在我的服务中监听的新数据。返回成功事件后,我会更新我的视图中引用的服务的属性。
然而,无论我尝试什么,我都无法在属性更新后显示数据。
我已经搜索了几天,我找到的所有博客文章都说这个变化应该是无缝的,或者我需要以某种方式合并zone.js,或者尝试使用表单的相同逻辑(但是我试图没有用户交互就这样做)。似乎没有什么对我有用,我有点沮丧。
例如:
假设我收到一个字符串数组,我想用。创建一个未排序的列表。
app.ts
package ScopeTest::Model;
use Mojo::Base -base;
has 'ctrl';
...
$self->ctrl->app->log->debug("username = " . $self->ctrl->username );
MyService.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]);
MY-app.tpl.html
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;
});
}
}
有趣的是,我发现如果我在我的组件中加入了一个超时,它会在从成功回调更新someList属性后更新我在视图上设置的某些任意属性,那么两个属性值会同时正确更新
例如:
新app.ts
<div>
<h2>My List</h2>
<ul>
<li *ng-for="#item of mySvc.myList">Item: {{item}}</li>
</ul>
</div>
new my-app.tpl.html
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]);
那么我应该如何在没有更新其他属性的情况下让angular2识别出数据在'success'事件后发生了变化?
使用NgFor指令是否缺少某些内容?
答案 0 :(得分:35)
所以我终于找到了一个我喜欢的解决方案。根据这篇文章How to update view after change in angular2 after google event listener fired中的答案,我在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);
});
});
}
}
答案 1 :(得分:5)
只需将socket.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));
});
}
答案 2 :(得分:4)
一种非常简单的方法是在区域中运行您想要更新的任何变量。
zone.run(()=>{
this.variable = this.variable;
});
看起来似乎并不那么容易,但如果在区域中运行,只需将其分配给自身就会更新它。我不知道这是否仍然是angular2中的问题,因为我正在运行一些旧版本。
答案 3 :(得分:1)
<强>更新强>
我链接的plunker提供了一个基本示例,但令我感到沮丧的是,我无法展示整个“工作示例”。所以我创建了一个github repo,您可以下载以查看我在下面讨论的各个部分的完整工作示例。
所以在你的实际问题中有两个问题。您在“MyService.ts”文件中的原始代码中输入了错误
self.someList = data;//should be this.someList, self is the browser window object
另一个问题是Angular2无法按照您期望的方式识别change detection。如果已将其设置为“this”,我仍然认为它不会更新您的组件视图。
在你的回答中,它确实有效,但你却以错误的方式绕过这个问题。您应该实施的是服务中的Observable。
当您将这两个功能组合在一起时,您可以以相当直接的方式实现风帆。我创建了一个示例plunker,但请记住它实际上并没有连接到sails服务器,因为plunker需要https而我不打算为我的本地机器购买ssl证书。该代码确实反映了您应该如何在Angular2中实现与sails的通信。
基本思路可以在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
});
}
}
答案 4 :(得分:0)
如果您有兴趣,我建议您使用ngrx/store进行admin@cqlsh> SELECT cql_version FROM system.local;
cql_version
-------------
3.4.0
更改检测。我遇到类似的问题,在Angular之外发生的事情(无论这意味着什么),我的观点没有反映出这种变化。
将Redux模式与事件调度程序一起使用以及将我的数据与nodetool version
更改检测结合在一起的单个状态已经为我解决了这个问题。我不知道为什么或如何解决这个问题。 Cum grano salis。
有关详细信息,请参阅this comment。
答案 5 :(得分:0)
我遇到了同样的问题,问题是:
我正在使用 &#34; angular2&#34;:&#34; 2.0.0-beta.1&#34; 似乎有一个bug,因为更新后 &#34; angular2&#34;:&#34; 2.0.0-beta.15&#34; 它工作正常。我希望它有所帮助,我从中学到了痛苦的方式