web-dev-qa-db-fra.com

Comment puis-je transmettre des données aux composants routés angulaires?

Dans l'un des modèles de mes routes angulaires 2 (FirstComponent), j'ai un bouton

first.component.html

<div class="button" click="routeWithData()">Pass data and route</div>

Mon objectif est de réaliser:

Cliquez sur le bouton -> route vers un autre composant tout en préservant les données et sans utiliser l'autre composant comme directive.

C'est ce que j'ai essayé ...

1ER APPROCHE

Dans la même vue, je stocke la collecte des mêmes données en fonction de l’interaction de l’utilisateur.

first.component.ts

export class FirstComponent {
     constructor(private _router: Router) { }

     property1: number;
     property2: string;
     property3: TypeXY; // this a class, not a primitive type

    // here some class methods set the properties above

    // DOM events
    routeWithData(){
         // here route
    }
}

Normalement, je me dirigerais vers SecondComponent par

 this._router.navigate(['SecondComponent']);

en passant éventuellement les données par

 this._router.navigate(['SecondComponent', {p1: this.property1, p2: property2 }]);

alors que la définition du lien avec des paramètres serait 

@RouteConfig([
      // ...
      { path: '/SecondComponent/:p1:p2', name: 'SecondComponent', component: SecondComponent} 
)]

Le problème avec cette approche est que je suppose que je ne peux pas transmettre de données complexes (par exemple un object comme property3) in-url;

2ème APPROCHE

Une alternative serait d'inclure SecondComponent en tant que directive dans FirstComponent.

  <SecondComponent [p3]="property3"></SecondComponent>

Cependant, je veux router vers ce composant, pas l'inclure!

3ème APPROCHE

La solution la plus viable que je vois ici serait d’utiliser un Service (par exemple, FirstComponentService) pour: 

  • store les données (_firstComponentService.storeData ()) sur routeWithData () dans FirstComponent
  • récupère les données (_firstComponentService.retrieveDataData) dans ngOnInit () dans SecondComponent

Bien que cette approche semble parfaitement viable, je me demande si c’est le moyen le plus simple et le plus élégant d’atteindre cet objectif.

En général, j'aimerais savoir s'il me manque d'autres approches potentielles pour transmettre les données entre composants, en particulier avec le moins de code possible.

145
dragonmnl

update 4.0.0

Voir la documentation angulaire pour plus de détails https://angular.io/guide/router#fetch-data-before-navigating

original

Utiliser un service est la voie à suivre. Dans les paramètres de route, vous ne devez transmettre que les données que vous souhaitez voir reflétées dans la barre d'URL du navigateur.

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

Le routeur fourni avec RC.4 réintroduit data

constructor(private route: ActivatedRoute) {}
const routes: RouterConfig = [
  {path: '', redirectTo: '/heroes', pathMatch : 'full'},
  {path : 'heroes', component : HeroDetailComponent, data : {some_data : 'some value'}}
];
class HeroDetailComponent {
  ngOnInit() {
    this.sub = this.route
      .data
      .subscribe(v => console.log(v));
  }

  ngOnDestroy() {
    this.sub.unsubscribe();
  }
}

Voir aussi le Plunker à https://github.com/angular/angular/issues/9757#issuecomment-229847781

111
Günter Zöchbauer

Je pense que puisque $ rootScope n’a pas le genre de chose dans angular 2, comme dans angular 1.x. Nous pouvons utiliser un service/classe partagé angulaire 2 alors que, dans ngOnDestroy, transmettez des données au service et, après acheminement, récupérez les données du service dans la fonction ngOnInit

J'utilise DataService pour partager un objet héros:

import { Hero } from './hero';
export class DataService {
  public hero: Hero;
}

Passer l'objet du premier composant de la page:

 ngOnDestroy() {
    this.dataService.hero = this.hero; 
 }

Prendre un objet du composant de la deuxième page:

 ngOnInit() {
    this.hero = this.dataService.hero; 
 }

Voici un exemple: plunker

47
Utpal Kumar Das
<div class="button" click="routeWithData()">Pass data and route</div>

eh bien, le moyen le plus simple de le faire dans les versions angulaires 6 ou autres est, j’espère, de simplement définir votre chemin avec la quantité de données que vous souhaitez transmettre. 

{path: 'detailView/:id', component: DetailedViewComponent}

comme vous pouvez le constater à partir de la définition de mes itinéraires, j'ai ajouté le /:id pour tenir compte des données que je souhaite transmettre au composant via la navigation du routeur. Par conséquent, votre code ressemblera à

<a class="btn btn-white-view" [routerLink]="[ '/detailView',list.id]">view</a>

pour lire la id sur le composant, il suffit d'importer ActivatedRoute comme 

import { ActivatedRoute } from '@angular/router'

et sur la ngOnInit est l'endroit où vous récupérez les données

ngOnInit() {
       this.sub = this.route.params.subscribe(params => {
        this.id = params['id'];
        });
        console.log(this.id);
      }

vous pouvez en lire plus dans cet article https://www.tektutorialshub.com/angular-passing-parameters-to-route/

La 3ème approche est le moyen le plus courant de partager des données entre composants. vous pouvez injecter le service d'article que vous souhaitez utiliser dans le composant associé.

import { Injectable } from '@angular/core';
import { Predicate } from '../interfaces'

import * as _ from 'lodash';

@Injectable()
export class ItemsService {

    constructor() { }


    removeItemFromArray<T>(array: Array<T>, item: any) {
        _.remove(array, function (current) {
            //console.log(current);
            return JSON.stringify(current) === JSON.stringify(item);
        });
    }

    removeItems<T>(array: Array<T>, predicate: Predicate<T>) {
        _.remove(array, predicate);
    }

    setItem<T>(array: Array<T>, predicate: Predicate<T>, item: T) {
        var _oldItem = _.find(array, predicate);
        if(_oldItem){
            var index = _.indexOf(array, _oldItem);
            array.splice(index, 1, item);
        } else {
            array.Push(item);
        }
    }


    addItemToStart<T>(array: Array<T>, item: any) {
        array.splice(0, 0, item);
    }


    getPropertyValues<T, R>(array: Array<T>, property : string) : R
    {
        var result = _.map(array, property);
        return <R><any>result;
    }

    getSerialized<T>(arg: any): T {
        return <T>JSON.parse(JSON.stringify(arg));
    }
}



export interface Predicate<T> {
    (item: T): boolean
}
5
ahankendi

Solution avec Active Route (si vous voulez passer objet par route, utilisez JSON.stringify/JSON.parse)

Préparer l'objet avant d'envoyer: 

export class AdminUserListComponent {

  users : User[];

  constructor( private router : Router) { }

  modifyUser(i) {

    let navigationExtras: NavigationExtras = {
      queryParams: {
          "user": JSON.stringify(this.users[i])
      }
    };

    this.router.navigate(["admin/user/edit"],  navigationExtras);
  }

}

Recevez votre objet dans le composant de destination: 

export class AdminUserEditComponent  {

  userWithRole: UserWithRole;      

  constructor( private route: ActivatedRoute) {}

  ngOnInit(): void {
    super.ngOnInit();

      this.route.queryParams.subscribe(params => {
        this.userWithRole.user = JSON.parse(params["user"]);
      });
  }

}
3
scorpion

Une personne super intelligente (tmburnell) qui n'est pas moi suggère de réécrire les données de l'itinéraire:

let route = this.router.config.find(r => r.path === '/path');
route.data = { entity: 'entity' };
this.router.navigateByUrl('/path');

Comme vu ici dans les commentaires.

J'espère que quelqu'un trouvera cela utile

3
terary

Angular 7.2.0 a introduit une nouvelle façon de transmettre les données lors de la navigation entre les composants routés:

@Component({
  template: `<a (click)="navigateWithState()">Go</a>`,
})
export class AppComponent  {
  constructor(public router: Router) {}
  navigateWithState() {
    this.router.navigateByUrl('/123', { state: { hello: 'world' } });
  }
}

Ou:

@Component({
  selector: 'my-app',
  template: `
  <a routerLink="/details" [state]="{ hello: 'world' }">Go</a>`,
})
export class AppComponent  {}

Pour lire l'état, vous pouvez accéder à la propriété window.history.state une fois la navigation terminée:

export class PageComponent implements OnInit {
  state$: Observable<object>;

  constructor(public activatedRoute: ActivatedRoute) {}

  ngOnInit() {
    this.state$ = this.activatedRoute.paramMap
      .pipe(map(() => window.history.state))
  }
}

Passer à l'aide de JSON

  <a routerLink = "/link"
   [queryParams] = "{parameterName: objectToPass| json }">
         sample Link                   
  </a>
2
Nathaniel Ho Tabin

Si cette autre approche n’est pas bonne pour cette question ... Je pense que la meilleure approche est Paramètre de requête de Router angular qui a 2 voies:

Passer le paramètre de requête directement

Avec ce code, vous pouvez accéder à url par params dans votre code html:

<a [routerLink]="['customer-service']" [queryParams]="{ serviceId: 99 }"></a>

Passage du paramètre de requête par Router

Vous devez injecter le routeur dans votre constructor comme:

constructor(private router:Router){

}

Maintenant, utilisez comme ça:

goToPage(pageNum) {
    this.router.navigate(['/product-list'], { queryParams: { serviceId: serviceId} });
}

Maintenant, si vous voulez lire à partir de Router dans une autre Component, vous devez utiliser ActivatedRoute comme:

constructor(private activateRouter:ActivatedRouter){

}

et subscribe que:

  ngOnInit() {
    this.sub = this.route
      .queryParams
      .subscribe(params => {
        // Defaults to 0 if no query param provided.
        this.page = +params['serviceId'] || 0;
      });
  }
1

utiliser un service partagé pour stocker des données avec un index personnalisé. puis envoyez cet index personnalisé avec queryParam. cette approche est plus flexible .

// component-a : TypeScript :
constructor( private DataCollector: DataCollectorService ) {}

ngOnInit() {
    this.DataCollector['someDataIndex'] = data;
}

// component-a : html :
<a routerLink="/target-page" 
   [queryParams]="{index: 'someDataIndex'}"></a>

.

// component-b : TypeScript :
public data;

constructor( private DataCollector: DataCollectorService ) {}

ngOnInit() {
    this.route.queryParams.subscribe(
        (queryParams: Params) => {
            this.data = this.DataCollector[queryParams['index']];
        }
    );
}
0
Amin Adel

Si vous ne souhaitez pas exposer les données à votre utilisateur à l'intérieur de la chaîne de requête de l'URL de navigation, vous pouvez transmettre les données demandées entre deux itinéraires de composant différents à l'aide de localStorage.

Avant de naviguer par programme

localStorage.setItem("myHiddenData", JSON.stringify(routeData));    
this.router.navigate(["route"]);

Inside ngOnInit du deuxième composant

ngOnInit(): void {
  this.dataFromRoute = localStorage.getItem("myHiddenData");
}

(Facultatif) Protecteur de routeur

Si vous ne souhaitez pas que l'utilisateur accède à cette page sans les données de routage, vous pouvez également créer un protecteur de route canActivate associé à ce composant spécifique qui vérifie avant la navigation si les données sont correctement définies dans le stockage local, comme ceci:

  canActivate(): boolean {
    if (localStorage.getItem("myHiddenData")) {
      return true;
    } else {
      // redirect
      this.router.navigate("Home");

      return false;
    }

Cette solution de contournement est un peu détaillée, mais elle peut être utile si vous souhaitez que l'utilisateur accède à cette page uniquement avec les données demandées et ne permet en réalité pas à l'utilisateur de modifier facilement les données transmises.

0
Giovanni Bertoncelli

dis que tu as

  1. composant1.ts
  2. composant1.html

et vous voulez transmettre des données à composant2.ts .

  • dans composant1.ts est une variable avec des données dites

      //component1.ts
      item={name:"Nelson", bankAccount:"1 million dollars"}
    
      //component1.html
       //the line routerLink="/meter-readings/{{item.meterReadingId}}" has nothing to 
      //do with this , replace that with the url you are navigating to
      <a
        mat-button
        [queryParams]="{ params: item | json}"
        routerLink="/meter-readings/{{item.meterReadingId}}"
        routerLinkActive="router-link-active">
        View
      </a>
    
      //component2.ts
      import { ActivatedRoute} from "@angular/router";
      import 'rxjs/add/operator/filter';
    
      /*class name etc and class boiler plate */
      data:any //will hold our final object that we passed 
      constructor(
      private route: ActivatedRoute,
      ) {}
    
     ngOnInit() {
    
     this.route.queryParams
      .filter(params => params.reading)
      .subscribe(params => {
      console.log(params); // DATA WILL BE A JSON STRING- WE PARSE TO GET BACK OUR 
                           //OBJECT
    
      this.data = JSON.parse(params.item) ;
    
      console.log(this.data,'PASSED DATA'); //Gives {name:"Nelson", bankAccount:"1 
                                            //million dollars"}
       });
      }
    
0
Nelson Bwogora