Angular2 View Not Changing After Data Is Updated

前端 未结 6 1401
长情又很酷
长情又很酷 2020-12-09 15:42

I am trying to update my view after a websocket event returns updated data.

I injected a service into my app and call getData() method on the service. This method em

相关标签:
6条回答
  • 2020-12-09 16:15

    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.

    0 讨论(0)
  • 2020-12-09 16:16

    If you're interested, may I suggest using ngrx/store with OnPush change detection. I have run into similar issues where something happened outside of Angular (whatever that means exactly) and my view did not reflect the change.

    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. I do not know why or how it solves this problem though. Cum grano salis.

    See this comment specifically for more details.

    0 讨论(0)
  • 2020-12-09 16:19

    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.

    I hope it helps, I learnt it the painful way

    0 讨论(0)
  • 2020-12-09 16:21

    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.

    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);
                });
            });
        }
     }
    
    0 讨论(0)
  • 2020-12-09 16:30

    Just move your socket.io initialization to Service constructor and it will work.
    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));
            });  
    }  
    
    0 讨论(0)
  • 2020-12-09 16:36

    UPDATE

    The plunker I linked provided a base example but it frustrates me that I couldn't show an entire "working example". So I created a github repo that you can pull down to see a full working example of the pieces I talked about below.


    So in your actual question there were two problems. You had a typo in the original code in the "MyService.ts" file

    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. If it had been set to 'this', I still don't think it would have updated your component view.

    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.

    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. The code does reflect how you should implement communication with sails in Angular2.

    The basic idea can be found in the src/io.service.ts file

    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
        });
      }
    }
    
    0 讨论(0)
提交回复
热议问题