Estou tentando aprender Angular 2.
Gostaria de acessar um componente filho de um componente pai usando a Anotação @ViewChild .
Aqui estão algumas linhas de código:
Em BodyContent.ts eu tenho:
import {ViewChild, Component, Injectable} from 'angular2/core';
import {FilterTiles} from '../Components/FilterTiles/FilterTiles';
@Component({
selector: 'ico-body-content'
, templateUrl: 'App/Pages/Filters/BodyContent/BodyContent.html'
, directives: [FilterTiles]
})
export class BodyContent {
@ViewChild(FilterTiles) ft:FilterTiles;
public onClickSidebar(clickedElement: string) {
console.log(this.ft);
var startingFilter = {
title: 'cognomi',
values: [
'griffin'
, 'simpson'
]}
this.ft.tiles.push(startingFilter);
}
}
enquanto em FilterTiles.ts :
import {Component} from 'angular2/core';
@Component({
selector: 'ico-filter-tiles'
,templateUrl: 'App/Pages/Filters/Components/FilterTiles/FilterTiles.html'
})
export class FilterTiles {
public tiles = [];
public constructor(){};
}
Finalmente, aqui os modelos (como sugerido nos comentários):
BodyContent.html
<div (click)="onClickSidebar()" class="row" style="height:200px; background-color:red;">
<ico-filter-tiles></ico-filter-tiles>
</div>
FilterTiles.html
<h1>Tiles loaded</h1>
<div *ngFor="#tile of tiles" class="col-md-4">
... stuff ...
</div>
O modelo FilterTiles.html foi carregado corretamente na tag ico-filter-tiles (na verdade, eu consigo ver o cabeçalho).
Nota: a classe BodyContent é injetada dentro de outro modelo (Body) usando DynamicComponetLoader: dcl.loadAsRoot (BodyContent, '# ico-bodyContent', injetor):
import {ViewChild, Component, DynamicComponentLoader, Injector} from 'angular2/core';
import {Body} from '../../Layout/Dashboard/Body/Body';
import {BodyContent} from './BodyContent/BodyContent';
@Component({
selector: 'filters'
, templateUrl: 'App/Pages/Filters/Filters.html'
, directives: [Body, Sidebar, Navbar]
})
export class Filters {
constructor(dcl: DynamicComponentLoader, injector: Injector) {
dcl.loadAsRoot(BodyContent, '#ico-bodyContent', injector);
dcl.loadAsRoot(SidebarContent, '#ico-sidebarContent', injector);
}
}
O problema é que, quando tento gravar ft
no log do console, recebo undefined
e, é claro, recebo uma exceção ao tentar enviar algo para dentro da matriz "tiles": 'nenhum bloco de propriedades para "indefinido"' .
Mais uma coisa: o componente FilterTiles parece estar carregado corretamente, pois consigo ver o modelo html.
Alguma sugestão? obrigado
fonte
ft
não seria definido no construtor, mas em um manipulador de eventos de clique já seria definido.loadAsRoot
, que tem um problema conhecido com a detecção de alterações. Apenas para ter certeza de tentar usarloadNextToLocation
ouloadIntoLocation
.loadAsRoot
. Uma vez que eu substituí comloadIntoLocation
o problema foi resolvido. Se você fizer seu comentário como resposta, posso marcá-lo como aceito.Respostas:
Eu tive um problema semelhante e pensei em publicar caso alguém cometesse o mesmo erro. Primeiro, uma coisa a considerar é
AfterViewInit
; você precisa aguardar a inicialização da visualização antes de poder acessar sua@ViewChild
. No entanto, meu@ViewChild
ainda estava retornando nulo. O problema foi meu*ngIf
. A*ngIf
diretiva estava matando meu componente de controles, então não pude fazer referência a ela.Espero que ajude.
EDIT
Como mencionado por @Ashg abaixo , uma solução é usar em
@ViewChildren
vez de@ViewChild
.fonte
ngClass
+hidden
classe em vez dengIf
. Isso funcionou. Obrigado!O problema mencionado anteriormente é o
ngIf
que está causando a indefinição da exibição. A resposta é usar emViewChildren
vez deViewChild
. Eu tive um problema semelhante em que não queria que uma grade fosse exibida até que todos os dados de referência tivessem sido carregados.html:
Código do componente
Aqui estamos usando o
ViewChildren
qual você pode ouvir as alterações. Nesse caso, qualquer criança com a referência#searchGrid
. Espero que isto ajude.fonte
this.SearchGrid
propriedades que você deve usar sintaxe comosetTimeout(()=>{ ///your code here }, 1);
a Exceção evitar: Expression mudou depois que foi verificada*ngIf
funciona e, depois da renderização, podemos salvar um ElementRef dos componentes dinâmicos.Você pode usar um setter para
@ViewChild()
Se você tiver um wrapper ngIf, o setter será chamado com indefinido e, novamente, com uma referência quando o ngIf permitir que ele seja renderizado.
Meu problema era outra coisa. Eu não incluí o módulo que contém meus "FilterTiles" nos meus app.modules. O modelo não gerou um erro, mas a referência sempre foi indefinida.
fonte
FilterTiles
. Eu já encontrei esse problema por esse motivo antes.@ViewChild('paginator', {static: false})
Isso funcionou para mim.
Meu componente chamado 'my-component', por exemplo, foi exibido usando * ngIf = "showMe" da seguinte forma:
Portanto, quando o componente é inicializado, o componente ainda não é exibido até que "showMe" seja verdadeiro. Assim, minhas referências @ViewChild eram todas indefinidas.
Foi aqui que usei o @ViewChildren e o QueryList que ele retorna. Consulte o artigo angular sobre QueryList e uma demonstração de uso do @ViewChildren .
Você pode usar o QueryList que o @ViewChildren retorna e se inscrever em quaisquer alterações nos itens referenciados usando o rxjs, como mostrado abaixo. O @ViewChild não tem essa capacidade.
Espero que isso ajude alguém a economizar tempo e frustração.
fonte
.take(1).subscribe()
, mas excelente resposta, muito obrigado!Minha solução alternativa era usar em
[style.display]="getControlsOnStyleDisplay()"
vez de*ngIf="controlsOn"
. O bloco está lá, mas não é exibido.fonte
O que resolveu o meu problema foi garantir que
static
estava definido comofalse
.Com
static
desativada, a@ViewChild
referência é atualizada pelo Angular quando a*ngIf
diretiva é alterada.fonte
Minha solução para isso era substituir
*ngIf
com[hidden]
. A desvantagem foi que todos os componentes filhos estavam presentes no código DOM. Mas trabalhou para minhas necessidades.fonte
No meu caso, eu tinha um setter de variável de entrada usando o
ViewChild
, eViewChild
estava dentro de uma*ngIf
diretiva; portanto, o setter estava tentando acessá-lo antes da*ngIf
renderização (funcionaria bem sem o*ngIf
, mas não funcionaria se estivesse sempre definido como verdadeiro com*ngIf="true"
).Para resolver, usei o Rxjs para garantir que qualquer referência ao
ViewChild
esperado até o início da exibição. Primeiro, crie um Assunto que seja concluído quando depois da visualização init.Em seguida, crie uma função que obtenha e execute uma lambda após a conclusão do assunto.
Por fim, verifique se as referências ao ViewChild usam essa função.
fonte
Isso deve funcionar.
Mas como Günter Zöchbauer disse que deve haver algum outro problema no modelo. Eu criei uma resposta meio relevante . Por favor, verifique o console do navegador.
boot.ts
filterTiles.ts
Ele funciona como um encanto. Verifique suas tags e referências.
Obrigado...
fonte
Isso funciona para mim, veja o exemplo abaixo.
fonte
Eu tive um problema semelhante, onde
ViewChild
estava dentro de umaswitch
cláusula que não carregava o elemento viewChild antes de ser referenciado. Eu o resolvi de maneira semi-hacky, mas envolvendo aViewChild
referência em umasetTimeout
executada imediatamente (ou seja, 0ms)fonte
Minha solução para isso foi mover o ngIf de fora do componente filho para dentro do componente filho em uma div que envolvesse toda a seção do html. Dessa forma, ele ainda estava ficando oculto quando precisava, mas conseguiu carregar o componente e eu pude referenciá-lo no pai.
fonte
Corrijo-o apenas adicionando SetTimeout após definir o componente visível
Meu HTML:
Meu componente JS
fonte
No meu caso, eu sabia que o componente filho sempre estaria presente, mas queria alterar o estado anterior à inicialização do filho para economizar trabalho.
Eu escolho testar a criança até que ela apareça e faça alterações imediatamente, o que me salvou de um ciclo de alterações no componente filho.
Em alerta, você pode adicionar um número máximo de tentativas ou adicionar alguns ms de atraso ao arquivo
setTimeout
.setTimeout
lança efetivamente a função no final da lista de operações pendentes.fonte
Uma espécie de abordagem genérica:
Você pode criar um método que espere até
ViewChild
que esteja prontoUso:
fonte
Para mim, o problema era que eu estava referenciando o ID no elemento.
Em vez de assim:
fonte
Se você estiver usando o Ionic, precisará usar o
ionViewDidEnter()
gancho do ciclo de vida. Ionic corre algum material adicional (principalmente de animação relacionadas), que normalmente provoca erros inesperados como este, daí a necessidade de algo que é executado depoisngOnInit
,ngAfterContentInit
e assim por diante.fonte
Aqui está algo que funcionou para mim.
Então, basicamente, eu faço uma verificação a cada segundo até que
*ngIf
se torne realidade e depois faço minhas coisas relacionadas àElementRef
.fonte
A solução que funcionou para mim foi adicionar a diretiva nas declarações em app.module.ts
fonte