J'ai un composant simple qui appelle une API REST toutes les quelques secondes et reçoit en retour des données JSON. Je peux voir à partir de mes instructions de journal et du trafic réseau que les données JSON renvoyées changent et que mon modèle est en cours de mise à jour, mais la vue ne change pas.
Mon composant ressemble à:
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());
}
}
Et mon point de vue ressemble à:
<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>
Je peux voir d'après les résultats console.log(this.recentDetections[0].macAddress)
que l'objet recentDetections est en cours de mise à jour, mais le tableau de la vue ne change jamais à moins que je ne recharge la page.
J'ai du mal à voir ce que je fais de mal ici. Quelqu'un peut-il aider?
Réponses:
Il se peut que le code de votre service sorte d'une manière ou d'une autre de la zone d'Angular. Cela rompt la détection des changements. Cela devrait fonctionner:
Pour d'autres moyens d'appeler la détection des modifications, voir Déclenchement manuel de la détection de changement dans Angular
D'autres moyens d'invoquer la détection des modifications sont
pour exécuter immédiatement la détection des modifications pour le composant actuel et ses enfants
pour inclure le composant actuel la prochaine fois qu'Angular exécute la détection de changement
pour exécuter la détection des modifications pour l'ensemble de l'application
la source
cdRef.detectChanges()
place dezone.run()
. Cela pourrait être plus efficace, car ilzone.run()
n'exécutera pas de détection de changement sur l'ensemble de l'arborescence des composants comme le fait.detectChanges()
si les modifications que vous apportez sont locales pour le composant et ses descendants. Je crois comprendre quedetectChanges()
cela vérifiera le composant et ses descendants, maiszone.run()
etApplicationRef.tick()
vérifiera l'ensemble de l'arborescence des composants.@Input()
n'avait pas de sens ni fonctionnait.C'est à l'origine une réponse dans les commentaires de @Mark Rajcok, mais je veux le placer ici comme une solution testée et travaillée en utilisant ChangeDetectorRef , je vois un bon point ici:
Le code doit donc être comme:
Edit : L'utilisation de
.detectChanges()
subscibe à l'intérieur peut entraîner un problème.Tentative d'utilisation d'une vue détruite: detectChangesPour le résoudre, vous devez le faire
unsubscribe
avant de détruire le composant, le code complet ressemblera donc à:la source
Essayez d'utiliser
@Input() recentDetections: Array<RecentDetection>;
EDIT: La raison pour laquelle
@Input()
est importante est que vous souhaitez lier la valeur du fichier dactylographié / javascript à la vue (html). La vue se mettra à jour si une valeur déclarée avec le@Input()
décorateur est modifiée. Si un décorateur@Input()
ou@Output()
est modifié, unngOnChanges
-event se déclenchera et la vue se mettra à jour avec la nouvelle valeur. Vous pouvez dire que le@Input()
va lier la valeur dans les deux sens.rechercher Entrée sur ce lien par angulaire: glossaire pour plus d'informations
ÉDITER: après en avoir appris plus sur le développement d'Angular 2, j'ai pensé que faire un
@Input()
n'est vraiment pas la solution, et comme mentionné dans les commentaires,Si vous regardez la réponse de @ Günter, c'est une solution plus précise et plus correcte au problème. Je garderai toujours cette réponse ici, mais veuillez suivre la réponse de Günter comme étant la bonne.
la source
@Input()
mot clé, et cette liaison garantit que la valeur est mise à jour dans la vue. la valeur fera partie d'un événement du cycle de vie. Ce message contient plus d'informations sur le@Input()
mot-clé@Input()
est impliqué ici ou pourquoi il devrait l'être et la question ne fournit pas suffisamment d'informations. Merci pour votre patience quand même :)@Input()
est plus une solution de contournement qui cache la racine du problème. Les problèmes doivent être liés aux zones et à la détection des changements.Dans mon cas, j'ai eu un problème très similaire. J'étais en train de mettre à jour ma vue dans une fonction qui était appelée par un composant parent, et dans mon composant parent, j'ai oublié d'utiliser @ViewChild (NameOfMyChieldComponent). J'ai perdu au moins 3 heures rien que pour cette stupide erreur. ie: je n'ai pas eu besoin d'utiliser l' une de ces méthodes:
la source
Au lieu de s'occuper des zones et de la détection des changements, laissez AsyncPipe gérer la complexité. Cela mettra l'abonnement observable, le désabonnement (pour éviter les fuites de mémoire) et la détection des changements sur les épaules angulaires.
Changez votre classe pour créer un observable, qui émettra les résultats des nouvelles requêtes:
Et mettez à jour votre vue pour utiliser AsyncPipe:
Vous voulez ajouter, qu'il est préférable de créer un service avec une méthode qui prendra
interval
argument, et:exhaustMap
comme dans le code ci-dessus);la source