У меня есть простой компонент, который вызывает 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)
что объект latestDetections обновляется, но таблица в представлении никогда не изменяется, пока я не перезагружу страницу.
Я изо всех сил пытаюсь понять, что я здесь делаю не так. Кто-нибудь может помочь?
Ответы:
Возможно, код в вашем сервисе каким-то образом вырывается из зоны Angular. Это нарушает обнаружение изменений. Это должно работать:
Чтобы узнать о других способах вызова обнаружения изменений, см. Запуск обнаружения изменений вручную в Angular.
Альтернативные способы вызова обнаружения изменений:
для немедленного запуска обнаружения изменений для текущего компонента и его дочерних элементов
чтобы включить текущий компонент в следующий раз, когда Angular выполнит обнаружение изменений
запустить обнаружение изменений для всего приложения
источник
cdRef.detectChanges()
вместоzone.run()
. Это могло бы быть более эффективным, поскольку оно не будет запускать обнаружение изменений по всему дереву компонентов, как этоzone.run()
делает.detectChanges()
случае, если любые вносимые вами изменения являются локальными для компонента и его потомков. Насколько я понимаю, этоdetectChanges()
будет проверять компонент и его потомки, ноzone.run()
иApplicationRef.tick()
все дерево компонентов.@Input()
не имело смысла и не работало.Изначально это ответ в комментариях от @Mark Rajcok, но я хочу разместить его здесь как протестированное и работающее как решение с использованием ChangeDetectorRef , я вижу здесь хороший момент:
Итак, код должен быть таким:
Изменить : использование
.detectChanges()
внутри подписки может привести к проблеме Попытка использовать уничтоженное представление: detectChangesЧтобы решить эту проблему, вам нужно
unsubscribe
перед тем, как уничтожить компонент, поэтому полный код будет примерно таким:источник
Попробуй использовать
@Input() recentDetections: Array<RecentDetection>;
РЕДАКТИРОВАТЬ: причина
@Input()
важности заключается в том, что вы хотите привязать значение в файле typescript / javascript к представлению (html). Представление обновится, если значение, объявленное с помощью@Input()
декоратора, изменится. Еслидекоратор@Input()
или@Output()
изменяется,ngOnChanges
-event запускается, и представление обновляется с новым значением. Вы можете сказать, что значение@Input()
будет двусторонним.поиск Input по этой ссылке с помощью angular: глоссарий для получения дополнительной информации
РЕДАКТИРОВАТЬ: узнав больше о разработке Angular 2, я решил, что это
@Input()
действительно не решение, и, как упоминалось в комментариях,Если вы посмотрите ответ @ Günter, это более точное и правильное решение проблемы. Я все равно сохраню этот ответ здесь, но, пожалуйста, следуйте ответу Гюнтера как правильному.
источник
@Input()
ключевого слова прикрепляется привязка свойства , и эта привязка гарантирует, что значение обновляется в представлении. значение будет частью события жизненного цикла. Этот пост содержит дополнительную информацию о@Input()
ключевом слове@Input()
здесь участвует или почему это должно быть, и вопрос не дает достаточно информации. В любом случае спасибо за терпение :)@Input()
это скорее обходной путь, который скрывает корень проблемы. Проблемы должны быть связаны с зонами и обнаружением изменений.В моем случае у меня была очень похожая проблема. Я обновлял свое представление внутри функции, которая вызывалась родительским компонентом, а в моем родительском компоненте я забыл использовать @ViewChild (NameOfMyChieldComponent). Я потерял как минимум 3 часа только из-за этой глупой ошибки. то есть: мне не нужно было использовать ни один из этих методов:
источник
Вместо того, чтобы иметь дело с зонами и обнаружением изменений - позвольте AsyncPipe справиться со сложностью. Это поместит наблюдаемую подписку, отказ от подписки (для предотвращения утечек памяти) и обнаружение изменений на плечах Angular.
Измените свой класс, чтобы сделать наблюдаемым, который будет выдавать результаты новых запросов:
И обновите свое представление, чтобы использовать AsyncPipe:
Хочу добавить, что лучше сделать сервис с методом, который будет принимать
interval
аргументы, и:exhaustMap
как в коде выше);источник