Questão
Qual é a maneira mais elegante de obter @ViewChild
depois que o elemento correspondente no modelo foi mostrado?
Abaixo está um exemplo. Também Plunker disponível.
Modelo:
<div id="layout" *ngIf="display">
<div #contentPlaceholder></div>
</div>
Componente:
export class AppComponent {
display = false;
@ViewChild('contentPlaceholder', {read: ViewContainerRef}) viewContainerRef;
show() {
this.display = true;
console.log(this.viewContainerRef); // undefined
setTimeout(()=> {
console.log(this.viewContainerRef); // OK
}, 1);
}
}
Eu tenho um componente com seu conteúdo oculto por padrão. Quando alguém chama o show()
método, ele se torna visível. No entanto, antes que a detecção de alteração do Angular 2 seja concluída, não posso fazer referência a viewContainerRef
. Normalmente, envolvo todas as ações necessárias, setTimeout(()=>{},1)
como mostrado acima. Existe uma maneira mais correta?
Eu sei que existe uma opção com ngAfterViewChecked
, mas causa muitas chamadas inúteis.
RESPOSTA (Plunker)
angular
angular2-changedetection
sinedsem
fonte
fonte
Respostas:
Use um setter para o ViewChild:
O setter é chamado com uma referência de elemento uma vez que
*ngIf
se tornatrue
.Observe que, no Angular 8, você precisa definir
{ static: false }
, que é uma configuração padrão em outras versões do Agnular:Nota: se contentPlaceholder for um componente, você poderá alterar ElementRef para sua classe de componente:
fonte
contentPlaceholder
é .ElementRef
ViewContainerRef
<div #contentPlaceholder></div>
. Tecnicamente, você pode chamá-lo manualmente como qualquer outro levantador,this.content = someElementRef
mas não vejo por que você desejaria fazer isso.Uma alternativa para superar isso é executar o detector de alterações manualmente.
Você injeta primeiro o
ChangeDetectorRef
:Você o chama depois de atualizar a variável que controla o * ngIf
fonte
onInit()
, então adicionei odetectChanges
antes de chamar qualquer função filho e foi corrigida. (Eu usei tanto a resposta aceita e esta resposta)Angular 8+
Você deve adicionar
{ static: false }
como uma segunda opção para@ViewChild
. Isso faz com que os resultados da consulta sejam resolvidos após a execução da detecção de alterações, permitindo que você@ViewChild
seja atualizado após a alteração do valor.Exemplo:
Exemplo de Stackblitz: https://stackblitz.com/edit/angular-d8ezsn
fonte
<mat-horizontal-stepper *ngIf="viewMode === DialogModeEnum['New']" linear #stepper
,@ViewChild('stepper', {static: true}) private stepper: MatStepper;
ethis.changeDetector.detectChanges();
e ele ainda não funciona.As respostas acima não funcionaram para mim porque no meu projeto, o ngIf está em um elemento de entrada. Eu precisava de acesso ao atributo nativeElement para focar na entrada quando ngIf for true. Parece não haver atributo nativeElement em ViewContainerRef. Aqui está o que eu fiz (seguindo a documentação do @ViewChild ):
Eu usei setTimeout antes de focar porque o ViewChild leva um segundo para ser atribuído. Caso contrário, seria indefinido.
fonte
setTimeout
?I usually wrap all required actions into setTimeout(()=>{},1) as shown above. Is there a more correct way?
Como foi mencionado por outros, a solução mais rápida e rápida é usar [oculto] em vez de * ngSe assim o componente for criado, mas não visível, para que você possa ter acesso a ele, embora possa não ser o mais eficiente maneira.
fonte
Isso pode funcionar, mas não sei se é conveniente para o seu caso:
fonte
ngAfterViewInit()
vez dengOnInit()
. Presumi queviewContainerRefs
já foi inicializado, mas ainda não contém itens. Parece que me lembrei disso errado.AfterViewInit
realmente funciona. Eu removi todos os meus comentários para não confundir as pessoas. Aqui está um Plunker em funcionamento: plnkr.co/edit/myu7qXonmpA2hxxU3SLB?p=previewconst
parâmetro deViewChild
Outro "truque" rápido (solução fácil) é apenas usar a tag [hidden] em vez de * ngIf, é importante saber que nesse caso o Angular cria o objeto e o pinta na classe: oculto é por isso que o ViewChild funciona sem problemas . Portanto, é importante ter em mente que você não deve ocultar itens pesados ou caros que possam causar problemas de desempenho
fonte
Meu objetivo era evitar métodos hacky que assumissem algo (por exemplo, setTimeout) e acabei implementando a solução aceita com um pouco de sabor RxJS no topo:
Meu cenário: eu queria disparar uma ação em um
@ViewChild
elemento dependendo do roteadorqueryParams
. Como um agrupamento*ngIf
é falso até que a solicitação HTTP retorne os dados, a inicialização do@ViewChild
elemento ocorre com um atraso.Como funciona:
combineLatest
emite um valor pela primeira vez somente quando cada um dos Observáveis fornecidos emite o primeiro valor desde que o momentocombineLatest
foi assinado. Meu AssuntotabSetInitialized
emite um valor quando o@ViewChild
elemento está sendo definido. Com isso, atraso a execução do códigosubscribe
até que ele se*ngIf
torne positivo e@ViewChild
seja inicializado.Claro que não se esqueça de cancelar a inscrição no ngOnDestroy, eu faço isso usando o
ngUnsubscribe
Assunto:fonte
Uma versão simplificada, tive um problema semelhante ao usar o SDK JS do Google Maps.
Minha solução foi extrair o
div
eViewChild
para o seu próprio componente filho, que quando usado no componente pai foi capaz de ser oculto / exibido usando um*ngIf
.Antes
HomePageComponent
ModeloHomePageComponent
ComponenteDepois de
MapComponent
ModeloMapComponent
ComponenteHomePageComponent
ModeloHomePageComponent
Componentefonte
No meu caso, eu precisava carregar um módulo inteiro apenas quando a div existia no modelo, significando que a saída estava dentro de um ngif. Dessa maneira, sempre que o angular detectou o elemento #geolocalisationOutlet, ele criou o componente dentro dele. O módulo carrega apenas uma vez também.
fonte
Eu acho que o uso do adiamento da lodash faz muito sentido, especialmente no meu caso, onde meu tubo
@ViewChild()
estava dentroasync
fonte
Trabalhando no Angular 8 Não há necessidade de importar o ChangeDector
ngIf permite que você não carregue o elemento e evite adicionar mais estresse ao seu aplicativo. Veja como eu o executei sem o ChangeDetector
Então, quando eu altero meu valor ngIf para ser verdade, eu usaria setTimeout como este para aguardar apenas o próximo ciclo de alteração:
Isso também me permitiu evitar o uso de bibliotecas ou importações adicionais.
fonte
para o Angular 8 - uma mistura de verificação nula e
@ViewChild
static: false
hackerspara um controle de paginação aguardando dados assíncronos
fonte
Funciona para mim se eu usar o ChangeDetectorRef no Angular 9
fonte