Question
Quelle est la façon la plus élégante d'obtenir @ViewChild
après que l'élément correspondant dans le modèle a été affiché?
Voici un exemple. Plunker également disponible.
Modèle:
<div id="layout" *ngIf="display">
<div #contentPlaceholder></div>
</div>
Composant:
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);
}
}
J'ai un composant avec son contenu caché par défaut. Quand quelqu'un appelle la show()
méthode, elle devient visible. Cependant, avant la fin de la détection du changement Angular 2, je ne peux pas faire référence à viewContainerRef
. J'encapsule généralement toutes les actions requises setTimeout(()=>{},1)
comme indiqué ci-dessus. Y a-t-il une manière plus correcte?
Je sais qu'il y a une option avec ngAfterViewChecked
, mais cela provoque trop d'appels inutiles.
RÉPONSE (Plunker)
angular
angular2-changedetection
sinedsem
la source
la source
Réponses:
Utilisez un setter pour ViewChild:
Le setter est appelé avec une référence d'élément
*ngIf
devient une foistrue
.Remarque, pour Angular 8, vous devez vous assurer de définir
{ static: false }
, qui est un paramètre par défaut dans d'autres versions Agnular:Remarque: si contentPlaceholder est un composant, vous pouvez changer ElementRef en votre classe de composant:
la source
contentPlaceholder
estElementRef
pasViewContainerRef
.<div #contentPlaceholder></div>
. Techniquement, vous pouvez l'appeler manuellement comme n'importe quel autre setter,this.content = someElementRef
mais je ne vois pas pourquoi vous voudriez le faire.Une alternative pour résoudre ce problème consiste à exécuter le détecteur de changement manuellement.
Vous injectez d'abord
ChangeDetectorRef
:Ensuite, vous l'appelez après avoir mis à jour la variable qui contrôle le * ngIf
la source
onInit()
, j'ai donc ajouté ledetectChanges
avant d'appeler une fonction enfant et il l'a corrigé. (J'ai utilisé à la fois la réponse acceptée et cette réponse)Angulaire 8+
Vous devez ajouter
{ static: false }
une deuxième option pour@ViewChild
. Cela entraîne la résolution des résultats de la requête après l' exécution de la détection des modifications, ce qui vous permet@ViewChild
d'être mis à jour après la modification de la valeur.Exemple:
Exemple de Stackblitz: https://stackblitz.com/edit/angular-d8ezsn
la source
<mat-horizontal-stepper *ngIf="viewMode === DialogModeEnum['New']" linear #stepper
,@ViewChild('stepper', {static: true}) private stepper: MatStepper;
etthis.changeDetector.detectChanges();
ça ne marche toujours pas.Les réponses ci-dessus n'ont pas fonctionné pour moi car dans mon projet, le ngIf est sur un élément d'entrée. J'avais besoin d'accéder à l'attribut nativeElement afin de me concentrer sur l'entrée lorsque ngIf est vrai. Il ne semble pas y avoir d'attribut nativeElement sur ViewContainerRef. Voici ce que j'ai fait (en suivant la documentation de @ViewChild ):
J'ai utilisé setTimeout avant de me concentrer car le ViewChild prend une seconde pour être affecté. Sinon, ce ne serait pas défini.
la source
setTimeout
?I usually wrap all required actions into setTimeout(()=>{},1) as shown above. Is there a more correct way?
Comme cela a été mentionné par d'autres, la solution la plus rapide et la plus rapide consiste à utiliser [caché] au lieu de * ngIf, de cette façon le composant sera créé mais non visible, là pour vous pouvez y avoir accès, bien qu'il ne soit pas le plus efficace façon.
la source
Cela pourrait fonctionner mais je ne sais pas si cela convient à votre cas:
la source
ngAfterViewInit()
au lieu dengOnInit()
. J'ai supposé qu'ilviewContainerRefs
est déjà initialisé mais ne contient pas encore d'éléments. On dirait que je me suis souvenu de ce mal.AfterViewInit
fonctionne réellement. J'ai supprimé tous mes commentaires afin de ne pas confondre les gens. Voici un Plunker fonctionnel: plnkr.co/edit/myu7qXonmpA2hxxU3SLB?p=previewconst
paramètre deViewChild
Une autre "astuce" rapide (solution facile) consiste simplement à utiliser la balise [hidden] au lieu de * ngIf, juste important de savoir que dans ce cas, Angular construit l'objet et le peint sous classe: caché c'est pourquoi ViewChild fonctionne sans problème . Il est donc important de garder à l'esprit que vous ne devez pas l'utiliser caché sur des articles lourds ou coûteux qui peuvent entraîner des problèmes de performances
la source
Mon objectif était d'éviter toute méthode hacky qui suppose quelque chose (par exemple setTimeout) et j'ai fini par implémenter la solution acceptée avec un peu de saveur RxJS en plus:
Mon scénario: je voulais déclencher une action sur un
@ViewChild
élément en fonction du routeurqueryParams
. Du fait qu'un habillage*ngIf
est faux jusqu'à ce que la requête HTTP renvoie les données, l'initialisation de l'@ViewChild
élément se produit avec un retard.Comment ça marche:
combineLatest
n'émet une valeur pour la première fois que lorsque chacun des observables fournis émet la première valeur depuis que le moment acombineLatest
été souscrit. Mon sujettabSetInitialized
émet une valeur lors de la@ViewChild
définition de l' élément. Avec cela, je retarde l'exécution du code soussubscribe
jusqu'à ce que les*ngIf
virages soient positifs et l'@ViewChild
initialisation.Bien sûr, n'oubliez pas de vous désinscrire sur ngOnDestroy, je le fais en utilisant le
ngUnsubscribe
sujet:la source
Une version simplifiée, j'ai eu un problème similaire à cela lors de l'utilisation du SDK Google Maps JS.
Ma solution était d'extraire le
div
etViewChild
dans son propre composant enfant qui, lorsqu'il était utilisé dans le composant parent, pouvait être masqué / affiché à l'aide d'un*ngIf
.Avant
HomePageComponent
ModèleHomePageComponent
ComposantAprès
MapComponent
ModèleMapComponent
ComposantHomePageComponent
ModèleHomePageComponent
Composantla source
Dans mon cas, je n'avais besoin de charger un module entier que lorsque la div existait dans le modèle, ce qui signifie que la sortie était à l'intérieur d'un ngif. De cette façon, chaque fois que angular détectait l'élément #geolocalisationOutlet, il créait le composant à l'intérieur. Le module ne se charge qu'une seule fois également.
la source
Je pense que l'utilisation du report de lodash a beaucoup de sens, surtout dans mon cas où mon tuyau
@ViewChild()
était à l'intérieurasync
la source
Travailler sur Angular 8 Pas besoin d'importer ChangeDector
ngIf vous permet de ne pas charger l'élément et d'éviter d'ajouter plus de stress à votre application. Voici comment je l'ai fait fonctionner sans ChangeDetector
Ensuite, lorsque je change ma valeur ngIf pour être vraie, j'utilise setTimeout comme ceci pour qu'il n'attende que le prochain cycle de changement:
Cela m'a également permis d'éviter d'utiliser des bibliothèques ou des importations supplémentaires.
la source
pour Angular 8 - un mélange de vérification nulle et
@ViewChild
static: false
piratagepour un contrôle de pagination en attente de données asynchrones
la source
Cela fonctionne pour moi si j'utilise ChangeDetectorRef dans Angular 9
la source