简体   繁体   English

数据更新后Angular2视图不更改

[英]Angular2 View Not Changing After Data Is Updated

I am trying to update my view after a websocket event returns updated data. 我试图在websocket事件返回更新数据后更新我的视图。

I injected a service into my app and call getData() method on the service. 我将一个服务注入到我的应用程序中并在服务上调用getData()方法。 This method emits a socket.io event to my NodeJS server which in turn performs an external api call and parses some data. 此方法向我的NodeJS服务器发出一个socket.io事件,该事件又执行外部api调用并解析一些数据。 The NodeJS server then emits a success event with the new data that I listen for in my service. 然后NodeJS服务器发出一个成功事件,其中包含我在我的服务中监听的新数据。 When the success event is returned I then update my property on the service that is referenced in my view. 返回成功事件后,我会在我的视图中引用的服务上更新我的属性。

However no matter what I try I cannot get the data to show once the property is updated. 但无论我尝试什么,一旦属性更新,我都无法获取数据。

I have searched for a few days now and all I find are blog posts that say this change should be seamless, or that I need to incorporate zone.js somehow, or to try the same logic using forms (however im trying to do this without user interaction). 我已经搜索了几天,我发现的所有内容都是博客文章,说这个变化应该是无缝的,或者我需要以某种方式合并zone.js,或者尝试使用表单相同的逻辑(但是我试图这样做而没有用户互动)。 Nothing seems to work for me and I am getting a bit frustrated. 似乎没有什么对我有用,我有点沮丧。

For example: 例如:

Lets say I receive an array of strings that I want to create an unsorted list with. 假设我收到一个字符串数组,我想创建一个未排序的列表。

app.ts 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 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 我-app.tpl.html

<div>
    <h2>My List</h2>
    <ul>
        <li *ng-for="#item of mySvc.myList">Item: {{item}}</li>
    </ul>
</div>

Interesting enough, I have found that If I incorporate a timeout within my component that updates some arbitrary property that I set on the view after the someList property is updated from the success callback then both property values are updated correctly at the same time. 有趣的是,我发现如果我在我的组件中加入了一个超时,它会更新我在视图上设置的一些任意属性,从成功回调更新someList属性后,两个属性值会同时正确更新。

For instance: 例如:

new app.ts 新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]);

new my-app.tpl.html 新的my-app.tpl.html

<div>
    <h2>My List {{num}}</h2>
    <ul>
        <li *ng-for="#item of mySvc.myList">Item: {{item}}</li>
    </ul>
</div>

So how should I go about getting angular2 to recognize that the data has changed after the 'success' event without updating some other property? 那么我应该如何在没有更新其他属性的情况下让angular2识别出数据在“成功”事件后发生了变化?

Is there something I am missing with the use of the NgFor directive? 使用NgFor指令有什么我想念的吗?

So I finally found a solution that I like. 所以我终于找到了一个我喜欢的解决方案。 Following the answer in this post How to update view after change in angular2 after google event listener fired I updated myList within zone.run() and now my data is updated in my view like expected. 按照这篇文章中的答案如何在google事件监听器触发后更改angular2后更新视图我在zone.run()中更新了myList,现在我的数据在我的视图中更新,如预期的那样。

MyService.ts 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);
            });
        });
    }
 }

Just move your socket.io initialization to Service constructor and it will work. 只需将socket.io初始化移动到Service构造函数即可。
Take a look at this example: 看看这个例子:

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;
    }
}

Now whenever you inject this service to a component and use a socket, your view will automatically update. 现在,只要将此服务注入组件并使用套接字,您的视图就会自动更新。 But if you leave it in a global scope like you did, you will have to interact with something in order to force the view to update. 但是如果你把它放在像你这样的全局范围内,你将不得不与某些东西进行交互以强制视图更新。
Here is an example component that uses this service: 以下是使用此服务的示例组件:

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));
        });  
}  

A very simple way to do this is just run in zone whatever variable you want to update. 一个非常简单的方法是在区域中运行您想要更新的任何变量。

zone.run(()=>{
    this.variable = this.variable;
});

Doesn't seem like it can be that easy, but just assigning it to itself will update it if run in zone. 似乎不是那么容易,但只是将它分配给自己将在区域中运行时更新它。 I don't know if this is still an issue in angular2 since I'm running a little bit older version. 我不知道这是否仍然是angular2中的一个问题,因为我正在运行一些旧版本。

UPDATE UPDATE

The plunker I linked provided a base example but it frustrates me that I couldn't show an entire "working example". 我链接的plunker提供了一个基本示例,但它让我感到沮丧,我无法展示整个“工作示例”。 So I created a github repo that you can pull down to see a full working example of the pieces I talked about below. 因此,我创建了一个github仓库 ,您可以下载以查看我在下面谈到的各个部分的完整工作示例。


So in your actual question there were two problems. 所以在你的实际问题中有两个问题。 You had a typo in the original code in the "MyService.ts" file 您在“MyService.ts”文件中的原始代码中输入了拼写错误

self.someList = data;//should be this.someList, self is the browser window object

Another issue is Angular2 doesn't recognize change detection the way you're expecting it to. 另一个问题是Angular2无法像您期望的那样识别变化检测 If it had been set to 'this', I still don't think it would have updated your component view. 如果已将其设置为“this”,我仍然认为它不会更新您的组件视图。

In your answer, it does work but you're kind of going around the issue the wrong way. 在你的回答中,它确实有效,但你有点错误地绕过这个问题。 What you should implement is an Observable in your service. 您应该实现的是服务中的Observable

When you combine these two features together you can implement sails in a fairly straight forward way. 将这两个功能组合在一起时,您可以以相当直接的方式实现风帆。 I have created an example plunker but keep in mind that it doesn't actually connect to a sails server because plunker requires https and I'm not going to go buy a ssl cert for my local machine. 我已经创建了一个示例plunker但请记住它实际上并没有连接到sails服务器,因为plunker需要https而我不打算为我的本地机器购买ssl证书。 The code does reflect how you should implement communication with sails in Angular2. 代码确实反映了如何在Angular2中实现与sails的通信。

The basic idea can be found in the src/io.service.ts file 基本思路可以在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
    });
  }
}

If you're interested, may I suggest using ngrx/store with OnPush change detection. 如果您有兴趣,我可以建议使用带有OnPush变化检测的ngrx / store I have run into similar issues where something happened outside of Angular (whatever that means exactly) and my view did not reflect the change. 我遇到过类似的问题,在Angular之外发生了一些事情(无论这意味着什么),我的观点并没有反映出这种变化。

Using the Redux pattern with event dispatchers and a single state that holds my data in conjunction with OnPush change detection has solved that issue for me. 将Redux模式与事件调度程序一起使用以及将我的数据与OnPush更改检测结合在一起的单个状态已经为我解决了这个问题。 I do not know why or how it solves this problem though. 我不知道为什么或如何解决这个问题。 Cum grano salis. Cum grano salis。

See this comment specifically for more details. 有关更多详细信息,请参阅此评论

I was having the same problem, and the issue was: 我遇到了同样的问题,问题是:

I was using "angular2": "2.0.0-beta.1" It seems that there is a bug, because after updating to "angular2": "2.0.0-beta.15" It is working fine. 我正在使用“angular2”:“2.0.0-beta.1”似乎有一个错误,因为更新到“angular2”后:“2.0.0-beta.15”它工作正常。

I hope it helps, I learnt it the painful way 我希望它有所帮助,我学到了痛苦的方法

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM