Làm cách nào để truyền dữ liệu đến các thành phần định tuyến Angular?


268

Trong một trong các mẫu Angular 2 của tôi ( FirstComponent ), tôi có một nút

First.component.html

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

Mục tiêu của tôi là đạt được:

Nhấn vào nút -> định tuyến đến thành phần khác trong khi bảo quản dữ liệu và không sử dụng thành phần khác làm chỉ thị.

Đây là những gì tôi đã cố gắng ...

TIẾP CẬN 1ST

Trong cùng một quan điểm, tôi đang lưu trữ việc thu thập dữ liệu tương tự dựa trên sự tương tác của người dùng.

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

Thông thường tôi sẽ định tuyến đến SecondComponent bằng

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

cuối cùng truyền dữ liệu qua

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

trong khi định nghĩa của liên kết với các tham số sẽ là

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

Vấn đề với cách tiếp cận này là tôi đoán tôi không thể truyền dữ liệu phức tạp (ví dụ: một đối tượng như property3) trong url;

TIẾP CẬN 2ND

Một sự thay thế sẽ bao gồm SecondComponent là chỉ thị trong FirstComponent.

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

Tuy nhiên tôi muốn định tuyến đến thành phần đó, không bao gồm nó!

TIẾP CẬN 3RD

Giải pháp khả thi nhất tôi thấy ở đây sẽ là sử dụng Dịch vụ (ví dụ: FirstComponentService) để

  • lưu trữ dữ liệu (_firstComponentService.storeData ()) trên tuyếnWithData () trong FirstComponent
  • lấy dữ liệu (_firstComponentService.retrieveData ()) trong ngOnInit () trong SecondComponent

Trong khi phương pháp này có vẻ hoàn toàn khả thi, tôi tự hỏi liệu đây có phải là cách dễ nhất / thanh lịch nhất để đạt được mục tiêu.

Nói chung, tôi muốn biết liệu tôi có thiếu các cách tiếp cận tiềm năng khác để truyền dữ liệu giữa các thành phần hay không, đặc biệt là với số lượng mã ít có thể hơn


6
3 cách đơn giản để chia sẻ dữ liệu qua tuyến đường - angulartutorial.net/2017/12/ từ
Prashobh

2
cảm ơn @Prashobh. Pass data using Query Parameterslà những gì tôi đang tìm kiếm. liên kết của bạn đã lưu ngày của tôi.
Raj

Angular 7.2 hiện có tính năng mới để truyền dữ liệu giữa các tuyến bằng cách sử dụng statekiểm tra PR để biết thêm chi tiết. Một số thông tin hữu ích ở đây
AzizKapProg

@Prashobh Cảm ơn rất nhiều. Liên kết mà bạn đã chia sẻ rất hữu ích
vandu

Câu trả lời:


193

cập nhật 4.0.0

Xem tài liệu góc để biết thêm chi tiết https://angular.io/guide/router#fetch-data-b Before-nigigating

nguyên

Sử dụng một dịch vụ là cách để đi. Trong thông số tuyến đường, bạn chỉ nên truyền dữ liệu mà bạn muốn được phản ánh trong thanh URL của trình duyệt.

Xem thêm https://angular.io/docs/ts/latest/cookbook/component-cransication.html#!#bidirectional-service

Các bộ định tuyến được vận chuyển với RC.4 giới thiệu lại 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();
  }
}

Xem thêm Plunker tại https://github.com/angular/angular/issues/9757#issuecomment-229847781


4
Câu trả lời này có còn hợp lệ cho Angular 2.1.0 không?
smartmouse

9
Dữ liệu bộ định tuyến RC.4 chỉ dành cho dữ liệu tĩnh. Bạn không thể gửi dữ liệu khác nhau đến cùng một tuyến, nó luôn phải là cùng một dữ liệu phải không?
aycanadal

1
Không, sử dụng một dịch vụ chia sẻ cho trường hợp sử dụng này.
Günter Zöchbauer

8
Trong Angular 5, dù sao đi nữa, bạn sẽ có thể ... ngOnInit() { this.myVar = this.route.snapshot.data['some_data']; }
Roger

5
Nếu bạn có thể sử dụng Angular v7.2, nó cho phép chuyển trạng thái ngay bây giờ trong bộ định tuyến bằng cách sử dụng NavigationExtras- stackoverflow.com/a/54879389/1148107
mtpultz

67

Tôi nghĩ vì chúng ta không có loại $ rootScope ở góc 2 như ở góc 1.x. Chúng ta có thể sử dụng dịch vụ / lớp chia sẻ 2 góc trong khi trong ngOnDestroy truyền dữ liệu cho dịch vụ và sau khi định tuyến lấy dữ liệu từ dịch vụ trong hàm ngOnInit :

Ở đây tôi đang sử dụng DataService để chia sẻ đối tượng anh hùng:

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

Truyền đối tượng từ thành phần trang đầu tiên:

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

Lấy đối tượng từ thành phần trang thứ hai:

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

Đây là một ví dụ: plunker


Điều này thật đẹp, nhưng làm thế nào phổ biến trong cộng đồng Ng2 là thế này? Tôi không thể nhớ đã đọc nó trong các tài liệu ...
Alfa Bravo

1
So với các tùy chọn khác như tham số url hoặc lưu trữ trình duyệt khác, điều này có vẻ tốt hơn đối với tôi. Tôi cũng không thấy trong bất kỳ tài liệu nào để làm việc như thế này.
Utpal Kumar Das

2
Nó có hoạt động khi người dùng mở một tab mới và sao chép dán tuyến thành phần thứ hai không? Tôi có thể lấy this.hero = this.dataService.herokhông? Tôi sẽ nhận được các giá trị?

Điều này thực sự rất đơn giản và mọi nhà phát triển Angular đều biết nhưng vấn đề là một khi bạn làm mới bạn mất dữ liệu trong các dịch vụ. Người dùng sẽ phải làm lại tất cả mọi thứ.
Santosh Kadam

@SantoshKadam câu hỏi là "Làm cách nào để truyền dữ liệu đến các thành phần định tuyến Angular?" vì vậy việc truyền dữ liệu bằng các hàm ngOnDestroy và ngOnInit là một cách và luôn luôn đơn giản là tốt nhất. Nếu người dùng cần lấy dữ liệu sau khi tải lại thì cần phải lưu dữ liệu trong bộ lưu trữ vĩnh viễn và đọc lại từ bộ lưu trữ đó.
Utpal Kumar Das

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

Cách tốt nhất để làm điều đó trong 6 góc hoặc các phiên bản khác tôi hy vọng là chỉ cần xác định đường dẫn của bạn với lượng dữ liệu bạn muốn vượt qua

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

như bạn có thể thấy từ định nghĩa tuyến đường của mình, tôi đã thêm phần /:idđứng vào dữ liệu tôi muốn truyền cho thành phần thông qua bộ điều hướng bộ định tuyến. Do đó, mã của bạn sẽ trông như thế

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

để đọc idthành phần, chỉ cần nhập ActivatedRoute như

import { ActivatedRoute } from '@angular/router'

và trên đó ngOnInitlà nơi bạn lấy dữ liệu

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

bạn có thể đọc thêm trong bài viết này https://www.tektutorialshub.com/angular-passing-parameter-to-route/


12
Nếu tôi muốn gửi một đối tượng phức tạp thì sao? tôi không muốn làm
hỏng

@cmxl Sử dụng dịch vụ chia sẻ rồi.
Stanislasdrg Phục hồi Monica

Chính xác những gì tôi đang tìm kiếm. Cảm ơn!
Akhil

21

Angular 7.2.0 đã giới thiệu cách truyền dữ liệu mới khi điều hướng giữa các thành phần được định tuyến:

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

Hoặc là:

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

Để đọc trạng thái, bạn có thể truy cập thuộc window.history.statetính sau khi điều hướng kết thúc:

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

  constructor(public activatedRoute: ActivatedRoute) {}

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

4
không làm việc cho tôi, window.history.statetrả lại một cái gì đó như {navigationId: 2}thay vì trả lại đối tượng tôi đã truyền vào.
Louis

@Louis bạn đang sử dụng phiên bản Angular nào?
Washington Soares Braga

Tôi đang sử dụng phiên bản góc 8.1.0
Louis

Tôi đang thấy điều tương tự như Louis, với phiên bản thấp hơn phiên bản của anh ấy nhưng vẫn đủ cao để nó có tính năng đó.
WindowsWeenie

như một phần của đối tượng được trả về, navigationIdvới một giá trị được thêm vào. Nếu không có dữ liệu được thêm vào, chỉ có navigationIdsẽ được hiển thị. Được xem dữ liệu đã truyền, quay lại hoặc làm mới ứng dụng của bạn và kích hoạt lại hành động thêm dữ liệu vào tuyến đường và điều hướng đến tuyến đường tiếp theo (ví dụ: nhấp vào nút). Điều này sau đó sẽ thêm dữ liệu của bạn vào đối tượng.
Alf Moh

12

Một số người siêu thông minh (tmburnell) không phải tôi đề nghị viết lại dữ liệu tuyến đường:

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

Như đã thấy ở đây trong các ý kiến.

Tôi hy vọng ai đó sẽ tìm thấy điều này hữu ích


7
chỉ cần tìm hiểu về điều này và tôi cảm thấy như tôi cần một số điểm stackoverflow :)
Tim.Burnell

11

Tôi đây là cách tiếp cận khác không tốt cho vấn đề này. Tôi nghĩ cách tiếp cận tốt nhất là Tham số truy vấn theo Routergóc có 2 cách:

Truyền tham số truy vấn trực tiếp

Với mã này, bạn có thể điều hướng đến urlbằng paramsmã html của mình:

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

Truyền tham số truy vấn bằng Router

Bạn phải tiêm bộ định tuyến trong phạm vi constructornhư của bạn :

constructor(private router:Router){

}

Bây giờ sử dụng như thế:

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

Bây giờ nếu bạn muốn đọc từ Routerkhác, Componentbạn phải sử dụng ActivatedRoutenhư:

constructor(private activateRouter:ActivatedRouter){

}

subscriberằng:

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

this .router.navigate (['/ sản phẩm-list'], {queryParams: {serviceId: serviceId}}); có thể được thay thế bằng this.router.navigate (['/ sản phẩm-list'], {queryParams: {serviceId}});
Ramakant Singh

10

Đó là năm 2019 và nhiều câu trả lời ở đây sẽ hoạt động, tùy thuộc vào những gì bạn muốn làm. Nếu bạn muốn vượt qua trong một số trạng thái nội bộ không hiển thị trong URL (params, truy vấn), bạn có thể sử dụng statetừ 7.2 (như tôi đã học hôm nay :)).

Từ blog (tín dụng Tomasz Kula) - bạn điều hướng đến tuyến đường ....

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

... từ mẫu HTML: <a routerLink="/details" [state]="{ hello: 'world' }">Go</a>

Và để chọn nó trong thành phần đích:

constructor(public activatedRoute: ActivatedRoute) {}

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

Muộn, nhưng hy vọng điều này sẽ giúp ai đó với Angular gần đây.


6

Giải pháp với ActiveRoute (nếu bạn muốn truyền đối tượng theo tuyến - sử dụng JSON.opesfy / JSON.parse):

Chuẩn bị đối tượng trước khi gửi:

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

}

Nhận đối tượng của bạn trong thành phần đích:

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
Điều đó hoạt động, nhưng nếu tôi không muốn tiết lộ tất cả dữ liệu trong URL thì sao?
mpro

Bạn có thể mã hóa dữ liệu đưa nó vào params, sau đó mã hóa trong thành phần đích.
Bọ cạp

Tôi đã tạo dịch vụ để chia sẻ dữ liệu.
mpro 6/12/18

Cái đó super.ngOnInit();để làm gì?
Andrea Gherardi

5

Cách tiếp cận thứ 3 là cách phổ biến nhất để chia sẻ dữ liệu giữa các thành phần. bạn có thể tiêm dịch vụ vật phẩm mà bạn muốn sử dụng trong thành phần liên quan.

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
Dịch vụ này được khởi tạo ngay khi chuyển tuyến. Vì vậy, bạn mất dữ liệu
Jimmy Kane

1
@JimmyKane Bạn nói về cụ thể khi trang làm mới nhưng nếu nó không làm mới thì bộ nhớ vẫn được lưu trong một dịch vụ. Đây phải là hành vi mặc định vì nó sẽ tiết kiệm tải nhiều lần.
Aaron Rabinowitz

1
@AaronRabinowitz đúng. Xin lỗi vì sự nhầm lẫn. Và xin lỗi vì đã bỏ phiếu. Ước gì tôi có thể hoàn tác nó bây giờ. Quá muộn. Là điểm mới đối với góc 2 và vấn đề của tôi khi thử cách tiếp cận của bạn là tôi đã cung cấp dịch vụ cho nhiều thành phần và không được cung cấp qua mô-đun ứng dụng.
Jimmy Kane

3

Chuyển qua sử dụng JSON

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

7
Đây sẽ là một câu trả lời tốt hơn nếu bạn có thể chỉ ra cách tham số được tiêu thụ trong thành phần nhận - toàn bộ tuyến đường mà nó đi. Có nghĩa là nếu ai đó không biết cách truyền tham số, anh ta cũng sẽ không biết cách sử dụng tham số này trong thành phần nhận. :)
Alfa Bravo

Một bất lợi cho điều này là có giới hạn kích thước đối với chuỗi truy vấn và đôi khi bạn không muốn các thuộc tính đối tượng hiển thị trên thanh địa chỉ.
CM

3

sử dụng dịch vụ chia sẻ để lưu trữ dữ liệu với chỉ mục tùy chỉnh. sau đó gửi chỉ mục tùy chỉnh đó với queryParam. Cách tiếp cận này linh hoạt hơn .

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

nói rằng bạn có

  1. thành phần1.ts
  2. thành phần1.html

và bạn muốn truyền dữ liệu đến thành phần2.ts .

  • trong thành phần1.ts là một biến có dữ liệu nói

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

Bạn có thể sử dụng BehaviorSubject để chia sẻ dữ liệu giữa các thành phần được định tuyến. Một BehaviorSubject giữ một giá trị. Khi được đăng ký, nó phát ra giá trị ngay lập tức. Một chủ đề không giữ một giá trị.

Trong dịch vụ.

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

Trong thành phần, bạn có thể đăng ký BehaviorSubject này.

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

Lưu ý: Chủ đề sẽ không hoạt động ở đây, Chỉ cần sử dụng Chủ đề hành vi.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.