ฉันจะส่งข้อมูลไปยังคอมโพเนนต์ที่กำหนดเส้นทางของ Angular ได้อย่างไร


268

ในเทมเพลตหนึ่งในเส้นทางของ Angular 2 ( FirstComponent ) ฉันมีปุ่ม

first.component.html

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

เป้าหมายของฉันคือการบรรลุ:

คลิกปุ่ม -> เส้นทางไปยังส่วนประกอบอื่นในขณะที่เก็บรักษาข้อมูลและไม่ใช้ส่วนประกอบอื่นเป็นคำสั่ง

นี่คือสิ่งที่ฉันพยายาม ...

แนวทางที่ 1

ในมุมมองเดียวกันฉันกำลังเก็บรวบรวมข้อมูลเดียวกันตามการโต้ตอบของผู้ใช้

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

ปกติฉันจะไปที่SecondComponentโดย

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

ในที่สุดก็ผ่านข้อมูลโดย

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

ในขณะที่ความหมายของการเชื่อมโยงกับพารามิเตอร์จะเป็น

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

ปัญหาของวิธีนี้คือฉันเดาว่าฉันไม่สามารถส่งผ่านข้อมูลที่ซับซ้อนได้ (เช่นวัตถุเช่นคุณสมบัติ 3) ใน URL

แนวทางที่ 2

อีกทางเลือกหนึ่งคือการรวม SecondComponent เป็นคำสั่งใน FirstComponent

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

อย่างไรก็ตามฉันต้องการเส้นทางไปยังส่วนประกอบนั้นไม่รวมมัน!

แนวทางที่ 3

ทางออกที่เป็นไปได้มากที่สุดที่ฉันเห็นที่นี่คือการใช้บริการ (เช่น FirstComponentService) เพื่อ

  • เก็บข้อมูล (_firstComponentService.storeData ()) บน routeWithData () ใน FirstComponent
  • ดึงข้อมูล (_firstComponentService.retrieveData ()) ในngOnInit ()ในSecondComponent

ในขณะที่วิธีการนี้ดูเหมือนว่าปฏิบัติได้อย่างสมบูรณ์แบบฉันสงสัยว่านี่เป็นวิธีที่ง่ายที่สุด / หรูหราที่สุดในการบรรลุเป้าหมาย

โดยทั่วไปฉันต้องการทราบว่าฉันขาดวิธีการที่เป็นไปได้อื่น ๆ ในการส่งผ่านข้อมูลระหว่างส่วนประกอบโดยเฉพาะอย่างยิ่งเมื่อมีจำนวนรหัสน้อยลง


6
3 วิธีง่าย ๆ ในการแบ่งปันข้อมูลผ่านเส้นทาง - angulartutorial.net/2017/12/…
Prashobh

2
ขอบคุณ @Pashashh Pass data using Query Parametersคือสิ่งที่ฉันกำลังมองหา ลิงค์ของคุณบันทึกวันของฉัน
ราชา

Angular 7.2 มีคุณสมบัติใหม่ในการส่งผ่านข้อมูลระหว่างเส้นทางโดยใช้การstateตรวจสอบPRสำหรับรายละเอียดเพิ่มเติม ข้อมูลที่เป็นประโยชน์บางอย่างที่นี่
AzizKapProg

@ Prashobh ขอบคุณมาก การเชื่อมโยงที่คุณได้ร่วมกันเป็นประโยชน์อย่างมาก
vandu

คำตอบ:


193

อัปเดต 4.0.0

ดูเอกสารเชิงมุมสำหรับรายละเอียดเพิ่มเติมhttps://angular.io/guide/router#fetch-data-before-navigating

เป็นต้นฉบับ

การใช้บริการเป็นวิธีที่จะไป ใน params เส้นทางคุณควรส่งผ่านข้อมูลที่คุณต้องการให้แสดงในแถบ URL ของเบราว์เซอร์เท่านั้น

ดูเพิ่มเติมที่https://angular.io/docs/ts/latest/cookbook/component-communication.html#!#bidirectional-service

เราเตอร์ที่จัดส่งมาพร้อมกับ RC.4 แนะนำอีกครั้ง 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();
  }
}

ดู Plunker ได้ที่https://github.com/angular/angular/angular/issues/9757#issuecomment-229847781


4
คำตอบนี้ยังใช้ได้กับ Angular 2.1.0 หรือไม่
smartmouse

9
ข้อมูลเราเตอร์ RC.4 ใช้สำหรับข้อมูลแบบคงที่เท่านั้น คุณไม่สามารถส่งข้อมูลที่แตกต่างกันไปยังเส้นทางเดียวกันได้เสมอฉันต้องเป็นข้อมูลเดิมหรือไม่
aycanadal

1
ไม่ใช้บริการสาธารณะสำหรับกรณีการใช้งานนี้
GünterZöchbauer

8
ใน Angular 5 คุณควรจะสามารถ ... ngOnInit() { this.myVar = this.route.snapshot.data['some_data']; }
Roger

5
หากคุณสามารถใช้ Angular v7.2 ได้อนุญาตให้ส่งผ่านสถานะในเราเตอร์โดยใช้NavigationExtras- stackoverflow.com/a/54879389/1148107
mtpultz

67

ฉันคิดว่าเนื่องจากเราไม่มี$ rootScopeชนิดใดในมุม 2 เท่ากับ 1.x เราสามารถใช้บริการ / คลาสที่ใช้ร่วมกันของ angular 2 ในขณะที่ในngOnDestroyส่งผ่านข้อมูลไปยังบริการและหลังจากที่เราต์เส้นทางใช้ข้อมูลจากบริการในฟังก์ชันngOnInit :

ที่นี่ฉันใช้ DataService เพื่อแชร์ออบเจ็กต์ฮีโร่:

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

ผ่านวัตถุจากองค์ประกอบหน้าแรก:

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

นำวัตถุจากองค์ประกอบหน้าสอง:

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

นี่คือตัวอย่าง: พลั่ว


นี่เป็นสิ่งที่สวยงาม แต่สิ่งที่พบได้ทั่วไปในชุมชน Ng2 เป็นอย่างไร ฉันจำการอ่านในเอกสารไม่ได้ ...
Alfa Bravo

1
เมื่อเทียบกับตัวเลือกอื่น ๆ เช่นพารามิเตอร์ url หรือที่เก็บข้อมูลเบราว์เซอร์อื่น ๆ ฉันคิดว่าดีกว่านี้ ฉันยังไม่เห็นในเอกสารใด ๆ ที่จะทำงานเช่นนี้
Utpal Kumar Das

2
มันทำงานเมื่อผู้ใช้เปิดแท็บใหม่และคัดลอกวางเส้นทางองค์ประกอบที่สอง? ฉันสามารถดึงข้อมูลได้this.hero = this.dataService.heroหรือไม่? ฉันจะได้รับค่าหรือไม่

นี่เป็นเรื่องง่ายมากและนักพัฒนา Angular ทุกคนรู้ แต่ปัญหาคือเมื่อคุณรีเฟรชข้อมูลที่หลวมในบริการ ผู้ใช้จะต้องทำทุกสิ่งอีกครั้ง
Santosh Kadam

@SantoshKadam คำถามคือ "ฉันจะส่งข้อมูลไปยังคอมโพเนนต์ที่กำหนดเส้นทาง Angular ได้อย่างไร" ดังนั้นการส่งผ่านข้อมูลโดยฟังก์ชั่น ngOnDestroy และ ngOnInit จึงเป็นวิธีที่ง่ายและดีที่สุดเสมอ หากผู้ใช้ต้องการรับข้อมูลหลังจากรีโหลดแล้วจำเป็นต้องบันทึกข้อมูลในที่เก็บข้อมูลถาวรและอ่านอีกครั้งจากที่เก็บข้อมูลนั้น
Utpal Kumar Das

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

วิธีที่ง่ายที่สุดในการทำในเชิงมุม 6 หรือเวอร์ชันอื่น ๆ ฉันหวังว่าจะเพียงกำหนดเส้นทางของคุณด้วยจำนวนข้อมูลที่คุณต้องการผ่าน

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

อย่างที่คุณเห็นจากคำจำกัดความเส้นทางของฉันฉันได้เพิ่ม/:idขาตั้งไปยังข้อมูลที่ฉันต้องการส่งไปยังส่วนประกอบผ่านการนำทางเราเตอร์ ดังนั้นรหัสของคุณจะเป็นอย่างไร

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

เพื่อที่จะอ่านidองค์ประกอบบนเพียงแค่นำเข้าActivatedRoute เช่น

import { ActivatedRoute } from '@angular/router'

และบนngOnInitคือที่ที่คุณดึงข้อมูล

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

คุณสามารถอ่านเพิ่มเติมในบทความนี้ https://www.tektutorialshub.com/angular-passing-parameters-to-route/


12
ถ้าฉันต้องการส่งวัตถุที่ซับซ้อน ฉันไม่ต้องการที่จะขยายเส้นทางของฉันไปไร้สาระไร้สาระ :(
cmxl

@cmxl ใช้บริการที่แบ่งปันแล้ว
Stanislasdrg Reinstate Monica

สิ่งที่ฉันกำลังมองหา ขอบคุณ!
Akhil

21

Angular 7.2.0 แนะนำวิธีใหม่ในการส่งผ่านข้อมูลเมื่อทำการสำรวจระหว่างส่วนประกอบที่กำหนดเส้นทาง:

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

หรือ:

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

หากต้องการอ่านสถานะคุณสามารถเข้าถึงwindow.history.stateคุณสมบัติหลังจากการนำทางเสร็จสิ้น:

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

  constructor(public activatedRoute: ActivatedRoute) {}

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

4
ใช้งานไม่ได้สำหรับฉันwindow.history.stateส่งคืนสิ่งที่ต้องการ{navigationId: 2}แทนที่จะส่งคืนวัตถุที่ส่งผ่าน
Louis

@Louis คุณกำลังใช้แองกูลาร์รุ่นใดอยู่
Washington Soares Braga

ฉันใช้แองกูลาร์เวอร์ชัน 8.1.0
Louis

ฉันเห็นสิ่งเดียวกันกับหลุยส์ด้วยรุ่นที่ต่ำกว่าของเขา แต่ก็ยังสูงพอที่ควรจะมีคุณลักษณะนั้น
WindowsWeenie

เป็นส่วนหนึ่งของวัตถุที่ส่งคืนโดยnavigationIdมีการเพิ่มค่า หากไม่มีการเพิ่มข้อมูลnavigationIdจะมีการแสดงเฉพาะ ดูข้อมูลที่ส่งผ่านย้อนกลับหรือรีเฟรชแอปของคุณและเรียกการกระทำที่เพิ่มข้อมูลไปยังเส้นทางและนำทางไปยังเส้นทางถัดไป (เช่นการคลิกปุ่ม) นี้จะเพิ่มข้อมูลของคุณไปยังวัตถุ
Alf Moh

12

บุคคลที่ฉลาดสุด ๆ (tmburnell) ที่ไม่ใช่ฉันแนะนำให้เขียนข้อมูลเส้นทางใหม่อีกครั้ง:

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

เท่าที่เห็นที่นี่ในความคิดเห็น

ฉันหวังว่าบางคนจะพบว่ามีประโยชน์นี้


7
เพิ่งค้นพบเกี่ยวกับเรื่องนี้และฉันรู้สึกเหมือนฉันต้องจุด StackOverflow บาง :)
Tim.Burnell

11

ฉันนี้วิธีการอื่นไม่ดีสำหรับปัญหานี้ วิธีที่ดีที่สุดสำหรับฉันคือQuery-ParameterโดยRouterเชิงมุมที่มี 2 วิธี:

ผ่านพารามิเตอร์แบบสอบถามโดยตรง

ด้วยรหัสนี้คุณสามารถนำทางurlโดยparamsในรหัส html ของคุณ:

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

ผ่านพารามิเตอร์แบบสอบถามโดย Router

คุณต้องฉีดเราเตอร์ภายในสิ่งที่constructorชอบ:

constructor(private router:Router){

}

ตอนนี้ใช้สิ่งที่ชอบ:

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

ตอนนี้ถ้าคุณต้องการอ่านจากที่Routerอื่นComponentคุณต้องใช้ของActivatedRouteชอบ:

constructor(private activateRouter:ActivatedRouter){

}

และsubscribeนั่น:

  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}}); สามารถถูกแทนที่ด้วย this.router.navigate (['/ product-list'], {queryParams: {serviceId}});
Ramakant Singh เมื่อ

10

มันคือ 2019 และคำตอบมากมายที่นี่จะใช้ได้ขึ้นอยู่กับสิ่งที่คุณต้องการจะทำ หากคุณต้องการที่จะผ่านในบางสถานะภายในไม่ปรากฏใน URL (params, แบบสอบถาม) คุณสามารถใช้stateตั้งแต่ 7.2 (ตามที่ฉันได้เรียนรู้ในวันนี้ :))

จากบล็อก (เครดิต Tomasz Kula) - คุณนำทางไปยังเส้นทาง ....

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

... จากเทมเพลต HTML: <a routerLink="/details" [state]="{ hello: 'world' }">Go</a>

และหยิบมันขึ้นมาในองค์ประกอบเป้าหมาย:

constructor(public activatedRoute: ActivatedRoute) {}

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

ช้า แต่หวังว่านี่จะช่วยให้คนที่มี Angular ล่าสุด


6

วิธีแก้ปัญหาด้วย ActiveRoute (ถ้าคุณต้องการผ่านวัตถุตามเส้นทาง - ใช้ JSON.stringfy / JSON.parse):

เตรียมวัตถุก่อนส่ง:

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

}

รับวัตถุของคุณในองค์ประกอบปลายทาง:

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
ใช้งานได้ แต่จะเกิดอะไรขึ้นถ้าฉันไม่ต้องการเปิดเผยข้อมูลทั้งหมดใน URL
mpro

คุณสามารถเข้ารหัสข้อมูลที่ใส่ไว้ใน params หลังจากนั้นเข้ารหัสในองค์ประกอบเป้าหมาย
แมงป่อง

ฉันได้สร้างบริการสำหรับการแบ่งปันข้อมูล
mpro

มีsuper.ngOnInit();ไว้เพื่ออะไร
Andrea Gherardi

5

วิธีที่ 3 เป็นวิธีที่ใช้กันมากที่สุดในการแชร์ข้อมูลระหว่างส่วนประกอบ คุณอาจฉีดบริการสินค้าที่คุณต้องการใช้ในองค์ประกอบที่เกี่ยวข้อง

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
บริการได้รับอินสแตนซ์เมื่อเปลี่ยนเส้นทาง ดังนั้นคุณจึงหลวมข้อมูล
Jimmy Kane

1
@JimmyKane คุณพูดถึงเฉพาะเมื่อหน้ารีเฟรช แต่ถ้ามันไม่รีเฟรชหน่วยความจำจะยังคงอยู่ในบริการ นี่ควรจะเป็นพฤติกรรมเริ่มต้นเนื่องจากมันจะบันทึกการโหลดหลายครั้ง
Aaron Rabinowitz

1
@AaronRabinowitz ถูกต้อง ขอโทษสำหรับความสับสน. และขออภัยสำหรับการลงคะแนน หวังว่าฉันจะยกเลิกตอนนี้ได้ สายเกินไป. เป็นเรื่องใหม่สำหรับเชิงมุม 2 และปัญหาของฉันในการลองวิธีการของคุณคือฉันมีบริการที่มอบให้กับส่วนประกอบหลายอย่างและไม่ได้ให้ผ่านโมดูลแอป
Jimmy Kane

3

ผ่านการใช้ JSON

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

7
นี่จะเป็นคำตอบที่ดีกว่าถ้าคุณสามารถแสดงวิธีการใช้พารามิเตอร์ในองค์ประกอบการรับเช่นกัน - เส้นทางทั้งหมดที่ใช้ หมายความว่าถ้ามีคนไม่ทราบวิธีการส่งพารามิเตอร์เขาจะไม่ทราบวิธีการใช้พารามิเตอร์นี้ในส่วนประกอบที่ได้รับด้วย :)
Alfa Bravo

ข้อเสียของข้อนี้คือมีข้อ จำกัด ขนาดในการสอบถามและบางครั้งคุณไม่ต้องการให้เห็นคุณสมบัติของวัตถุในแถบที่อยู่
CM

3

ใช้บริการสาธารณะเพื่อจัดเก็บข้อมูลด้วยดัชนีที่กำหนดเอง จากนั้นส่งดัชนีที่กำหนดเองนั้นด้วย queryParam วิธีการนี้มีความยืดหยุ่นมากขึ้น

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

บอกว่าคุณมี

  1. component1.ts
  2. component1.html

และคุณต้องการที่จะผ่านข้อมูลไปยังcomponent2.ts

  • in component1.ts เป็นตัวแปรที่มีข้อมูลบอกว่า

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

คุณสามารถใช้ BehaviorSubject เพื่อใช้ข้อมูลร่วมกันระหว่างคอมโพเนนต์ที่กำหนดเส้นทาง BehaviorSubject เก็บค่าหนึ่งค่า เมื่อสมัครเป็นสมาชิกมันจะปล่อยค่าทันที หัวเรื่องไม่ได้เก็บค่าไว้

ในการบริการ

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

ในองค์ประกอบที่คุณสามารถสมัครเป็นสมาชิก BehaviorSubject นี้

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

หมายเหตุ: หัวเรื่องไม่ทำงานที่นี่จำเป็นต้องใช้หัวเรื่องเรื่องพฤติกรรมเท่านั้น

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.