example - websocket angular 6
La vista Angular2 no cambia después de actualizar los datos (6)
Estoy intentando actualizar mi vista después de que un evento de websocket devuelva datos actualizados.
Inyecté un servicio en mi aplicación y llamé al método getData () en el servicio. Este método emite un evento socket.io a mi servidor NodeJS que a su vez realiza una llamada a una API externa y analiza algunos datos. El servidor NodeJS emite un evento de éxito con los nuevos datos que escucho en mi servicio. Cuando se devuelve el evento de éxito, actualizo mi propiedad en el servicio al que se hace referencia en mi vista.
Sin embargo, no importa lo que intente, no puedo obtener los datos para mostrarlos una vez que se actualice la propiedad.
He buscado por algunos días y todo lo que encuentro son publicaciones de blog que dicen que este cambio debería ser perfecto, o que debo incorporar zone.js de alguna manera, o probar la misma lógica usando formularios (sin embargo, estoy intentando hacerlo sin la interacción del usuario). Nada parece funcionar para mí y me estoy frustrando un poco.
Por ejemplo:
Digamos que recibo una serie de cadenas con las que quiero crear una lista sin clasificar.
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>
Lo suficientemente interesante, he encontrado que si incorporo un tiempo de espera dentro de mi componente que actualiza alguna propiedad arbitraria que configuré en la vista después de que la propiedad someList se actualice desde la devolución de llamada correcta, ambos valores de propiedad se actualizan correctamente al mismo tiempo.
Por ejemplo:
nueva 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]);
nueva my-app.tpl.html
<div>
<h2>My List {{num}}</h2>
<ul>
<li *ng-for="#item of mySvc.myList">Item: {{item}}</li>
</ul>
</div>
Entonces, ¿cómo debo obtener Angular2 para reconocer que los datos han cambiado después del evento "éxito" sin actualizar alguna otra propiedad?
¿Hay algo que me esté perdiendo con el uso de la directiva NgFor?
Así que finalmente encontré una solución que me gusta. Siguiendo la respuesta en este post Cómo actualizar la vista después del cambio en angular2 después de que se activó el detector de eventos de Google, actualicé myList dentro de zone.run () y ahora mis datos se actualizan en mi opinión como se esperaba.
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);
});
});
}
}
Estaba teniendo el mismo problema, y el problema era:
Estaba usando "angular2": "2.0.0-beta.1" Parece que hay un error, porque después de actualizar a "angular2": "2.0.0-beta.15" Está funcionando bien.
Espero que ayude, lo aprendí de manera dolorosa.
Si está interesado, le sugiero que utilice ngrx/store con la detección de cambios de OnPush
. Me he encontrado con problemas similares donde algo sucedió fuera de Angular (lo que sea que eso signifique exactamente) y mi opinión no reflejó el cambio.
El uso del patrón Redux con despachadores de eventos y un solo estado que contiene mis datos junto con la detección de cambios de OnPush
ha solucionado ese problema. Aunque no sé por qué o cómo resuelve este problema. Cum grano salis.
Ver este comentario específicamente para más detalles.
Simplemente mueva su inicialización de socket.io al constructor de servicios y funcionará.
Echale un vistazo a éste ejemplo:
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;
}
}
Ahora, cada vez que inyecte este servicio a un componente y use un zócalo, su vista se actualizará automáticamente. Pero si lo deja en un ámbito global como lo hizo, tendrá que interactuar con algo para forzar la actualización de la vista.
Aquí hay un componente de ejemplo que usa este servicio:
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));
});
}
Una forma muy sencilla de hacer esto es simplemente ejecutar en la zona la variable que desee actualizar.
zone.run(()=>{
this.variable = this.variable;
});
No parece que pueda ser tan fácil, pero asignarlo a sí mismo lo actualizará si se ejecuta en la zona. No sé si esto sigue siendo un problema en angular2 ya que estoy ejecutando una versión un poco más antigua.
ACTUALIZAR
El plunker que plunker proporcionó un ejemplo básico, pero me frustra que no pueda mostrar un "ejemplo funcional" completo. Así que creé un repositorio de github que puedes desplegar para ver un ejemplo completo de las piezas que mencioné a continuación.
Así que en tu pregunta real había dos problemas. Tuvo un error tipográfico en el código original en el archivo "MyService.ts"
self.someList = data;//should be this.someList, self is the browser window object
Otro problema es que Angular2 no reconoce la detección de cambios de la forma que usted espera. Si se hubiera configurado en ''esto'', todavía no creo que haya actualizado la vista de sus componentes.
En su respuesta, funciona, pero está manejando el problema de forma incorrecta. Lo que debes implementar es un Observable en tu servicio.
Cuando combina estas dos características juntas, puede implementar las velas de una manera bastante directa. He creado un plunker ejemplo, pero tenga en cuenta que en realidad no se conecta a un servidor de velas porque plunker requiere https y no voy a comprar un certificado ssl para mi máquina local. El código refleja cómo debe implementar la comunicación con las velas en Angular2.
La idea básica se puede encontrar en el archivo 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
});
}
}