Por que o componente deste simples puxão
@Component({
selector: 'my-app',
template: `<div>I'm {{message}} </div>`,
})
export class App {
message:string = 'loading :(';
ngAfterViewInit() {
this.updateMessage();
}
updateMessage(){
this.message = 'all done loading :)'
}
}
jogando:
EXCEÇÃO: A expressão 'Estou {{message}} no App @ 0: 5' foi alterada após a verificação. Valor anterior: 'Estou carregando :('. Valor atual: 'Estou pronto para carregar :)' em [Estou {{message}} no App @ 0: 5]
quando tudo o que estou fazendo é atualizar uma ligação simples quando minha visualização é iniciada?
typescript
angular
chamou moore
fonte
fonte
ExpressionChangedAfterItHasBeenCheckedError
erro explica o comportamento em grandes detalhes.ChangeDetectionStrategy
ao usar odetectChanges()
stackoverflow.com/questions/39787038/…Respostas:
Conforme declarado por drewmoore, a solução adequada nesse caso é acionar manualmente a detecção de alterações para o componente atual. Isso é feito usando o
detectChanges()
método doChangeDetectorRef
objeto (importado deangular2/core
) ou seumarkForCheck()
método, que também atualiza qualquer componente pai. Exemplo relevante :Aqui também estão Plunkers demonstrando as abordagens ngOnInit , setTimeout e enableProdMode , apenas por precaução.
fonte
cdr : any
ou algo assim sob amessage
declaração. Só preocupado que eu perdi alguma coisa?Primeiro, observe que essa exceção só será lançada quando você estiver executando seu aplicativo no modo dev (que é o caso por padrão no beta-0): se você chamar
enableProdMode()
ao inicializar o aplicativo, ele não será lançado ( consulte plunk atualizado ).Segundo, não faça isso porque essa exceção está sendo lançada por uma boa razão: em resumo, quando no modo dev, todas as rodadas de detecção de alterações são seguidas imediatamente por uma segunda rodada que verifica se nenhuma ligação foi alterada desde o final da primeira, pois isso indica que as alterações estão sendo causadas pela própria detecção de alterações.
Na sua conexão, a ligação
{{message}}
é alterada pela sua chamada parasetMessage()
, o que ocorre nongAfterViewInit
gancho, que ocorre como parte da curva inicial de detecção de alterações. Porém, isso por si só não é problemático - o problema é quesetMessage()
altera a ligação, mas não aciona uma nova rodada de detecção de alterações, o que significa que essa alteração não será detectada até que alguma futura rodada de detecção de alterações seja acionada em outro lugar.O ponto principal: tudo o que altera uma ligação precisa acionar uma rodada de detecção de alterações quando isso ocorre.
Atualize em resposta a todos os pedidos de um exemplo de como fazer isso : a solução do @ Tycho funciona, assim como os três métodos na resposta que o @MarkRajcok apontou. Mas, francamente, todos eles se sentem feios e errados para mim, como o tipo de hacks que nos acostumamos a usar no ng1.
Para ter certeza, existem ocasionais circunstâncias em que estes hacks são apropriados, mas se você estiver usando-os em qualquer coisa mais do que um muito ocasional, é um sinal de que você está lutando contra a estrutura, em vez de abraçar plenamente a sua natureza reativa.
IMHO, um "Angular2 maneira" mais idiomática de abordar isso é algo ao longo das linhas de: ( plunk )
fonte
detectChanges()
.updateMessage
, nãosetMessage
ngAfterViewChecked()
trabalhou para mim:fonte
Corrigi isso adicionando ChangeDetectionStrategy do núcleo angular.
fonte
CheckOnce
( documentação )this.cdr.detectChanges();
atrás do que estiver tentando carregar. Porque pode ser porque a detecção de alterações não está sendo desencadeadaVocê não pode usar
ngOnInit
porque acabou de alterar a variável de membromessage
?Se você deseja acessar uma referência a um componente filho
@ViewChild(ChildComponent)
, é necessário aguardá-longAfterViewInit
.Uma correção suja é chamar
updateMessage()
o próximo loop de evento com, por exemplo, setTimeout.fonte
Por isso, eu tentei as respostas acima, muitas não funcionam na versão mais recente do Angular (6 ou posterior)
Estou usando o controle de material que exigiu alterações após a primeira ligação.
Adicionando minha resposta, isso ajuda alguns a resolver problemas específicos.
fonte
O artigo Tudo o que você precisa saber sobre o
ExpressionChangedAfterItHasBeenCheckedError
erro explica o comportamento em grandes detalhes.O problema com a configuração é que
ngAfterViewInit
o gancho do ciclo de vida é executado após as atualizações do DOM processadas pela detecção de alterações. E você está efetivamente alterando a propriedade usada no modelo neste gancho, o que significa que o DOM precisa ser renderizado novamente:e isso exigirá outro ciclo de detecção de alterações e o Angular, por design, executa apenas um ciclo de resumo.
Você basicamente tem duas alternativas para corrigi-lo:
atualizar a propriedade de forma assíncrona ou usando
setTimeout
,Promise.then
ou assíncrono observável referenciada no modeloexecute a atualização da propriedade em um gancho antes da atualização do DOM - ngOnInit, ngDoCheck, ngAfterContentInit, ngAfterContentChecked.
fonte
Você só precisa atualizar sua mensagem no gancho do ciclo de vida correto, neste caso, é em
ngAfterContentChecked
vez dengAfterViewInit
, porque em ngAfterViewInit, uma verificação da mensagem variável foi iniciada, mas ainda não foi encerrada.consulte: https://angular.io/docs/ts/latest/guide/lifecycle-hooks.html#!#afterview
então o código será apenas:
veja a demonstração de trabalho no Plunker.
fonte
@ViewChildren()
contador de comprimento que foi preenchido por um Observable. Esta foi a única solução que funcionou para mim!ngAfterContentChecked
eChangeDetectorRef
de cima funcionou para mim. EmngAfterContentChecked
chamado -this.cdr.detectChanges();
Este erro está ocorrendo porque o valor existente está sendo atualizado imediatamente após a inicialização. Portanto, se você atualizar um novo valor depois que o valor existente for renderizado no DOM, ele funcionará bem.Como mencionado neste artigo Depuração angular "A expressão foi alterada após a verificação"
por exemplo, você pode usar
}
ou
Como você pode ver, não mencionei o tempo no método setTimeout. Como é uma API fornecida pelo navegador, não uma API JavaScript, portanto, isso será executado separadamente na pilha do navegador e aguardará até que os itens da pilha de chamadas sejam concluídos.
Como o conceito de API do navegador é explicado por Philip Roberts em um dos vídeos do Youtube (o que o hack é o loop de eventos?).
fonte
Você também pode fazer sua chamada para updateMessage () no método ngOnInt () -, pelo menos funciona para mim
No RC1, isso não aciona a exceção
fonte
Você também pode criar um timer usando a
Observable.timer
função rxjs e atualizar a mensagem na sua assinatura:fonte
Ele gera um erro porque seu código é atualizado quando ngAfterViewInit () é chamado. Significa que seu valor inicial foi alterado quando o ngAfterViewInit ocorre. Se você chamar isso em ngAfterContentInit () , ele não emitirá um erro.
fonte
Não pude comentar no post de @Biranchi, já que não tenho reputação suficiente, mas isso resolveu o problema para mim.
Uma coisa a notar! Se a adição de changeDetection: ChangeDetectionStrategy.OnPush no componente não funcionou, e é um componente filho (componente burro), tente adicioná-lo também ao pai.
Isso corrigiu o bug, mas eu me pergunto quais são os efeitos colaterais disso.
fonte
Eu recebi um erro semelhante ao trabalhar com a tabela de dados. O que acontece é quando você usa * ngFor dentro de outra * ngFor a tabela de dados lança esse erro à medida que intercepta o ciclo de alterações angulares. Portanto, em vez de usar a tabela de dados dentro da tabela de dados, use uma tabela regular ou substitua mf.data pelo nome da matriz. Isso funciona bem.
fonte
Eu acho que uma solução mais simples seria a seguinte:
(static working: boolean)
na classe em que essa função existe e toda vez que você chamar a função, simplesmente faça com que ela seja verdadeira como desejar. Dentro da função, se o valor do trabalho for verdadeiro, basta retornar imediatamente sem fazer nada. caso contrário, execute a tarefa desejada. Altere essa variável para false assim que a tarefa for concluída, ou seja, no final da linha de códigos ou dentro do método de inscrição, quando terminar de atribuir valores!fonte