数据更新后Angular2视图不更改

时间:2015-07-29 17:12:10

标签: websocket angular

我试图在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指令是否缺少某些内容?

6 个答案:

答案 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; 它工作正常。

我希望它有所帮助,我从中学到了痛苦的方式