我有一个简单的组件,它每隔几秒就调用一次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());
}
}
Run Code Online (Sandbox Code Playgroud)
我的观点看起来像:
<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>
Run Code Online (Sandbox Code Playgroud)
我可以从结果中console.log(this.recentDetections[0].macAddress)看到recentDetections对象正在更新,但视图中的表永远不会更改,除非我重新加载页面.
我很难看到我在这里做错了什么.有人可以帮忙吗?
Gün*_*uer 185
可能是你的服务中的代码以某种方式突破了Angular的区域.这打破了变化检测.这应该工作:
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());
}
}
Run Code Online (Sandbox Code Playgroud)
有关调用更改检测的其他方法,请参阅在Angular中手动触发更改检测
调用变更检测的其他方法是
ChangeDetectorRef.detectChanges()
Run Code Online (Sandbox Code Playgroud)
立即运行当前组件及其子组件的更改检测
ChangeDetectorRef.markForCheck()
Run Code Online (Sandbox Code Playgroud)
在Angular下次运行更改检测时包含当前组件
ApplicationRef.tick()
Run Code Online (Sandbox Code Playgroud)
为整个应用程序运行更改检测
Al-*_*far 27
它最初是来自@Mark Rajcok的评论中的答案,但我想把它放在这里作为一个经过测试并使用ChangeDetectorRef作为解决方案,我在这里看到一个好点:
另一种选择是注入
ChangeDetectorRef和调用cdRef.detectChanges()而不是zone.run().这可能更有效,因为它不会像整个组件树那样运行变更检测zone.run(). - Mark Rajcok
所以代码必须像:
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());
}
}
Run Code Online (Sandbox Code Playgroud)
编辑:使用.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();
}
}
Run Code Online (Sandbox Code Playgroud)
尝试使用 @Input() recentDetections: Array<RecentDetection>;
编辑:
之所以@Input()重要,是因为您希望将typescript/javascript文件中的值绑定到视图(html).如果@Input()更改了使用装饰器声明的值,则视图将自动更新.如果更改了@Input()或者@Output()装饰器,ngOnChanges则会触发-event,并且视图将使用新值更新自身.你可以说@Input()意志双向绑定值.
通过angular:glossary搜索此链接上的输入以获取更多信息
编辑: 在更多地了解Angular 2开发之后,我认为做一个@Input()真的不是解决方案,正如评论中所提到的,
@Input()仅当数据通过组件外部的数据绑定更改(父级中的绑定数据已更改)时才适用,而不是在组件内的代码更改数据时.
如果您看看@Günter的答案,它是解决问题的更准确和正确的解决方案.我仍然会在这里保留这个答案,但请按照Günter的答案作为正确答案.
小智 6
我知道这是一个老问题,但我想分享我的情况。我在一个团队工作,有人将变更检测策略设置为 onPush ,基本上禁用了自动变更检测。不确定这是否有其他帮助,但以防万一
@Component({
selector: 'app-header',
templateUrl: './header.component.html',
styleUrls: ['./header.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
Run Code Online (Sandbox Code Playgroud)
https://angular.io/api/core/ChangeDetectionStrategy
| 归档时间: |
|
| 查看次数: |
165642 次 |
| 最近记录: |