Wie übergebe ich Daten an Angular-Routing-Komponenten?

267

In einer der Vorlagen meiner Angular 2-Routen ( FirstComponent ) habe ich eine Schaltfläche

first.component.html

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

Mein Ziel ist es zu erreichen:

Klicken Sie auf die Schaltfläche -> Route zu einer anderen Komponente, während Sie die Daten beibehalten und die andere Komponente nicht als Direktive verwenden.

Das habe ich versucht ...

1. ANSATZ

In derselben Ansicht speichere ich das Sammeln derselben Daten basierend auf Benutzerinteraktion.

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

Normalerweise würde ich den Weg nach SecondComponent durch

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

eventuell die Daten weitergeben

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

Die Definition der Verknüpfung mit Parametern wäre

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

Das Problem bei diesem Ansatz ist, dass ich vermutlich keine komplexen Daten (z. B. ein Objekt wie property3) in der URL übergeben kann.

2. ANSATZ

Eine Alternative wäre, SecondComponent als Direktive in FirstComponent aufzunehmen.

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

Ich möchte jedoch zu dieser Komponente routen und sie nicht einschließen!

3. ANSATZ

Die praktikabelste Lösung, die ich hier sehe, wäre die Verwendung eines Dienstes (z. B. FirstComponentService) für

  • Speichern Sie die Daten (_firstComponentService.storeData ()) auf routeWithData () in FirstComponent
  • Abrufen der Daten (_firstComponentService.retrieveData ()) in ngOnInit () in SecondComponent

Obwohl dieser Ansatz durchaus praktikabel erscheint, frage ich mich, ob dies der einfachste / eleganteste Weg ist, um das Ziel zu erreichen.

Im Allgemeinen würde ich gerne wissen, ob mir andere mögliche Ansätze fehlen , um die Daten zwischen Komponenten zu übertragen, insbesondere mit der weniger möglichen Menge an Code

dragonmnl
quelle
6
3 einfache Möglichkeiten, Daten über die Route zu teilen - angulartutorial.net/2017/12/…
Prashobh
2
danke @Prashobh. Pass data using Query Parametersist das, wonach ich gesucht habe. Ihr Link hat meinen Tag gerettet.
Raj
Angular 7.2 verfügt jetzt über eine neue Funktion zum Übertragen von Daten zwischen Routen. stateÜberprüfen Sie die PR auf weitere Details. Einige nützliche Informationen hier
AzizKapProg
@ Prashobh Vielen Dank. Der Link, den Sie geteilt haben, ist sehr nützlich
vandu

Antworten:

193

Update 4.0.0

Weitere Informationen finden Sie in den Angular-Dokumenten unter https://angular.io/guide/router#fetch-data-before-navigating

Original

Die Nutzung eines Dienstes ist der richtige Weg. In Routenparametern sollten Sie nur Daten übergeben, die in der Browser-URL-Leiste angezeigt werden sollen.

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

Der mit RC.4 gelieferte Router wird wieder eingeführt 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();
  }
}

Siehe auch den Plunker unter https://github.com/angular/angular/issues/9757#issuecomment-229847781

Günter Zöchbauer
quelle
4
Ist diese Antwort für Angular 2.1.0 noch gültig?
Smartmouse
9
RC.4-Routerdaten gelten nur für statische Daten. Sie können nicht unterschiedliche Daten an dieselbe Route senden. Es müssen immer dieselben Daten sein. Bin ich falsch?
Aycanadal
1
Nein, verwenden Sie für diesen Anwendungsfall einen gemeinsam genutzten Dienst.
Günter Zöchbauer
8
In Angular 5 sollten Sie sowieso in der Lage sein ... ngOnInit() { this.myVar = this.route.snapshot.data['some_data']; }
Roger
5
Wenn Sie in der Lage sind Angular v7.2 zu verwenden , um es ermöglicht Zustand jetzt im Router vorbei mit der NavigationExtras- stackoverflow.com/a/54879389/1148107
mtpultz
67

Ich denke, da wir in Winkel 2 nicht wie in Winkel 1.x $ rootScope haben . Wir können Angular 2 Shared Service / Class verwenden, während wir in ngOnDestroy Daten an den Service übergeben und nach dem Routing die Daten aus dem Service in der ngOnInit- Funktion übernehmen:

Hier verwende ich DataService, um Heldenobjekte zu teilen:

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

Objekt von der ersten Seitenkomponente übergeben:

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

Nehmen Sie das Objekt aus der zweiten Seitenkomponente:

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

Hier ist ein Beispiel: Plunker

Utpal Kumar Das
quelle
Das ist schön, aber wie häufig ist das in der Ng2-Community? Ich kann mich nicht erinnern, es in den Dokumenten gelesen zu haben ...
Alfa Bravo
1
Im Vergleich zu anderen Optionen wie URL-Parametern oder anderem Browserspeicher scheint mir dies besser zu sein. Ich habe auch in keiner Dokumentation gesehen, dass es so funktioniert.
Utpal Kumar Das
2
Funktioniert es, wenn der Benutzer eine neue Registerkarte öffnet und die zweite Komponentenroute kopiert und einfügt? Kann ich holen this.hero = this.dataService.hero? Bekomme ich die Werte?
Dies ist in der Tat sehr einfach und jeder Angular-Entwickler weiß es. Das Problem besteht jedoch darin, dass Sie nach dem Aktualisieren lose Daten in den Diensten verlieren. Der Benutzer muss alle Dinge erneut erledigen.
Santosh Kadam
@ SantoshKadam lautet die Frage: "Wie übergebe ich Daten an Angular-Routing-Komponenten?" Das Übergeben von Daten durch die Funktionen ngOnDestroy und ngOnInit ist daher ein Weg, und immer einfach ist der beste. Wenn der Benutzer nach dem erneuten Laden Daten abrufen muss, müssen die Daten in einem permanenten Speicher gespeichert und erneut aus diesem Speicher gelesen werden.
Utpal Kumar Das
28
<div class="button" click="routeWithData()">Pass data and route</div>

Nun, der einfachste Weg, dies in Angular 6 oder anderen Versionen zu tun, besteht darin, einfach Ihren Pfad mit der Datenmenge zu definieren, die Sie übergeben möchten

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

Wie Sie meiner Routendefinition entnehmen können, habe ich /:idden Daten, die ich über die Routernavigation an die Komponente übergeben möchte, den Stand hinzugefügt . Daher wird Ihr Code so aussehen

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

Um das idauf der Komponente zu lesen , importieren Sie einfach ActivatedRoute wie

import { ActivatedRoute } from '@angular/router'

und auf der ngOnInitist, wo Sie die Daten abrufen

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

Sie können mehr in diesem Artikel lesen https://www.tektutorialshub.com/angular-passing-parameters-to-route/

Daniel Charles Mwangila
quelle
12
Was ist, wenn ich ein komplexes Objekt senden möchte? Ich möchte meine Routen nicht zu unaufhaltsamem Unsinn aufblähen :(
cmxl
@cmxl Verwenden Sie dann einen gemeinsam genutzten Dienst.
Stanislasdrg Reinstate Monica
Genau das, wonach ich gesucht habe. Vielen Dank!
Akhil
21

Angular 7.2.0 führte eine neue Methode zum Übergeben der Daten beim Navigieren zwischen gerouteten Komponenten ein:

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

Oder:

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

Um den Status zu lesen, können Sie window.history.statenach Abschluss der Navigation auf die Eigenschaft zugreifen :

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

  constructor(public activatedRoute: ActivatedRoute) {}

  ngOnInit() {
    this.state$ = this.activatedRoute.paramMap
      .pipe(map(() => window.history.state))
  }
}
Washington Soares Braga
quelle
4
funktioniert bei mir nicht, window.history.stategibt so etwas wie zurück, {navigationId: 2}anstatt das Objekt zurückzugeben, das ich übergeben habe.
Louis
@ Louis Welche Angular-Version verwendest du?
Washington Soares Braga
Ich benutze eckige Version 8.1.0
Louis
Ich sehe dasselbe wie Louis, mit einer niedrigeren Version als seiner, aber immer noch hoch genug, dass es diese Funktion haben soll.
WindowsWeenie
Als Teil des zurückgegebenen Objekts wird navigationIdein Wert hinzugefügt. Wenn keine Daten hinzugefügt werden, werden nur die navigationIdangezeigt. Sehen Sie sich die übergebenen Daten an, gehen Sie zurück oder aktualisieren Sie Ihre App und lösen Sie die Aktion erneut aus, mit der die Daten zur Route hinzugefügt und zur nächsten Route navigiert werden (z. B. Klicken auf die Schaltfläche). Dadurch werden Ihre Daten zum Objekt hinzugefügt.
Alf Moh
12

Eine super kluge Person (tmburnell), die nicht ich bin, schlägt vor, die Routendaten neu zu schreiben:

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

Wie hier in den Kommentaren zu sehen.

Ich hoffe, jemand wird dies nützlich finden

terary
quelle
7
Ich habe gerade davon erfahren und ich habe das Gefühl, ich brauche ein paar Stackoverflow-Punkte :)
Tim.Burnell
11

Ich dies der andere Ansatz nicht gut für dieses Problem. Ich denke, der beste Ansatz ist Query-Parameter by RouterAngular, die zwei Möglichkeiten haben:

Abfrageparameter direkt übergeben

Mit diesem Code können Sie in Ihrem HTML-Code zu urlby navigieren params:

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

Abfrageparameter übergeben an Router

Sie müssen den Router nach Ihren Wünschen einspeisen constructor:

constructor(private router:Router){

}

Verwenden Sie jetzt Folgendes:

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

Wenn Sie nun Routerin einem anderen lesen möchten, müssen ComponentSie Folgendes verwenden ActivatedRoute:

constructor(private activateRouter:ActivatedRouter){

}

und subscribedas:

  ngOnInit() {
    this.sub = this.route
      .queryParams
      .subscribe(params => {
        // Defaults to 0 if no query param provided.
        this.page = +params['serviceId'] || 0;
      });
  }
AmirReza-Farahlagha
quelle
this.router.navigate (['/ product-list'], {queryParams: {serviceId: serviceId}}); kann durch this.router.navigate ersetzt werden (['/ product-list'], {queryParams: {serviceId}});
Ramakant Singh
10

Es ist 2019 und viele der Antworten hier würden funktionieren, je nachdem, was Sie tun möchten. Wenn Sie einen internen Status übergeben möchten, der in der URL nicht sichtbar ist (Parameter, Abfrage), können Sie ihn stateseit 7.2 verwenden (wie ich erst heute erfahren habe :)).

Aus dem Blog (Credits Tomasz Kula) - Sie navigieren zur Route ....

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

... aus der HTML-Vorlage: <a routerLink="/details" [state]="{ hello: 'world' }">Go</a>

Und um es in der Zielkomponente aufzunehmen:

constructor(public activatedRoute: ActivatedRoute) {}

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

Spät, aber ich hoffe, das hilft jemandem mit Angular.

PeS
quelle
6

Lösung mit ActiveRoute (wenn Sie ein Objekt über die Route übergeben möchten, verwenden Sie JSON.stringfy / JSON.parse):

Bereiten Sie das Objekt vor dem Senden vor:

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

}

Empfangen Sie Ihr Objekt in der Zielkomponente:

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

}
Skorpion
quelle
1
Das funktioniert, aber was ist, wenn ich nicht alle Daten in der URL verfügbar machen möchte?
mpro
Sie können Daten verschlüsseln, indem Sie sie in Parameter eingeben und anschließend in der Zielkomponente verschlüsseln.
Skorpion
Ich habe den Dienst für die gemeinsame Nutzung von Daten erstellt.
mpro
Wofür ist das super.ngOnInit();?
Andrea Gherardi
5

Der dritte Ansatz ist die häufigste Methode, um Daten zwischen Komponenten auszutauschen. Sie können den Item-Service einfügen, den Sie in der zugehörigen Komponente verwenden möchten.

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
}
Ahankendi
quelle
3
Der Dienst wird beim Wechseln der Route instanziiert. Sie verlieren also Daten
Jimmy Kane
1
@JimmyKane Sie sprechen speziell darüber, wann die Seite aktualisiert wird, aber wenn sie nicht aktualisiert wird, wird der Speicher immer noch in einem Dienst gespeichert. Dies sollte das Standardverhalten sein, da dadurch das Laden viele Male gespeichert wird.
Aaron Rabinowitz
1
@ AaronRabinowitz richtig. Entschuldigung für die Verwirrung. Und entschuldigen Sie die Abstimmung. Ich wünschte, ich könnte es jetzt rückgängig machen. Zu spät. War neu in Angular 2 und mein Problem beim Ausprobieren Ihres Ansatzes war, dass ich den Service für viele Komponenten und nicht über das App-Modul bereitstellen ließ.
Jimmy Kane
3

Übergeben Sie mit JSON

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

quelle
7
Dies wäre eine bessere Antwort, wenn Sie zeigen könnten, wie der Parameter auch in der empfangenden Komponente verwendet wird - die gesamte Route, die er nimmt. Das heißt, wenn jemand nicht weiß, wie ein Parameter übergeben werden soll, wird er auch nicht wissen, wie dieser Parameter in der empfangenden Komponente verwendet wird. :)
Alfa Bravo
Ein Nachteil dabei ist, dass der Querystring eine Größenbeschränkung aufweist und manchmal keine Objekteigenschaften in der Adressleiste angezeigt werden sollen.
CM
3

Verwenden Sie einen gemeinsam genutzten Dienst, um Daten mit einem benutzerdefinierten Index zu speichern. Senden Sie dann diesen benutzerdefinierten Index mit queryParam. Dieser Ansatz ist flexibler .

// 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']];
        }
    );
}
Amin Adel
quelle
0

sag du hast

  1. component1.ts
  2. component1.html

und Sie möchten Daten an component2.ts übergeben .

  • in component1.ts ist eine Variable mit Daten sagen

      //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"}
       });
      }
Nelson Bwogora
quelle
0

Mit BehaviorSubject können Sie Daten zwischen gerouteten Komponenten austauschen. Ein BehaviorSubject enthält einen Wert. Wenn es abonniert ist, gibt es den Wert sofort aus. Ein Betreff enthält keinen Wert.

Im Dienste.

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

In der Komponente können Sie dieses BehaviorSubject abonnieren.

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

Hinweis: Der Betreff funktioniert hier nicht. Sie müssen nur den Betreff "Verhalten" verwenden.

Sushil Suthar
quelle