Eu tenho um componente simples que chama uma API REST a cada poucos segundos e recebe de volta alguns dados JSON. Posso ver nas minhas instruções de log e no tráfego de rede que os dados JSON retornados estão mudando e meu modelo está sendo atualizado, no entanto, a exibição não está mudando.
Meu componente se parece com:
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());
}
}
E minha visão se parece com:
<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)
Pude ver pelos resultados que o objeto recentDetections está sendo atualizado, mas a tabela na exibição nunca muda, a menos que eu recarregue a página.
Estou lutando para ver o que estou fazendo de errado aqui. Alguém pode ajudar?
Respostas:
Pode ser que o código em seu serviço de alguma forma saia da zona da Angular. Isso interrompe a detecção de alterações. Isso deve funcionar:
Para outras maneiras de chamar a detecção de alterações, consulte Disparando manualmente a detecção de alterações no Angular.
Formas alternativas de chamar a detecção de alterações são
executar imediatamente a detecção de alterações para o componente atual e seus filhos
para incluir o componente atual na próxima vez que o Angular executar a detecção de alterações
executar a detecção de alterações para todo o aplicativo
fonte
cdRef.detectChanges()
vez dezone.run()
. Isso poderia ser mais eficiente, pois não executará a detecção de alterações em toda a árvore de componentes, comozone.run()
faz.detectChanges()
se alguma alteração feita for local para o componente e seus descendentes. Meu entendimento é quedetectChanges()
irá verificar o componente e seus descendentes, maszone.run()
eApplicationRef.tick()
irá verificar toda a árvore de componentes.@Input()
não fazia sentido nem funcionava.É originalmente uma resposta nos comentários de @Mark Rajcok, mas quero colocá-lo aqui como testado e funcionou como uma solução usando o ChangeDetectorRef , vejo um bom ponto aqui:
Portanto, o código deve ser como:
Editar : o uso de
.detectChanges()
subscibe interno pode levar ao problema Tente usar uma exibição destruída: detectChangesPara resolvê-lo, você precisa
unsubscribe
antes de destruir o componente, para que o código completo seja como:fonte
Tente usar
@Input() recentDetections: Array<RecentDetection>;
EDIT: A razão pela qual
@Input()
é importante é porque você deseja vincular o valor no arquivo typescript / javascript à visualização (html). A visualização será atualizada se um valor declarado com o@Input()
decorador for alterado. Se um@Input()
ou@Output()
decorador for alterado, umngOnChanges
-event será acionado e a visualização será atualizada com o novo valor. Você pode dizer que o@Input()
bidirecional vinculará o valor.procure por Input neste link por angular: glossário para mais informações
EDIT: depois de aprender mais sobre o desenvolvimento do Angular 2, percebi que fazer
@Input()
realmente não é a solução e, conforme mencionado nos comentários,Se você der uma olhada na resposta da Günter, é uma solução mais precisa e correta para o problema. Ainda vou manter essa resposta aqui, mas siga a resposta de Günter como a correta.
fonte
@Input()
palavra-chave, e essa ligação garante que o valor seja atualizado na exibição. o valor fará parte de um evento do ciclo de vida. Este post contém mais algumas informações sobre a@Input()
palavra-chave@Input()
está envolvido aqui ou por que deveria estar e a pergunta não fornece informações suficientes. Obrigado por você paciência de qualquer maneira :)@Input()
é mais uma solução alternativa que oculta a raiz do problema. Os problemas devem estar relacionados às zonas e à detecção de alterações.No meu caso, eu tive um problema muito semelhante. Eu estava atualizando minha visão dentro de uma função que estava sendo chamada por um componente pai, e no meu componente pai esqueci de usar @ViewChild (NameOfMyChieldComponent). Perdi pelo menos 3 horas apenas por esse erro estúpido. ou seja: eu não precisei usar nenhum desses métodos:
fonte
Em vez de lidar com zonas e detecção de alterações - deixe o AsyncPipe lidar com a complexidade. Isso colocará assinatura observável, cancelamento de assinatura (para evitar vazamentos de memória) e detecção de alterações nos ombros angulares.
Mude sua classe para tornar um observável, que emitirá resultados de novas solicitações:
E atualize sua visualização para usar o AsyncPipe:
Quer acrescentar que é melhor fazer um serviço com um método que terá
interval
argumentos e:exhaustMap
como no código acima);fonte