Tengo un componente simple que llama a una API REST cada pocos segundos y recibe algunos datos JSON. Puedo ver por mis declaraciones de registro y el tráfico de red que los datos JSON que se devuelven están cambiando, y mi modelo se está actualizando, sin embargo, la vista no está cambiando.
Mi componente se ve así:
import {Component, OnInit} from 'angular2/core';
import {RecentDetectionService} from '../services/recentdetection.service';
import {RecentDetection} from '../model/recentdetection';
import {Observable} from 'rxjs/Rx';
@Component({
selector: 'recent-detections',
templateUrl: '/app/components/recentdetection.template.html',
providers: [RecentDetectionService]
})
export class RecentDetectionComponent implements OnInit {
recentDetections: Array<RecentDetection>;
constructor(private recentDetectionService: RecentDetectionService) {
this.recentDetections = new Array<RecentDetection>();
}
getRecentDetections(): void {
this.recentDetectionService.getJsonFromApi()
.subscribe(recent => { this.recentDetections = recent;
console.log(this.recentDetections[0].macAddress) });
}
ngOnInit() {
this.getRecentDetections();
let timer = Observable.timer(2000, 5000);
timer.subscribe(() => this.getRecentDetections());
}
}
Y mi punto de vista se ve así:
<div class="panel panel-default">
<!-- Default panel contents -->
<div class="panel-heading"><h3>Recently detected</h3></div>
<div class="panel-body">
<p>Recently detected devices</p>
</div>
<!-- Table -->
<table class="table" style="table-layout: fixed; word-wrap: break-word;">
<thead>
<tr>
<th>Id</th>
<th>Vendor</th>
<th>Time</th>
<th>Mac</th>
</tr>
</thead>
<tbody >
<tr *ngFor="#detected of recentDetections">
<td>{{detected.broadcastId}}</td>
<td>{{detected.vendor}}</td>
<td>{{detected.timeStamp | date:'yyyy-MM-dd HH:mm:ss'}}</td>
<td>{{detected.macAddress}}</td>
</tr>
</tbody>
</table>
</div>
Puedo ver de los resultados de console.log(this.recentDetections[0].macAddress)
que el objeto RecentDetections se está actualizando, pero la tabla en la vista nunca cambia a menos que vuelva a cargar la página.
Estoy luchando por ver qué estoy haciendo mal aquí. ¿Alguien puede ayudar?
Respuestas:
Puede ser que el código en su servicio de alguna manera salga de la zona de Angular. Esto rompe la detección de cambios. Esto debería funcionar:
Para conocer otras formas de invocar la detección de cambios, consulte Cómo activar la detección de cambios manualmente en Angular
Las formas alternativas de invocar la detección de cambios son
ejecutar inmediatamente la detección de cambios para el componente actual y sus hijos
para incluir el componente actual la próxima vez que Angular ejecute la detección de cambios
ejecutar detección de cambios para toda la aplicación
fuente
cdRef.detectChanges()
lugar dezone.run()
. Esto podría ser más eficiente, ya que no ejecutará la detección de cambios en todo el árbol de componentes como lozone.run()
hace.detectChanges()
si los cambios que realiza son locales para el componente y sus descendientes. Mi entendimiento es quedetectChanges()
comprobará el componente y sus descendientes, perozone.run()
yApplicationRef.tick()
comprobará todo el árbol de componentes.@Input()
no tenía sentido ni funcionó.Originalmente es una respuesta en los comentarios de @Mark Rajcok, pero quiero colocarlo aquí como probado y funcionó como una solución usando ChangeDetectorRef , veo un buen punto aquí:
Entonces el código debe ser como:
Editar : el uso de
.detectChanges()
una subscibe interna podría generar problemas. Intento de usar una vista destruida: detectChangesPara resolverlo, debe hacerlo
unsubscribe
antes de destruir el componente, por lo que el código completo será como:fuente
Tratar de usar
@Input() recentDetections: Array<RecentDetection>;
EDITAR: La razón por la cual
@Input()
es importante es porque desea vincular el valor en el archivo de mecanografiado / javascript a la vista (html). La vista se actualizará sola si@Input()
se cambiaun valor declarado con eldecorador. Si una@Input()
o@Output()
se cambia decorador, unngOnChanges
-Evento se disparará, y la vista se actualizará con el nuevo valor. Puede decir que la@Input()
voluntad de dos vías unirá el valor.busque Entrada en este enlace por angular: glosario para más información
EDITAR: después de aprender más sobre el desarrollo de Angular 2, pensé que
@Input()
realmente no es la solución, y como se menciona en los comentarios,Si echas un vistazo a la respuesta de @ Günter, es una solución más precisa y correcta al problema. Todavía mantendré esta respuesta aquí, pero siga la respuesta de Günter como la correcta.
fuente
@Input()
palabra clave, y ese enlace asegura que el valor se actualice en la vista. El valor será parte de un evento del ciclo de vida. Esta publicación contiene más información sobre la@Input()
palabra clave@Input()
está involucrado un aquí o por qué debería estar, y la pregunta no proporciona suficiente información. Gracias por tu paciencia de todos modos :)@Input()
Es más una solución que oculta la raíz del problema. Los problemas tienen que estar relacionados con las zonas y la detección de cambios.En mi caso, tuve un problema muy similar. Estaba actualizando mi vista dentro de una función que estaba siendo llamada por un componente primario, y en mi componente primario olvidé usar @ViewChild (NameOfMyChieldComponent). Perdí al menos 3 horas solo por este estúpido error. es decir: no necesitaba usar ninguno de esos métodos:
fuente
En lugar de ocuparse de zonas y detección de cambios, deje que AsyncPipe maneje la complejidad. Esto colocará la suscripción observable, la cancelación de la suscripción (para evitar pérdidas de memoria) y la detección de cambios en los hombros angulares.
Cambie su clase para hacer un observable, que emitirá resultados de nuevas solicitudes:
Y actualice su vista para usar AsyncPipe:
Quiere agregar que es mejor hacer un servicio con un método que tome
interval
argumentos y:exhaustMap
como en el código anterior);fuente