Como forçar a re-renderização de um componente no Angular 2?

181

Como forçar a re-renderização de um componente no Angular 2? Para propósitos de depuração trabalhando com o Redux, eu gostaria de forçar um componente a renderizar novamente sua visualização, isso é possível?

born2net
fonte
O que você quer dizer com "re-renderização". Atualizar as ligações?
Günter Zöchbauer 30/01
Apenas uma pergunta rápida por que você precisa forçar a nova renderização?
Tuong Le

Respostas:

217

A renderização acontece após a detecção de alterações. Para forçar a detecção de alterações, para que os valores das propriedades dos componentes que foram alterados sejam propagados para o DOM (e o navegador renderize essas alterações na exibição), aqui estão algumas opções:

  • ApplicationRef.tick () - semelhante ao 1 angular $rootScope.$digest()- ou seja, verifique a árvore de componentes completa
  • NgZone.run (retorno de chamada) - semelhante a $rootScope.$apply(callback)- ou seja, avalie a função de retorno de chamada dentro da zona Angular 2. Eu acho, mas não tenho certeza, que isso acaba verificando a árvore de componentes completa após a execução da função de retorno de chamada.
  • ChangeDetectorRef.detectChanges () - semelhante a $scope.$digest()- ou seja, verifique apenas este componente e seus filhos

Você precisará importar e injetar ApplicationRef , NgZoneou ChangeDetectorRefem seu componente.

Para o seu cenário específico, eu recomendaria a última opção se apenas um único componente tiver sido alterado.

Mark Rajcok
fonte
1
Algum código de trabalho no ChangeDetectorRef para a versão final do angular2? Agora, estou enfrentando uma situação em que a exibição não é atualizada após a solicitação de postagem de um http para criar um novo usuário e, em seguida, com êxito, enviando o novo objeto para a lista de usuários antiga existente (usada para iterar na exibição). Muito estranho isso this is the first time I am facing an update not working in ng2. A estratégia de detecção de alterações é padrão, então eu sei que não estraguei a estratégia de detecção de alterações.
Gary
1
@ Gary, você deve postar uma nova pergunta e incluir seu componente e seu código de serviço (o ideal é incluir um plunker mínimo demonstrando o problema). Um problema comum que eu vi não está usando o thiscontexto apropriado no retorno de chamada POST.
Mark Rajcok
Você sabe se posso acionar manualmente os canos sempre que faço uma alteração? Eu tentei acionar a detecção de alterações, mas o tubo não é atualizado ... Eu também tentei com pure:falseo tubo. Funciona, mas é muito caro (ineficiente) para o meu caso de uso.
Nchen
1
@ ncohen, não conheço nenhuma maneira de acionar manualmente uma atualização de tubulação. Você pode usar um canal puro e alterar a referência do objeto sempre que desejar acionar uma atualização. Isso é discutido na seção "Pure Pipes" do documento Pipes . Dependendo do seu caso de uso, convém usar uma propriedade de componente em vez de um pipe. Essa técnica é discutida brevemente no final do documento do Pipes.
Mark Rajcok
1
@ N-ate, todos os links são corrigidos.
precisa saber é o seguinte
47

tx, encontrei a solução que eu precisava:

  constructor(private zone:NgZone) {
    // enable to for time travel
    this.appStore.subscribe((state) => {
        this.zone.run(() => {
            console.log('enabled time travel');
        });
    });

executar zone.run forçará o componente a renderizar novamente

born2net
fonte
6
o que é appStore nesse contexto - que tipo de variável e seu tipo? parece ser observável ... mas o meu observável está dentro do componente que eu quero atualizar com o clique de um botão ... e eu não sei como acessar um método / variável de componente filho do pai / local atual
Abdeali Chandanwala
28

Abordagem ChangeDetectorRef

import { Component, OnInit, ChangeDetectorRef } from '@angular/core';

export class MyComponent {

    constructor(private cdr: ChangeDetectorRef) { }

    selected(item: any) {
        if (item == 'Department')
            this.isDepartment = true;
        else
            this.isDepartment = false;
        this.cdr.detectChanges();
    }

}
Feng Zhang
fonte
14

Eu forço a recarregar meu componente usando * ngIf.

Todos os componentes dentro do meu contêiner retornam aos ganchos do ciclo de vida completo.

No modelo:

<ng-container *ngIf="_reload">
    components here 
</ng-container>

Então no arquivo ts:

public _reload = true;

private reload() {
    setTimeout(() => this._reload = false);
    setTimeout(() => this._reload = true);
}
loonis
fonte
Obrigado por isso, @loonis! Eu senti que isso deveria funcionar, e eu tinha tudo, exceto o setTimeout(). Agora o meu está trabalhando com uma solução simples e leve!
LHM
se eu pudesse votar isso mais de 10000 vezes ..
Yazan Khalaileh
Uma coisa a observar - o contêiner que desaparecer e aparecer novamente pode causar alterações de tamanho e a página pode piscar
ghosh
9

Outras respostas aqui fornecem soluções para acionar ciclos de detecção de alterações que atualizarão a exibição do componente (que não é a mesma que re-renderização completa).

Completa re-render, o que destruiria e reinicializar componente (chamando todos os ganchos de ciclo de vida e reconstruir vista) pode ser feito usando ng-template, ng-containere ViewContainerRefna seguinte forma:

<div>
  <ng-container #outlet >
  </ng-container>
</div>

<ng-template #content>
  <child></child>
</ng-template>

Em seguida, no componente com referência a ambos #outlete #contentpodemos limpar o conteúdo dos pontos de venda e inserir outra instância do componente filho:

@ViewChild("outlet", {read: ViewContainerRef}) outletRef: ViewContainerRef;
@ViewChild("content", {read: TemplateRef}) contentRef: TemplateRef<any>;

private rerender() {
    this.outletRef.clear();
    this.outletRef.createEmbeddedView(this.contentRef);
}

Além disso, o conteúdo inicial deve ser inserido no AfterContentInitgancho:

ngAfterContentInit() {
    this.outletRef.createEmbeddedView(this.contentRef);
}

Solução de trabalho completa pode ser encontrada aqui https://stackblitz.com/edit/angular-component-rerender .

Pavel Gurecki
fonte
1

ChangeDetectorRef.detectChanges() geralmente é a maneira mais focada de fazer isso. ApplicationRef.tick()geralmente é uma abordagem excessiva da marreta.

Para usar ChangeDetectorRef.detectChanges(), você precisará disso na parte superior do seu componente:

import {  ChangeDetectorRef } from '@angular/core';

... então, geralmente você alias que quando você injeta em seu construtor como este:

constructor( private cdr: ChangeDetectorRef ) { ... }

Então, no local apropriado , você chama assim:

this.cdr.detectChanges();

Onde você liga ChangeDetectorRef.detectChanges()pode ser altamente significativo. Você precisa entender completamente o ciclo de vida e exatamente como seu aplicativo está funcionando e processando seus componentes. Não há substituto aqui para fazer completamente sua lição de casa e garantir que você entenda o ciclo de vida angular de dentro para fora. Então, depois de entender isso, você pode usá-lo ChangeDetectorRef.detectChanges()adequadamente (às vezes é muito fácil entender onde você deve usá-lo, outras vezes pode ser muito complexo).

Chris Halcrow
fonte