Angular 2 - просмотр не обновляется после изменения модели
У меня есть простой компонент, который вызывает REST api каждые несколько секунд и получает обратно некоторые данные JSON. Из моих операторов журналов и сетевого трафика видно, что данные JSON, возвращаемые, меняются, а моя модель обновляется, однако представление не меняется.
Мой компонент выглядит так:
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());
}
}
И мое мнение выглядит так:
<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>
Из результатов console.log(this.recentDetections[0].macAddress)
видно, что объект recentDetections обновляется, но таблица в представлении никогда не изменяется, если я не перезагружаю страницу.
Я изо всех сил пытаюсь понять, что я делаю неправильно. Может ли кто-нибудь помочь?
Ответы
Ответ 1
Возможно, код в вашем сервисе как-то вырывается из зоны углов. Это нарушает обнаружение изменений. Это должно работать:
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());
}
}
Для других способов вызова обнаружения изменений см. Запуск Angular2 вручную изменить обнаружение
Альтернативные способы вызова обнаружения изменений:
ChangeDetectorRef.detectChanges()
для немедленного запуска обнаружения изменений для текущего компонента и его childrend
ChangeDetectorRef.markForCheck()
включить текущий компонент в следующий раз, когда Angular запускает обнаружение изменений
ApplicationRef.tick()
для запуска обнаружения изменений для всего приложения
Ответ 2
Попробуйте использовать @Input() recentDetections: Array<RecentDetection>;
EDIT:
Причина, почему @Input()
важна, заключается в том, что вы хотите связать значение в файле typescript/javascript с представлением (html). Представление обновится, если значение, объявленное с помощью декоратора @Input()
, будет изменено. Если изменится декоратор @Input()
или @Output()
, появится ngOnChanges
-event, и представление обновится с новым значением. Вы можете сказать, что @Input()
будет двусторонне связывать значение.
найти вход по этой ссылке angular: glossary для получения дополнительной информации
EDIT:, узнав больше о Angular 2, я понял, что выполнение @Input()
действительно не является решением, и, как упоминалось в комментариях,
@Input()
применяется только тогда, когда данные изменяются привязкой данных из вне компонента (связанные данные в родительском измененном), а не когда данные изменяются из кода внутри компонента.
Если вы посмотрите @Günter ответ, это более точное и правильное решение проблемы. Я по-прежнему буду держать этот ответ здесь, но, пожалуйста, следуйте правилу ответа Гюнтера.
Ответ 3
Первоначально это ответ в комментариях от @Mark Rajcok, но я хочу разместить его здесь как проверенный и работающий как решение, используя ChangeDetectorRef, я вижу здесь хороший момент:
Другой альтернативой является ввод ChangeDetectorRef
и вызов cdRef.detectChanges()
вместо zone.run()
. Это может быть больше эффективный, поскольку он не будет запускать обнаружение изменений на протяжении всего дерево компонентов, например zone.run()
. - Марк Райкок
Таким образом, код должен выглядеть следующим образом:
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());
}
}
Edit:
Использование .detectChanges()
внутри subscibe может привести к выпуску Попытка использовать разрушенное представление: detectChanges
Чтобы решить проблему, вам нужно unsubscribe
, прежде чем уничтожить компонент, поэтому полный код будет выглядеть следующим образом:
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();
}
}