Mam prosty komponent, który co kilka sekund wywołuje REST API i odbiera pewne dane JSON. Widzę z moich instrukcji dziennika i ruchu sieciowego, że zwracane dane JSON zmieniają się, a mój model jest aktualizowany, jednak widok się nie zmienia.
Mój komponent wygląda tak:
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());
}
}
A mój widok wygląda następująco:
<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>
Widzę z wyników tego, console.log(this.recentDetections[0].macAddress)
że obiekt latestDetections jest aktualizowany, ale tabela w widoku nigdy się nie zmienia, chyba że ponownie załaduję stronę.
Staram się zobaczyć, co tu robię źle. Czy ktoś może pomóc?
Odpowiedzi:
Może się zdarzyć, że kod w Twojej usłudze w jakiś sposób wyrwie się ze strefy Angulara. To przerywa wykrywanie zmian. To powinno działać:
Aby poznać inne sposoby wywoływania wykrywania zmian, zobacz Ręczne wyzwalanie wykrywania zmian w Angular
Alternatywne sposoby wywoływania wykrywania zmian to
aby natychmiast uruchomić wykrywanie zmian dla bieżącego składnika i jego elementów podrzędnych
aby włączyć bieżący komponent następnym razem, gdy Angular uruchomi wykrywanie zmian
aby uruchomić wykrywanie zmian dla całej aplikacji
źródło
cdRef.detectChanges()
zamiastzone.run()
. Może to być bardziej wydajne, ponieważ nie uruchomi wykrywania zmian w całym drzewie komponentów, tak jakzone.run()
ma to miejsce w przypadku.detectChanges()
wtedy, gdy jakiekolwiek wprowadzone zmiany są lokalne dla składnika i jego elementów podrzędnych. Rozumiem, żedetectChanges()
sprawdzi komponent i jego potomki, alezone.run()
iApplicationRef.tick()
sprawdzi całe drzewo komponentów.@Input()
nie miało sensu ani nie działało.Pierwotnie jest to odpowiedź w komentarzach @Mark Rajcok, ale chcę ją tutaj umieścić jako przetestowane i działające jako rozwiązanie wykorzystujące ChangeDetectorRef , widzę tutaj dobry punkt:
Więc kod musi wyglądać następująco:
Edycja : użycie
.detectChanges()
inside subscibe może spowodować problem. Próba użycia zniszczonego widoku: DetectChangesAby go rozwiązać, musisz
unsubscribe
przed zniszczeniem komponentu, więc pełny kod będzie wyglądał następująco:źródło
Spróbuj użyć
@Input() recentDetections: Array<RecentDetection>;
EDYCJA: Powodem, dla którego
@Input()
jest to ważne, jest to, że chcesz powiązać wartość w pliku maszynopisu / javascript z widokiem (html). Widok zostanie zaktualizowany, jeśli wartość zadeklarowana w@Input()
dekoratorze zostanie zmieniona. Jeślidekorator@Input()
lub@Output()
zostanie zmieniony,ngOnChanges
wyzwolone zostaniezdarzenie-event, a widok zaktualizuje się o nową wartość. Możesz powiedzieć, że@Input()
wola wiąże wartość w obie strony.wyszukaj dane wejściowe dla tego łącza według kątów: słownik, aby uzyskać więcej informacji
EDYCJA: po tym, jak dowiedziałem się więcej o rozwoju Angular 2, doszedłem do wniosku, że robienie
@Input()
naprawdę nie jest rozwiązaniem i jak wspomniano w komentarzach,Jeśli spojrzysz na odpowiedź @ Güntera, jest to dokładniejsze i poprawne rozwiązanie problemu. Nadal zachowam tę odpowiedź tutaj, ale proszę postępuj zgodnie z odpowiedzią Güntera jako poprawną.
źródło
@Input()
słowa kluczowego istnieje powiązanie właściwości , które zapewnia, że wartość jest aktualizowana w widoku. wartość będzie częścią zdarzenia w cyklu życia. Ten post zawiera więcej informacji na temat@Input()
słowa kluczowego@Input()
jest tu zaangażowany ani dlaczego powinien być, a pytanie nie dostarcza wystarczających informacji. W każdym razie dziękuję za cierpliwość :)@Input()
to raczej obejście, które ukrywa źródło problemu. Zagadnienia muszą być związane ze strefami i wykrywaniem zmian.W moim przypadku miałem bardzo podobny problem. Aktualizowałem swój widok w funkcji, która była wywoływana przez komponent nadrzędny, aw komponencie nadrzędnym zapomniałem użyć @ViewChild (NameOfMyChieldComponent). Przez ten głupi błąd straciłem przynajmniej 3 godziny. tj .: nie musiałem używać żadnej z tych metod:
źródło
Zamiast zajmować się strefami i wykrywaniem zmian - pozwól AsyncPipe zająć się złożonością. Spowoduje to umieszczenie obserwowalnej subskrypcji, anulowania subskrypcji (aby zapobiec wyciekom pamięci) i wykrywanie zmian na barkach Angulara.
Zmień swoją klasę tak, aby była obserwowalna, która będzie emitować wyniki nowych żądań:
I zaktualizuj widok, aby używać AsyncPipe:
Chcemy dodać, że lepiej wykonać usługę metodą, która będzie wymagała
interval
argumentacji i:exhaustMap
jak w kodzie powyżej);źródło