J'essaie d'apprendre Angular 2.
Je souhaite accéder à un composant enfant à partir d'un composant parent à l'aide de l' annotation @ViewChild .
Voici quelques lignes de code:
Dans BodyContent.ts, j'ai:
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);
}
}
dans 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(){};
}
Enfin voici les modèles (comme suggéré dans les commentaires):
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>
Le modèle FilterTiles.html est correctement chargé dans la balise ico-filter-tiles (en effet, je peux voir l'en-tête).
Remarque: la classe BodyContent est injectée dans un autre modèle (Body) à l'aide de DynamicComponetLoader: dcl.loadAsRoot (BodyContent, '# ico-bodyContent', injector):
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);
}
}
Le problème est que lorsque j'essaie d'écrire ft
dans le journal de la console, j'obtiens undefined
, et bien sûr, j'obtiens une exception lorsque j'essaie de pousser quelque chose à l'intérieur du tableau «tuiles»: «pas de tuiles de propriété pour« non défini »» .
Une dernière chose: le composant FilterTiles semble être correctement chargé, car je peux voir le modèle html correspondant.
Toute suggestion? Merci
la source
ft
ne serait pas défini dans le constructeur, mais dans un gestionnaire d'événements click, il serait déjà défini.loadAsRoot
, qui a un problème connu avec la détection des modifications. Assurez-vous simplement d'utiliserloadNextToLocation
ouloadIntoLocation
.loadAsRoot
. Une fois que j'ai remplacéloadIntoLocation
le problème a été résolu. Si vous faites votre commentaire comme réponse, je peux le marquer comme acceptéRéponses:
J'ai eu un problème similaire et j'ai pensé publier au cas où quelqu'un d'autre aurait fait la même erreur. Tout d'abord, une chose à considérer est
AfterViewInit
; vous devez attendre que la vue soit initialisée avant de pouvoir accéder à votre@ViewChild
. Cependant, mon@ViewChild
retourne toujours nul. Le problème était mon*ngIf
. La*ngIf
directive tuait mon composant de contrôles, donc je ne pouvais pas y faire référence.J'espère que cela pourra aider.
EDIT
Comme mentionné par @Ashg ci - dessous , une solution consiste à utiliser à la
@ViewChildren
place de@ViewChild
.la source
ngClass
+hidden
class au lieu dengIf
. Ça a marché. Merci!Le problème, comme mentionné précédemment, est celui
ngIf
qui rend la vue non définie. La réponse est d'utiliserViewChildren
au lieu deViewChild
. J'ai eu un problème similaire où je ne voulais pas qu'une grille soit affichée jusqu'à ce que toutes les données de référence aient été chargées.html:
Code de composant
Ici, nous utilisons
ViewChildren
sur lequel vous pouvez écouter les changements. Dans ce cas, tous les enfants avec la référence#searchGrid
. J'espère que cela t'aides.la source
this.SearchGrid
propriétés que vous devez utiliser comme syntaxesetTimeout(()=>{ ///your code here }, 1);
pour éviter l'exception: l'expression a changé après avoir été vérifiée*ngIf
travaux, et après le rendu, nous pouvons enregistrer un ElementRef des composants dinamic.Vous pouvez utiliser un setter pour
@ViewChild()
Si vous avez un wrapper ngIf, le setter sera appelé avec undefined, puis à nouveau avec une référence une fois que ngIf lui permettra de s'afficher.
Mais mon problème était autre chose. Je n'avais pas inclus le module contenant mes "FilterTiles" dans mes app.modules. Le modèle n'a pas généré d'erreur, mais la référence était toujours indéfinie.
la source
FilterTiles
. J'ai déjà rencontré ce problème pour cette raison auparavant.@ViewChild('paginator', {static: false})
Cela a fonctionné pour moi.
Mon composant nommé 'mon-composant', par exemple, était affiché en utilisant * ngIf = "showMe" comme ceci:
Ainsi, lorsque le composant est initialisé, le composant n'est pas encore affiché tant que "showMe" n'est pas vrai. Ainsi, mes références @ViewChild n'étaient pas définies.
C'est là que j'ai utilisé @ViewChildren et la QueryList qu'il renvoie. Voir l'article angulaire sur QueryList et une démonstration d'utilisation de @ViewChildren .
Vous pouvez utiliser la QueryList retournée par @ViewChildren et vous abonner à toutes les modifications apportées aux éléments référencés à l'aide de rxjs, comme indiqué ci-dessous. @ViewChild n'a pas cette capacité.
J'espère que cela aidera quelqu'un à gagner du temps et à éviter la frustration.
la source
.take(1).subscribe()
, mais excellente réponse, merci beaucoup!Ma solution de contournement était d'utiliser
[style.display]="getControlsOnStyleDisplay()"
au lieu de*ngIf="controlsOn"
. Le bloc est là mais il n'est pas affiché.la source
Ce qui a résolu mon problème était de m'assurer qu'il
static
était réglé surfalse
.Lorsque cette
static
option est désactivée, la@ViewChild
référence est mise à jour par Angular lorsque la*ngIf
directive change.la source
Ma solution était de remplacer
*ngIf
par[hidden]
. L'inconvénient était que tous les composants enfants étaient présents dans le code DOM. Mais a fonctionné pour mes besoins.la source
Dans mon cas, j'avais un setter de variable d'entrée utilisant le
ViewChild
, et leViewChild
était à l'intérieur d'une*ngIf
directive, donc le setter essayait d'y accéder avant le*ngIf
rendu (cela fonctionnerait bien sans le*ngIf
, mais ne fonctionnerait pas s'il était toujours défini sur vrai avec*ngIf="true"
).Pour résoudre, j'ai utilisé Rxjs pour m'assurer que toute référence à l'
ViewChild
attendu jusqu'à ce que la vue soit lancée. Tout d'abord, créez un objet qui se termine après le début de la vue.Ensuite, créez une fonction qui prend et exécute un lambda une fois le sujet terminé.
Enfin, assurez-vous que les références à ViewChild utilisent cette fonction.
la source
Ça doit marcher.
Mais comme l'a dit Günter Zöchbauer , il doit y avoir un autre problème dans le modèle. J'ai créé un peu Relevant-Plunkr-Answer . Veuillez vérifier la console du navigateur.
boot.ts
filterTiles.ts
Il fonctionne comme un charme. Veuillez vérifier vos étiquettes et références.
Merci...
la source
Cela fonctionne pour moi, voir l'exemple ci-dessous.
la source
J'ai eu un problème similaire, où
ViewChild
était à l'intérieur d'uneswitch
clause qui ne chargeait pas l'élément viewChild avant qu'il soit référencé. Je l'ai résolu de manière semi-hacky mais en encapsulant laViewChild
référence dans unsetTimeout
qui s'est exécuté immédiatement (ie 0 ms)la source
Ma solution à cela était de déplacer le ngIf de l'extérieur du composant enfant vers l'intérieur du composant enfant sur un div qui enveloppait la section entière de html. De cette façon, il était toujours caché quand il le fallait, mais j'ai pu charger le composant et je pouvais le référencer dans le parent.
la source
Je le corrige en ajoutant simplement SetTimeout après avoir visible le composant
Mon HTML:
Mon composant JS
la source
Dans mon cas, je savais que le composant enfant serait toujours présent, mais je voulais modifier l'état avant l'initialisation de l'enfant pour économiser du travail.
J'ai choisi de tester pour l'enfant jusqu'à ce qu'il apparaisse et d'apporter des modifications immédiatement, ce qui m'a sauvé un cycle de changement sur le composant enfant.
Alerte, vous pouvez ajouter un nombre maximum de tentatives ou ajouter un délai de quelques ms au
setTimeout
.setTimeout
jette efficacement la fonction au bas de la liste des opérations en attente.la source
Une sorte d'approche générique:
Vous pouvez créer une méthode qui attendra d'
ViewChild
être prêteUsage:
la source
Pour moi, le problème était que je faisais référence à l'ID sur l'élément.
Au lieu de comme ça:
la source
Si vous utilisez Ionic, vous devrez utiliser le
ionViewDidEnter()
crochet du cycle de vie. Ionic exécute des éléments supplémentaires (principalement liés à l'animation) qui provoquent généralement des erreurs inattendues comme celle-ci, d'où la nécessité de quelque chose qui s'exécute aprèsngOnInit
,ngAfterContentInit
etc.la source
Voici quelque chose qui a fonctionné pour moi.
Donc, fondamentalement, je vérifie chaque seconde jusqu'à ce que le
*ngIf
devienne vrai, puis je fais mes trucs liés auElementRef
.la source
La solution qui a fonctionné pour moi a été d'ajouter la directive dans les déclarations dans app.module.ts
la source