Come si passano i dati ai componenti indirizzati angolari?


268

In uno dei modelli dei miei percorsi di Angular 2 ( FirstComponent ) ho un pulsante

first.component.html

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

Il mio obiettivo è raggiungere:

Fare clic sul pulsante -> instradare verso un altro componente preservando i dati e senza utilizzare l'altro componente come direttiva.

Questo è quello che ho provato ...

1o APPROCCIO

Nella stessa vista sto memorizzando la raccolta degli stessi dati in base all'interazione dell'utente.

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
    }
}

Normalmente mi dirigo verso SecondComponent da

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

eventualmente passando i dati

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

mentre la definizione del collegamento con i parametri sarebbe

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

Il problema con questo approccio è che suppongo di non poter trasmettere dati complessi (ad esempio un oggetto come property3) nell'url;

2 ° APPROCCIO

Un'alternativa sarebbe includere SecondComponent come direttiva in FirstComponent.

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

Tuttavia, voglio indirizzare a quel componente, non includerlo!

3 ° APPROCCIO

La soluzione più valida che vedo qui sarebbe quella di utilizzare un servizio (ad esempio FirstComponentService)

  • archivia i dati (_firstComponentService.storeData ()) su routeWithData () in FirstComponent
  • recuperare i dati (_firstComponentService.retrieveData ()) in ngOnInit () in SecondComponent

Mentre questo approccio sembra perfettamente praticabile, mi chiedo se questo sia il modo più semplice / elegante per raggiungere l'obiettivo.

In generale, vorrei sapere se mi mancano altri potenziali approcci per passare i dati tra i componenti, in particolare con la minore quantità possibile di codice


6
3 semplici modi per condividere i dati attraverso il percorso - angulartutorial.net/2017/12/…
Prashobh

2
grazie @Prashobh. Pass data using Query Parametersè quello che stavo cercando. il tuo link mi ha salvato la giornata.
Raj,

Angular 7.2 ha ora una nuova funzione per passare i dati tra le rotte usando statecontrollare il PR per maggiori dettagli. Alcune informazioni utili qui
AzizKapProg,

@Prashobh Grazie mille. Il link che hai condiviso è molto utile
vandu

Risposte:


193

aggiornamento 4.0.0

Consulta i documenti angolari per maggiori dettagli https://angular.io/guide/router#fetch-data-before-navigating

originale

L'uso di un servizio è la strada da percorrere. Nei parametri del percorso dovresti passare solo i dati che desideri vengano riflessi nella barra degli URL del browser.

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

Il router fornito con RC.4 reintroduce 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();
  }
}

Vedi anche il Plunker su https://github.com/angular/angular/issues/9757#issuecomment-229847781


4
Questa risposta è ancora valida per Angular 2.1.0?
smartmouse,

9
I dati del router RC.4 sono solo per dati statici. Non è possibile inviare dati diversi sullo stesso percorso, devono sempre essere gli stessi dati, sbaglio?
aycanadal,

1
No, utilizzare un servizio condiviso per questo caso d'uso.
Günter Zöchbauer,

8
In Angular 5, comunque, dovresti essere in grado di ... ngOnInit() { this.myVar = this.route.snapshot.data['some_data']; }
Roger,

5
Se siete in grado di utilizzare v7.2 angolare permette per il passaggio di stato ora nel router utilizzando il NavigationExtras- stackoverflow.com/a/54879389/1148107
mtpultz

67

Penso che dal momento che non abbiamo $ rootScope in Angular 2 come in Angular 1.x. Possiamo usare angular 2 servizio / classe condivisi mentre in ngOnDestroy passa i dati al servizio e dopo il routing prendiamo i dati dal servizio nella funzione ngOnInit :

Qui sto usando DataService per condividere oggetti hero:

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

Passa l'oggetto dal componente della prima pagina:

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

Prendi l'oggetto dal componente della seconda pagina:

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

Ecco un esempio: plunker


È bello, ma quanto è comune nella comunità Ng2? Non ricordo di averlo letto nei documenti ...
Alfa Bravo,

1
Rispetto ad altre opzioni come i parametri url o altri archivi del browser, questo mi sembra migliore. Inoltre non ho visto in alcuna documentazione lavorare in questo modo.
Utpal Kumar Das,

2
Funziona quando l'utente apre una nuova scheda e copia incolla il secondo percorso del componente? Posso recuperare this.hero = this.dataService.hero? Riceverò i valori?

Questo è davvero molto semplice e ogni sviluppatore angolare lo sa, ma il problema è quando si aggiornano i dati persi nei servizi. L'utente dovrà fare di nuovo tutto.
Santosh Kadam,

@SantoshKadam la domanda è "Come faccio a passare i dati ai componenti indirizzati angolari?" quindi passare i dati dalle funzioni ngOnDestroy e ngOnInit è un modo, e sempre semplice è il migliore. Se l'utente deve recuperare i dati dopo il ricaricamento, è necessario salvarli in una memoria permanente e rileggerli da quella memoria.
Utpal Kumar Das,

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

bene il modo più semplice per farlo in angolare 6 o altre versioni spero sia semplicemente definire il tuo percorso con la quantità di dati che vuoi passare

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

come puoi vedere dalla definizione delle mie rotte, ho aggiunto lo /:idstand ai dati che voglio passare al componente tramite la navigazione del router. Pertanto il tuo codice sarà simile

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

per leggere idil componente, basta importare ActivatedRoute come

import { ActivatedRoute } from '@angular/router'

e sul ngOnInitè dove recuperare i dati

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

puoi leggere di più in questo articolo https://www.tektutorialshub.com/angular-passing-parameters-to-route/


12
cosa succede se desidero inviare un oggetto complesso? non voglio gonfiare i miei percorsi per assurdità
insostenibili

@cmxl Utilizzare quindi un servizio condiviso.
Stanislasdrg Ripristina Monica il

Esattamente quello che stavo cercando. Grazie!
Akhil

21

Angular 7.2.0 ha introdotto un nuovo modo di passare i dati durante la navigazione tra i componenti instradati:

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

O:

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

Per leggere lo stato, è possibile accedere alla window.history.stateproprietà al termine della navigazione:

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

  constructor(public activatedRoute: ActivatedRoute) {}

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

4
non funziona per me, window.history.staterestituisce qualcosa di simile {navigationId: 2}invece di restituire l'oggetto che ho passato.
Louis,

@Louis quale versione angolare stai usando?
Washington Soares Braga,

Sto usando la versione angolare 8.1.0
Louis

Sto vedendo la stessa cosa di Louis, con una versione inferiore alla sua ma ancora abbastanza alta da supporre che abbia quella caratteristica.
WindowsWeenie,

come parte dell'oggetto restituito, navigationIdcon l'aggiunta di un valore. Se non vengono aggiunti dati, navigationIdverrà mostrato solo il . Guarda i dati passati, torna indietro o aggiorna la tua app e riavvia l'azione che aggiunge i dati al percorso e passa al percorso successivo (es. Clic sul pulsante). Questo aggiungerà quindi i tuoi dati all'oggetto.
Alf Moh,

12

Qualche persona super intelligente (tmburnell) che non sono io suggerisce di riscrivere i dati del percorso:

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

Come visto qui nei commenti.

Spero che qualcuno lo troverà utile


7
ho appena scoperto questo e mi sento come se
avessi

11

Io questo l'altro approccio non va bene per questo problema. Penso che l'approccio migliore sia Query-Parameter per Routerangolare che ha 2 vie:

Passando direttamente il parametro di query

Con questo codice puoi navigare urlda paramsnel tuo codice html:

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

Passando il parametro di query di Router

Devi iniettare il router nel tuo constructorlike:

constructor(private router:Router){

}

Ora usalo come:

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

Ora se vuoi leggere da Routerun altro Componentdevi usare ActivatedRoutelike:

constructor(private activateRouter:ActivatedRouter){

}

e subscribeche:

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

this.router.navigate (['/ product-list'], {queryParams: {serviceId: serviceId}}); può essere sostituito con this.router.navigate (['/ product-list'], {queryParams: {serviceId}});
Ramakant Singh,

10

È il 2019 e molte delle risposte qui funzionerebbero, a seconda di ciò che vuoi fare. Se vuoi passare in uno stato interno non visibile nell'URL (parametri, query) puoi usare stateda 7.2 (come ho fatto io imparato proprio oggi :)).

Dal blog (crediti Tomasz Kula) - vai al percorso ....

... da ts: this.router.navigateByUrl('/details', { state: { hello: 'world' } });

... dal modello HTML: <a routerLink="/details" [state]="{ hello: 'world' }">Go</a>

E per raccoglierlo nel componente di destinazione:

constructor(public activatedRoute: ActivatedRoute) {}

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

In ritardo, ma spero che questo aiuti qualcuno con Angular recente.


6

Soluzione con ActiveRoute (se si desidera passare l'oggetto per percorso - utilizzare JSON.stringfy / JSON.parse):

Preparare l'oggetto prima di inviare:

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);
  }

}

Ricevi il tuo oggetto nel componente di destinazione:

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"]);
      });
  }

}

1
Funziona, ma cosa succede se non voglio esporre tutti i dati nell'URL?
mpro,

È possibile crittografare i dati inserendoli in parametri, dopodiché crittografarli nel componente di destinazione.
Scorpione,

Ho creato il servizio per la condivisione dei dati.
mpro,

A cosa serve super.ngOnInit();?
Andrea Gherardi,

5

Il terzo approccio è il modo più comune per condividere i dati tra i componenti. è possibile iniettare il servizio articoli che si desidera utilizzare nel componente correlato.

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
}

3
Il servizio viene istanziato quando si cambia rotta. Quindi perdi dati
Jimmy Kane,

1
@JimmyKane Stai parlando in particolare quando la pagina si aggiorna ma se non si aggiorna, la memoria viene comunque salvata in un servizio. Questo dovrebbe essere il comportamento predefinito poiché salverà il caricamento più volte.
Aaron Rabinowitz,

1
@AaronRabinowitz giusto. Dispiace per la confusione. E scusate il voto negativo. Vorrei poterlo annullare ora. Troppo tardi. Era nuovo di Angular 2 e il mio problema nel provare il tuo approccio era che avevo il servizio fornito a molti componenti e non fornito tramite il modulo dell'app.
Jimmy Kane,

3

Passa usando JSON

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

7
Questa sarebbe una risposta migliore se si potesse mostrare come il parametro viene consumato anche nel componente ricevente - l'intero percorso che prende. Significa che se qualcuno non sa come passare un parametro, non saprà nemmeno come usare questo parametro nel componente ricevente. :)
Alfa Bravo,

Uno svantaggio di questo è che c'è una limitazione di dimensione alla stringa di query e talvolta non si desidera che le proprietà degli oggetti siano visibili nella barra degli indirizzi.
CM

3

utilizzare un servizio condiviso per archiviare i dati con un indice personalizzato. quindi invia quell'indice personalizzato con queryParam. questo approccio è più flessibile .

// 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

dire che hai

  1. component1.ts
  2. component1.html

e si desidera passare i dati a component2.ts .

  • in component1.ts è una variabile con i dati dicono

      //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

È possibile utilizzare BehaviorSubject per condividere i dati tra i componenti instradati. Un oggetto comportamentale contiene un valore. Quando è sottoscritto, emette immediatamente il valore. Un soggetto non ha un valore.

Nel servizio

@Injectable({
  providedIn: 'root'
})
export class CustomerReportService extends BaseService {
  reportFilter = new BehaviorSubject<ReportFilterVM>(null);
  constructor(private httpClient: HttpClient) { super(); }

  getCustomerBalanceDetails(reportFilter: ReportFilterVM): Observable<Array<CustomerBalanceDetailVM>> {
    return this.httpClient.post<Array<CustomerBalanceDetailVM>>(this.apiBaseURL + 'CustomerReport/CustomerBalanceDetail', reportFilter);
  }
}

Nel componente è possibile abbonarsi a questo oggetto comportamentale.

this.reportService.reportFilter.subscribe(f => {
      if (f) {
        this.reportFilter = f;
      }
    });

Nota: il soggetto non funzionerà qui, è necessario utilizzare solo il comportamento.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.