2 - Données angulaires de passage à la composante de L'enfant après l'initialisation par le Parent
j'ai un composant parent qui reçoit des données d'un serveur via un service. J'ai besoin que certaines de ces données soient transmises à la composante enfant.
j'ai essayé de transmettre ces données avec l'habituel @Input()
cependant, comme je m'y attendais les données que je reçois dans le composant Enfant est undefined
.
exemple
Composant Parent
@Component({
selector: 'test-parent',
template: `
<test-child [childData]="data"></test-child>
`
})
export class ParentComponent implements OnInit {
data: any;
ngOnInit() {
this.getData();
}
getData() {
// calling service and getting data
this.testService.getData()
.subscribe(res => {
this.data = res;
});
}
}
Composant Enfant
@Component({
selector: 'test-child',
template: `
<!-- Content for child.... -->
`
})
export class ChildComponent implements OnInit {
@Input() childData: any;
ngOnInit() {
console.log(childData); // logs undefined
}
}
j'ai gardé l'exemple simple pour montrer ce que j'essaie de faire. Ma question est de savoir si il est possible de transmettre des données à l'enfant après l'initialisation. Je ne suis pas sûr, mais est-ce que le fait de lier des données dans les deux sens aiderait dans ce cas?
Suite Enquête
suite aux réponses postées j'ai essayé d'utiliser le ngOnChanges
cycle de vie du crochet. Le problème, c'est que la fonction ngOnChanges
n'est pas activée lorsque les données sont mises à jour. Les données est un objet qui est pourquoi je pense que les changements ne sont pas détectés.
code mis à jour dans le composant enfant
@Component({
selector: 'test-child',
template: `
<!-- Content for child.... -->
`
})
export class ChildComponent implements OnChanges {
@Input() childData: any;
ngOnChanges() {
console.log(childData); // logs undefined
}
}
j'ai essayé un test avec les exemples en direct sur Plunker de L'équipe Angular montrant les crochets de cycle de vie. Dans le test j'ai mis à jour le OnChangesComponent pour passer un objet qui, comme on peut le voir dans l'exemple, lors de la mise à jour d'un objet, ngOnChanges ne détecte pas le changement. (Ceci est également indiqué dans cette question )
https://plnkr.co/edit/KkqwpsYKXmRAx5DK4cnV
semble que le ngDoCheck
pourrait aider à résoudre ce problème, mais il me semble qu'il ne serait pas aider la performance à long terme que chaque changement est détecté par ce Cycle de vie du Crochet.
aussi je sais que je pourrais probablement utiliser le @ViewChild()
pour accéder à la composante enfant et définir les variables dont j'ai besoin, mais je ne pense pas que pour ce cas d'utilisation, il a beaucoup de sens.
Afficher plus de code pour mieux expliquer
Composant Parent
@Component({
selector: 'test-parent',
template: `
<test-child [childType]="numbers" [childData]="data" (pickItem)="pickNumber($event)">
<template let-number>
<span class="number" [class.is-picked]="number.isPicked">
{{ number.number }}
</span>
</template>
</test-child>
<test-child [childType]="letters" [childData]="data" (pickItem)="pickLetter($event)">
<template let-letter>
<span class="letter" [class.is-picked]="letter.isPicked">
{{ letter.displayName }}
</span>
</template>
</test-child>
<button (click)="submitSelections()">Submit Selection</button>
`
})
export class ParentComponent implements OnInit {
data: any;
numbersData: any;
lettersData: any;
ngOnInit() {
this.getData();
}
getData() {
// calling service and getting data for the game selections
this.testService.getData()
.subscribe(res => {
this.data = res;
setChildData(this.data);
});
}
setChildData(data: any) {
for(let i = 0; i < data.sections.length; i++) {
if(data.sections[i].type === 'numbers') {
this.numbersData = data.sections[i];
}
if(data.sections[i].type === 'letters') {
this.lettersData = data.sections[i];
}
}
}
pickNumber(pickedNumbers: any[]) {
this.pickedNumbers = pickedNumbers;
}
pickLetter(pickedLetters: any[]) {
this.pickedLetters = pickedLetters;
}
submitSelections() {
// call service to submit selections
}
}
Composant Enfant
@Component({
selector: 'test-child',
template: `
<!--
Content for child...
Showing list of items for selection, on click item is selected
-->
`
})
export class ChildComponent implements OnInit {
@Input() childData: any;
@Output() onSelection = new EventEmitter<Selection>;
pickedItems: any[];
// used for click event
pickItem(item: any) {
this.pickedItems.push(item.number);
this.onSelection.emit(this.pickedItems);
}
}
C'est plus ou moins le code que j'ai. En gros, j'ai un parent qui s'occupe des sélections des composants enfants et ensuite je soumets ces sélections au service. J'ai besoin de transmettre certaines données à l'enfant parce que j'essaie d'avoir l'objet que l'enfant retourne dans le format que le service attendu. Cela m'aiderait à ne pas créer tout l'objet attendu par le service dans la composante parent. En outre, il permettrait à l'enfant réutilisable dans le sens qu'il renvoie ce que le service attend.
mise à Jour
je comprends que les utilisateurs sont encore en train de poster des réponses sur cette question. Notez que le code affiché ci-dessus a fonctionné pour moi. Le problème que j'ai eu était que dans un modèle particulier, j'ai eu une faute de frappe qui a causé pour les données d'être undefined
.
l'Espoir qu'il s'avère être utile :)
4 réponses
puisque les données ne sont pas définies au début, vous pouvez les reporter avec *ngIf= 'data'
<div *ngIf='data'>
<test-child [childData]="data"></test-child>
</div>
ou vous pouvez implémenter Controlvaleaccessor sur votre component et le passer par ngmodelchange
<test-child [ngModel]="data?" (ngModelChange)="data? ? data= $event : null"></test-child>
vous pouvez utiliser ngOnChanges
pour obtenir des données comme ci-dessous,
ngOnChanges() {
if(!!childData){
console.log(childData);
}
}
OnChanges
Angular appelle sa méthode ngOnChanges chaque fois qu'elle détecte des changements entrer les propriétés du composant (ou de la directive).
lire la suite de it ici .
mise à Jour
ngOnChanges
ne se déclenche que si vous changez l'objet entier, si vous voulez simplement mettre à jour la propriété de l'objet lié et non l'objet entier, vous avez deux options,
-
soit lier la propriété directement dans le modèle, assurez-vous d'utiliser 151950920" comme
{{childData?.propertyname}}
-
ou vous pouvez créer une propriété get en fonction de l'objet childdddata,
get prop2(){ return this.childData.prop2; }
Copmlete Exemple,
import { Component, Input } from '@angular/core';
@Component({
selector: 'my-app',
template: `<h1>Hello {{name}}</h1>
<child-component [childData]='childData' ></child-component>
`
})
export class AppComponent {
name = 'Angular';
childData = {};
constructor(){
// set childdata after 2 second
setTimeout(() => {
this.childData = {
prop1 : 'value of prop1',
prop2 : 'value of prop2'
}
}, 2000)
}
}
@Component({
selector: 'child-component',
template: `
prop1 : {{childData?.prop1}}
<br />
prop2 : {{prop2}}
`
})
export class ChildComponent {
@Input() childData: {prop1: string, prop2: string};
get prop2(){
return this.childData.prop2;
}
}
voici le Plunker !
Espérons que cette aide!!
au moment où votre ChildComponent (vous l'Avez Appelé aussi Parent Component, mais je suppose que c'est une typographie) est initialisé et exécute son ngOnInit, il n'y a pas de données dans votre parent disponible puisque votre service sera toujours dehors en train de récupérer vos données. Une fois que vos données sont là, il sera poussé à l'enfant.
ce que vous pouvez faire dépend de votre usecase..
si vous voulez juste afficher les données dans votre ChildComponent template , vous pouvez utiliser l'opérateur elvis ( ?
). Quelque chose comme: {{ childData?}}
ou {{ childData?.myKeyName }}
...
si vous voulez faire d'autres choses, vous pouvez utiliser un setter dans votre ChildComponent. Il interceptera le changement d'entrée et vous donnera la possibilité de modifier/tester/"faire ce que vous voulez" avec lui avant de faire d'autres choses avec lui dans votre Component enfant.. par exemple.:
@Component({
selector: 'test-child',
template: `
<!-- Content for child.... -->
`
})
export class ChildComponent implements OnInit {
private _childData: any;
@Input()
set childData(parentData: any) {
// every time the data from the parent changes this will run
console.log(parnetData);
this._childData = parentData; // ...or do some other crazy stuff
}
get childData(): any { return this._childData; }
ngOnInit() {
// We don't need it for that...
}
}
ou utilisez ngOnChanges
comme Madhu l'a mentionné.
vous pouvez également jeter un oeil à l'entrée de Livre de cuisine pour communication de Composant dans les documents officiels.
je suppose que l'enfant obtient un sous-ensemble des données des parents, potentiellement à partir d'une action de l'utilisateur (comme charger un formulaire quand un utilisateur clique sur une ligne de grille). Dans ce cas, je voudrais utiliser un événement.
avoir le feu parent un événement, peut-être des données sélectionnées, et le contenu de l'événement serait les données qui ont été sélectionnées.
puis demander à l'enfant d'écouter pour ces événements et lors de la mise à feu charger les données de l'événement dans le modèle.
cela vous aidera à découpler vos composants, ce qui est toujours une bonne idée.