Ich habe eine einfache Komponente, die alle paar Sekunden eine REST-API aufruft und einige JSON-Daten zurückerhält. Ich kann anhand meiner Protokollanweisungen und des Netzwerkverkehrs erkennen, dass sich die zurückgegebenen JSON-Daten ändern und mein Modell aktualisiert wird. Die Ansicht ändert sich jedoch nicht.
Meine Komponente sieht aus wie:
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());
}
}
Und meine Ansicht sieht aus wie:
<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>
Ich kann anhand der Ergebnisse sehen, console.log(this.recentDetections[0].macAddress)
dass das RecentDetections-Objekt aktualisiert wird, aber die Tabelle in der Ansicht ändert sich nie, es sei denn, ich lade die Seite neu.
Ich kämpfe darum zu sehen, was ich hier falsch mache. Kann jemand helfen?
Antworten:
Es kann sein, dass der Code in Ihrem Dienst irgendwie aus Angulars Zone ausbricht. Dies unterbricht die Änderungserkennung. Das sollte funktionieren:
Weitere Möglichkeiten zum Aufrufen der Änderungserkennung finden Sie unter Manuelles Auslösen der Änderungserkennung in Angular
Alternative Möglichkeiten zum Aufrufen der Änderungserkennung sind
um die Änderungserkennung für die aktuelle Komponente und ihre untergeordneten Komponenten sofort auszuführen
um die aktuelle Komponente beim nächsten Mal einzubeziehen
um die Änderungserkennung für die gesamte Anwendung auszuführen
quelle
cdRef.detectChanges()
stattdessen aufzurufenzone.run()
. Dies könnte effizienter sein, da die Änderungserkennung nicht wie im gesamten Komponentenbaum ausgeführtzone.run()
wird.detectChanges()
wenn Änderungen, die Sie vornehmen, lokal für die Komponente und ihre Nachkommen sind. Mein Verständnis ist , dassdetectChanges()
die Komponente und ihre Nachkommen überprüfen, sondernzone.run()
undApplicationRef.tick()
den gesamten Komponentenbaum überprüfen.@Input()
ergab weder Sinn noch funktionierte sie.Es ist ursprünglich eine Antwort in den Kommentaren von @Mark Rajcok, aber ich möchte es hier als getestete und als Lösung mit ChangeDetectorRef funktionierende Lösung platzieren . Ich sehe hier einen guten Punkt:
Code muss also wie folgt aussehen:
Bearbeiten : Die Verwendung eines
.detectChanges()
internen Subscibe kann zu Problemen führen. Versuch, eine zerstörte Ansicht zu verwenden: detectChangesUm es zu lösen, müssen Sie
unsubscribe
vor dem Zerstören der Komponente Folgendes tun: Der vollständige Code lautet wie folgt:quelle
Versuchen zu benutzen
@Input() recentDetections: Array<RecentDetection>;
BEARBEITEN: Der Grund dafür
@Input()
ist, dass Sie den Wert in der Typoskript- / Javascript-Datei an die Ansicht (HTML) binden möchten. Die Ansicht aktualisiert sich selbst, wenn ein mit dem@Input()
Dekoratordeklarierter Wertgeändert wird. Wenn ein@Input()
oder@Output()
Dekorator geändert wird, wird einngOnChanges
-event ausgelöst und die Ansicht wird mit dem neuen Wert aktualisiert. Sie können sagen, dass der@Input()
Wille den Wert in beide Richtungen bindet.Suchen Sie nach Eingabe auf diesem Link nach Winkel: Glossar für weitere Informationen
BEARBEITEN: Nachdem ich mehr über die Entwicklung von Angular 2 erfahren hatte, stellte ich fest, dass
@Input()
es nicht die Lösung ist, wirklich etwas zutun, und wie in den Kommentaren erwähnt,Wenn Sie sich die Antwort von @ Günter ansehen, ist dies eine genauere und korrektere Lösung des Problems. Ich werde diese Antwort hier behalten, aber bitte folge Günters Antwort als die richtige.
quelle
@Input()
Schlüsselworts ist eine Eigenschaftsbindung angehängt , und diese Bindung stellt sicher, dass der Wert in der Ansicht aktualisiert wird. Der Wert ist Teil eines Lebenszyklusereignisses. Dieser Beitrag enthält einige weitere Informationen über das@Input()
Schlüsselwort@Input()
hier involviert ist oder warum es sein sollte und die Frage liefert nicht genug Informationen.@Input()
ist eher eine Problemumgehung, die die Wurzel des Problems verbirgt. Die Probleme müssen mit den Zonen und der Änderungserkennung zusammenhängen.In meinem Fall hatte ich ein sehr ähnliches Problem. Ich habe meine Ansicht in einer Funktion aktualisiert, die von einer übergeordneten Komponente aufgerufen wurde, und in meiner übergeordneten Komponente habe ich vergessen, @ViewChild (NameOfMyChieldComponent) zu verwenden. Ich habe mindestens 3 Stunden nur für diesen dummen Fehler verloren. dh: Ich musste keine dieser Methoden anwenden:
quelle
Anstatt sich mit Zonen und Änderungserkennung zu befassen, lassen Sie AsyncPipe die Komplexität bewältigen. Dadurch werden beobachtbare Abonnements, Abbestellungen (um Speicherlecks zu vermeiden) und Änderungserkennung auf Angular-Schultern ausgeführt.
Ändern Sie Ihre Klasse, um eine beobachtbare Klasse zu erstellen, die Ergebnisse neuer Anforderungen ausgibt:
Und aktualisieren Sie Ihre Ansicht, um AsyncPipe zu verwenden:
Möchten Sie hinzufügen, dass es besser ist, einen Dienst mit einer Methode zu erstellen, die
interval
Argumente akzeptiert, und:exhaustMap
wie im obigen Code verwenden);quelle