45 votes

Angulaire 2 - la transmission des données à l'Enfant composant Parent après l'initialisation

J'ai un composant parent qui est l'obtention de données à partir d'un serveur par l'intermédiaire d'un service. J'ai besoin de certaines de ces données à passer au composant 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 je suis en train 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 serait les deux sens de la liaison de données de l'aide dans ce cas?

Plus D'Enquête

Après la publication des réponses que j'ai essayé d'utiliser l' ngOnChanges cycle de vie du crochet. La question que je vais avoir, c'est que l' ngOnChanges fonction n'est pas déclenché 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é de faire un test avec le Live Exemples sur Plunker de l'Angulaire de l'équipe présentant le Cycle de vie des Crochets. Dans le test que j'ai mis à jour le OnChangesComponent à passer un objet qui, comme on peut le voir dans l'exemple lors de la mise à jour d'un objet de la ngOnChanges ne détecte pas le changement. (Ceci est également indiqué dans cette question)

https://plnkr.co/edit/KkqwpsYKXmRAx5DK4cnV

Semble que le ngDoCheck pourraient aider à résoudre ce problème, mais il me semble qu'il ne serait pas aider à la performance à long terme, car chaque changement est détecté par le présent Cycle de vie du Crochet.

Je sais aussi que je pourrais probablement utiliser l' @ViewChild() pour accéder au Composant Enfant et de définir les variables dont j'ai besoin, mais je ne pense pas que pour ce cas d'utilisation, il fait beaucoup de sens.

La publication de plus de code pour aider à expliquer mieux

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. Fondamentalement, j'ai un parent qui gère les sélections à partir de composants enfants, puis-je soumettre ces sélections pour le 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 de l'ensemble de l'objet prévu par le service du composant parent. Aussi, il serait de rendre l'enfant réutilisables, dans le sens qu'il renvoie ce que le service attend.

Mise à jour

J'apprécie le fait que les utilisateurs sont toujours l'affichage des réponses sur cette question. Notez que le code qui a été écrit 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 que les données soient undefined.

Espérons qu'il s'avère être utile :)

38voto

Mopa Points 321

Étant donné que les données n'est pas défini au départ, vous pouvez vous reporter à *ngIf='données'

<div *ngIf='data'>
   <test-child [childData]="data"></test-child>
</div>

Ou vous pouvez mettre en œuvre ControlValueAccessor sur votre composant et de le transmettre par ngModel avec ngModelChange

<test-child [ngModel]="data?" (ngModelChange)="data? ? data= $event : null"></test-child>

19voto

Madhu Ranjan Points 10925

vous pouvez utiliser ngOnChanges pour obtenir des données comme ci-dessous,

 ngOnChanges() {
   if(!!childData){         
        console.log(childData);         
   }
 }

OnChanges

Angulaire appelle sa ngOnChanges méthode chaque fois qu'il détecte les changements de propriétés d'entrée de composant (ou directive).

Lire plus à ce sujet ici.

Mise à jour

ngOnChanges se déclenche uniquement si vous changez d'ensemble de l'objet, si vous souhaitez simplement mettre à jour la propriété de l'objet lié pas la totalité de l'objet, vous avez deux options,

  1. soit lier la propriété directement dans le modèle, assurez-vous d'utiliser ? comme {{childData?.propertyname}}

  2. ou vous pouvez créer une propriété en fonction de l'childdata objet,

    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 la Plunker!

Espérons que cette aide!!

8voto

Pratheesh Nambi Points 93


`@Composant({
 sélecteur: 'test-enfant',
 modèle: `

`
})

l'exportation de la classe ChildComponent implémente OnChanges {
 @Input() childData: tout;

 ngOnChanges() {
 console.log(childData); // logs undefined
}
}`

C'est censé être comme ça non?J'espère que ce pourrait être le problème


`ngOnChanges() { console.journal(ce.childData); // référence à cette

}`

5voto

benny_boe Points 641

Au moment de votre ChildComponent (vous l'avez appelé aussi ParentComponent, mais je suppose que c'est une faute de frappe) est initialisé et exécute ses ngOnInit, il n'y a pas de données dans votre parent disponible depuis votre service de toujours obtenir 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 cas d'utilisation..

Si vous voulez juste pour afficher les données dans votre ChildComponent modèle, vous pouvez utiliser le elvis opérateur (?). Quelque chose comme: {{ childData?}} ou {{ childData?.myKeyName }}...

Si vous voulez faire un peu d'autres choses, vous pouvez utiliser un setter dans votre ChildComponent. Il va intercepter le changement d'entrée et vous donner la possibilité de modifier/test/"faites ce que vous voulez" avec elle avant de faire d'autres choses dans votre ChildComponent.. 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 utiliser ngOnChanges que Madhu mentionné.

Vous pouvez également prendre un coup d'oeil au livre de cuisine d'entrée pour le Composant de Communication dans les docs officielles.

0voto

R. McIntosh Points 146

Je suis en supposant que l'enfant est d'obtenir un sous-ensemble des parents de données, éventuellement, d'une action de l'utilisateur (comme le chargement d'un formulaire lorsqu'un utilisateur clique sur une ligne de la grille). Dans ce cas, je voudrais utiliser un événement.

Demander au parent de feu d'un événement, peut-être dataSelected, et le contenu de l'événement sont les données qui ont été sélectionnés.

Ensuite, demandez aux enfants d'écouter pour ces événements et une fois mis le feu à charger les données de l'événement dans le modèle.

Cela permettra de dissocier vos composants, ce qui est toujours une bonne idée.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X