web-dev-qa-db-fra.com

Passage de données dans des composants enfants "routeur-prise" (angular 2)

J'ai un composant parent qui va au serveur et récupère un objet.

// parent component

@Component({
    selector : 'node-display',
    template : `
        <router-outlet [node]="node"></router-outlet>
    `
})

export class NodeDisplayComponent implements OnInit {

    node: Node;

    ngOnInit(): void {
        this.nodeService.getNode(path)
            .subscribe(
                node => {
                    this.node = node;
                },
                err => {
                    console.log(err);
                }
            );
    }

Et dans (un de plusieurs) mon enfant afficher

export class ChildDisplay implements OnInit{

    @Input()
    node: Node;

    ngOnInit(): void {
        console.log(this.node);
    }

}

Il ne semble pas que je puisse simplement injecter des données dans le routeur-prise. Il semble que je reçois l'erreur dans la console Web ...

Can't bind to 'node' since it isn't a known property of 'router-outlet'.

Cela a du sens, mais comment ferais-je comme suit ...

1) Grab the "node" data from the server, from within the parent component
2) Pass the data I have retrieved from the server into the child router-outlet

Il ne semble pas que les routeurs-points de vente fonctionnent de la même manière.

57
Zack

Le seul moyen est d'utiliser un service partagé.

<router-outlet [node]="..."></router-outlet> 

est juste invalide. Le composant ajouté par le routeur est ajouté comme frère à <router-outlet> et ne le remplace pas.

Voir aussi https://angular.io/docs/ts/latest/cookbook/component-communication.html#!#bidirectional-service

@Injectable() 
export class NodeService {
  private node:Subject<Node> = new BehaviorSubject<Node>();

  get node$(){
    return this.node.asObservable().filter(node => !!node);
  }

  addNode(data:Node) {
    this.node.next(data);
  }
}
@Component({
    selector : 'node-display',
    providers: [NodeService],
    template : `
        <router-outlet></router-outlet>
    `
})
export class NodeDisplayComponent implements OnInit {
    constructor(private nodeService:NodeService) {}
    node: Node;
    ngOnInit(): void {
        this.nodeService.getNode(path)
            .subscribe(
                node => {
                    this.nodeService.addNode(node);
                },
                err => {
                    console.log(err);
                }
            );
    }
}
export class ChildDisplay implements OnInit{
    constructor(nodeService:NodeService) {
      nodeService.node$.subscribe(n => this.node = n);
    }
}
56
Günter Zöchbauer

La réponse de Günters est excellente, je veux simplement indiquer une autre manière sans utiliser Observables. 

Nous devons toutefois nous rappeler ici que ces objets sont passés par référence. Si vous souhaitez effectuer un travail sur l'objet dans l'enfant et ne pas affecter l'objet parent, je vous suggérerais d'utiliser la solution de Günther. Mais si cela n’a pas d’importance ou si c’est bien le comportement souhaité, je suggérerais ce qui suit.

@Injectable()
export class SharedService {

    sharedNode = {
      // properties
    };
}

Dans votre parent, vous pouvez attribuer la valeur:

this.sharedService.sharedNode = this.node;

Et dans vos enfants (ET parent), injectez le service partagé dans votre constructeur. N'oubliez pas de fournir le service au niveau du groupe de fournisseurs au niveau du module si vous souhaitez un service singleton sur tous les composants de ce module. Sinon, ajoutez simplement le service dans le tableau de fournisseurs dans le parent uniquement, le parent et l'enfant partageront la même instance de service.

node: Node;

ngOnInit() {
    this.node = this.sharedService.sharedNode;    
}

Et comme l'a gentiment souligné newman, vous pouvez également avoir this.sharedService.sharedNode dans le modèle html ou un getter:

get sharedNode(){
  return this.sharedService.sharedNode;
}
29
AJT_82

Un service:

import {Injectable, EventEmitter} from "@angular/core";    

@Injectable()
export class DataService {
onGetData: EventEmitter = new EventEmitter();

getData() {
  this.http.post(...params).map(res => {
    this.onGetData.emit(res.json());
  })
}

Composant:

import {Component} from '@angular/core';    
import {DataService} from "../services/data.service";       

@Component()
export class MyComponent {
  constructor(private DataService:DataService) {
    this.DataService.onGetData.subscribe(res => {
      (from service on .emit() )
    })
  }

  //To send data to all subscribers from current component
  sendData() {
    this.DataService.onGetData.emit(--NEW DATA--);
  }
}
5
egor.xyz

Il y a 3 façons de transmettre des données du parent aux enfants

  1. Par un service partageable: vous devez stocker dans un service les données que vous souhaitez partager avec les enfants.
  2. Via Children Router Resolver si vous devez recevoir des données différentes

    this.data = this.route.snaphsot.data['dataFromResolver'];
    
  3. Par le biais du résolveur de routeur parent si vous devez recevoir les mêmes données de la part du parent

    this.data = this.route.parent.snaphsot.data['dataFromResolver'];
    

Note1: Vous pouvez en savoir plus sur le résolveur ici . Il existe également un exemple de résolveur et explique comment enregistrer le résolveur dans le module, puis récupérer les données du résolveur dans le composant. L'enregistrement du résolveur est identique pour le parent et l'enfant.

Note2: Vous pouvez lire à propos de ActivatedRouteici pour pouvoir obtenir des données du routeur

1
Rzv Razvan

Après this question, dans Angular 7.2, vous pouvez transmettre des données du parent à l’enfant en utilisant l’état historique. Ainsi, vous pouvez faire quelque chose comme

Envoyer:

this.router.navigate(['action-selection'], { state: { example: 'bar' } });

Récupérer:

constructor(private router: Router) {
  console.log(this.router.getCurrentNavigation().extras.state.example);
}

Mais faites attention à être cohérent. Par exemple, supposons que vous souhaitiez afficher une liste dans la barre latérale gauche et les détails de l'élément sélectionné à droite à l'aide d'un routeur-prise. Quelque chose comme:


Élément 1 (x) | ..............................................

Point 2 (x) | ...... Détails de l'article sélectionné .......

Point 3 (x) | ..............................................

Point 4 (x) | ..............................................


Supposons maintenant que vous avez déjà cliqué sur certains éléments. En cliquant sur les boutons de retour du navigateur, les détails de l'élément précédent s'affichent. Mais que se passe-t-il si, entre-temps, vous avez cliqué sur le (x) et supprimé de votre liste cet élément? En effectuant ensuite le clic de retour, vous afficherez les détails d'un élément supprimé.

0
MTZ