Angular2视图在数据更新后不改变



我试图在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"

我希望这对你有帮助,我吸取了教训

相关内容

  • 没有找到相关文章

最新更新