ejemplos curso cli angular

curso - Angular 2: la vista no se actualiza después de los cambios del modelo



angular wikipedia (5)

En lugar de lidiar con 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:

export class RecentDetectionComponent implements OnInit { recentDetections$: Observable<Array<RecentDetection>>; constructor(private recentDetectionService: RecentDetectionService) { } ngOnInit() { this.recentDetections$ = Observable.interval(5000) .exhaustMap(() => this.recentDetectionService.getJsonFromApi()) .do(recent => console.log(recent[0].macAddress)); } }

Y actualice su vista para usar AsyncPipe:

<tr *ngFor="let detected of recentDetections$ | async"> ... </tr>

Quiere agregar que es mejor hacer un servicio con un método que tome argumentos de interval y:

  • crear nuevas solicitudes (usando exhaustMap como en el código anterior);
  • manejar solicitudes de errores;
  • evitar que el navegador realice nuevas solicitudes mientras está desconectado.

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 en 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?


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:

  • ChangeDetectorRef.detectChanges ()
  • ChangeDetectorRef.markForCheck ()
  • ApplicationRef.tick ()

Intente utilizar @Input() recentDetections: Array<RecentDetection>;

EDITAR: La razón por la que @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 se cambia un valor declarado con el decorador @Input() . Si se @Input() un @Input() o @Output() , se ngOnChanges un ngOnChanges ngOnChanges, y la vista se actualizará con el nuevo valor. Puede decir que @Input() el valor de dos maneras.

busque Entrada en este enlace por angular: glossary para más información

EDITAR: después de aprender más sobre el desarrollo de Angular 2, pensé que hacer un @Input() realmente no es la solución, y como se menciona en los comentarios,

@Input() solo se aplica cuando los datos se modifican mediante el enlace de datos desde fuera del componente (los datos vinculados en el elemento primario se modifican) no cuando los datos se modifican desde el código dentro del componente.

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.


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í:

Otra alternativa es inyectar ChangeDetectorRef y llamar a cdRef.detectChanges() lugar de zone.run() . Esto podría ser más eficiente, ya que no ejecutará la detección de cambios en todo el árbol de componentes como zone.run() . - Mark Rajcok

Entonces el código debe ser como:

import {Component, OnInit, ChangeDetectorRef} from ''angular2/core''; export class RecentDetectionComponent implements OnInit { recentDetections: Array<RecentDetection>; constructor(private cdRef: ChangeDetectorRef, // <== added private recentDetectionService: RecentDetectionService) { this.recentDetections = new Array<RecentDetection>(); } getRecentDetections(): void { this.recentDetectionService.getJsonFromApi() .subscribe(recent => { this.recentDetections = recent; console.log(this.recentDetections[0].macAddress); this.cdRef.detectChanges(); // <== added }); } ngOnInit() { this.getRecentDetections(); let timer = Observable.timer(2000, 5000); timer.subscribe(() => this.getRecentDetections()); } }

Editar : el uso de .detectChanges() dentro de subscibe podría provocar un intento de usar una vista destruida: detectChanges

Para resolverlo, debe unsubscribe de unsubscribe antes de destruir el componente, por lo que el código completo será como:

import {Component, OnInit, ChangeDetectorRef, OnDestroy} from ''angular2/core''; export class RecentDetectionComponent implements OnInit, OnDestroy { recentDetections: Array<RecentDetection>; private timerObserver: Subscription; constructor(private cdRef: ChangeDetectorRef, // <== added private recentDetectionService: RecentDetectionService) { this.recentDetections = new Array<RecentDetection>(); } getRecentDetections(): void { this.recentDetectionService.getJsonFromApi() .subscribe(recent => { this.recentDetections = recent; console.log(this.recentDetections[0].macAddress); this.cdRef.detectChanges(); // <== added }); } ngOnInit() { this.getRecentDetections(); let timer = Observable.timer(2000, 5000); this.timerObserver = timer.subscribe(() => this.getRecentDetections()); } ngOnDestroy() { this.timerObserver.unsubscribe(); } }


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:

import {Component, OnInit, NgZone} from ''angular2/core''; export class RecentDetectionComponent implements OnInit { recentDetections: Array<RecentDetection>; constructor(private zone:NgZone, // <== added private recentDetectionService: RecentDetectionService) { this.recentDetections = new Array<RecentDetection>(); } getRecentDetections(): void { this.recentDetectionService.getJsonFromApi() .subscribe(recent => { this.zone.run(() => { // <== added this.recentDetections = recent; console.log(this.recentDetections[0].macAddress) }); }); } ngOnInit() { this.getRecentDetections(); let timer = Observable.timer(2000, 5000); timer.subscribe(() => this.getRecentDetections()); } }

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

ChangeDetectorRef.detectChanges()

ejecutar inmediatamente la detección de cambios para el componente actual y sus hijos

ChangeDetectorRef.markForCheck()

para incluir el componente actual la próxima vez que Angular ejecute la detección de cambios

ApplicationRef.tick()

ejecutar detección de cambios para toda la aplicación