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 emits a socket.io event to my NodeJS server which in turn performs an external api call and parses some data. The NodeJS server then emits a success event with the new data that I listen for in my service. 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). 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
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>
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.
For instance:
new 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
<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?
Is there something I am missing with the use of the NgFor directive?
default change detection: Angular decides if the view needs to be updated by comparing all the template expression values before and after the occurrence of an event, for all components of the component tree.
By default, angular will run the change detector every time @Input() data is changed or modified. But with OnPush strategy, the change detector is only triggered if the data passed on @Input() has a new reference.
To run the change detector manually: Inject ChangeDetectorRef service in the component. Use markForCheck in the subscription method to instruct Angular to check the component the next time change detectors run. On the ngOnDestroy() life cycle hook, unsubscribe from the observable.
Angular 2's change detection system is built on top of zone. js hooks. Once an asynchronous action completes, Angular 2 starts its change detection routine. This means traversing all of the nodes in the "component tree" always starting with the root node.
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); }); }); } }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With