如何将数据传递到Angular路由组件?


268

在我的Angular 2路线模板之一(FirstComponent)中,我有一个按钮

first.component.html

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

我的目标是实现:

单击按钮->路由到另一个组件,同时保留数据并且不使用另一个组件作为指令。

这就是我尝试过的...

第一种方法

在同一视图中,我存储的是基于用户交互收集的相同数据。

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

这种方法的问题在于,我想我无法在URL中传递复杂的数据(例如,诸如property3之类的对象)。

第二种方法

另一种选择是将SecondComponent作为指令包含在FirstComponent中。

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

但是我想路由到该组件,而不包括它!

3RD方法

我在这里看到的最可行的解决方案是使用服务(例如FirstComponentService)来

  • 数据(_firstComponentService.storeData())存储在FirstComponent中的routeWithData()上
  • 检索SecondComponentngOnInit()中的数据(_firstComponentService.retrieveData())

尽管这种方法似乎完全可行,但我想知道这是否是实现目标的最简单/最优雅的方法。

总的来说,我想知道我是否缺少其他可能的方法来在组件之间传递数据,尤其是在代码量较少的情况下


6
通过路由共享数据的3种简单方法-angulartutorial.net/2017/12/…–
Prashobh,

2
谢谢@Prashobh。Pass data using Query Parameters这就是我想要的。您的链接挽救了我的生活。
拉吉

Angular 7.2现在具有新功能,可以使用PRstate检查更多详细信息,从而在路线之间传递数据。一些有用的信息在这里
AzizKapProg '19

@Prashobh非常感谢。您共享的链接非常有用
vandu

Answers:


193

更新4.0.0

有关更多详细信息,请参阅Angular文档https://angular.io/guide/router#fetch-data-before-navigating

原版的

使用服务是必经之路。在路由参数中,您只应传递要在浏览器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();
  }
}

另请参阅https://github.com/angular/angular/issues/9757#issuecomment-229847781的柱塞


4
这个答案对Angular 2.1.0仍然有效吗?
smartmouse

9
RC.4路由器数据仅用于静态数据。您不能将不同的数据发送到相同的路由,它总是必须是相同的数据,对吗?
aycanadal

1
不,在此用例中使用共享服务。
君特Zöchbauer

8
无论如何,在Angular 5中,您应该能够... ngOnInit() { this.myVar = this.route.snapshot.data['some_data']; }
Roger

5
如果你能够利用角度V7.2它允许使用现在正在状态中的路由器NavigationExtras- stackoverflow.com/a/54879389/1148107
mtpultz

67

我认为,既然在角度2中没有$ rootScope之类的东西,而在角度1.x中则没有。在ngOnDestroy中,我们可以使用angular 2共享服务/类,同时在ngOnInit函数中将数据传递给服务,并在路由之后从服务中获取数据:

在这里,我正在使用DataService共享英雄对象:

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

从首页组件传递对象:

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

从第二个页面组件获取对象:

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

下面是一个例子:plunker


这很漂亮,但这在Ng2社区中有多普遍?我不记得在文档中读过它吗
Alfa Bravo

1
与URL参数或其他浏览器存储之类的其他选项相比,在我看来这更好。我也没有看到任何文档可以像这样工作。
Utpal Kumar Das

2
当用户打开一个新选项卡并复制粘贴第二个组件路线时,它是否起作用?我可以取货this.hero = this.dataService.hero吗?我会得到值吗?

这确实非常简单,每个Angular开发人员都知道,但是问题在于,一旦刷新服务中的松散数据。用户将不得不再次做所有的事情。
Santosh Kadam19年

@SantoshKadam问题是“如何将数据传递到Angular路由组件?” 因此,通过ngOnDestroy和ngOnInit函数传递数据是一种方法,最好始终保持简单。如果用户需要在重新加载后获取数据,则需要将数据保存在永久存储中并从该存储中再次读取。
Utpal Kumar Das

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

最好是在angular 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然后使用共享服务。
Stanislasdrg恢复莫妮卡

正是我想要的。谢谢!
阿基尔

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您使用的是哪个Angular版本?
华盛顿·苏亚雷斯·布拉加

我正在使用角度版本8.1.0
路易斯

我看到的是与Louis相同的东西,虽然版本比他低,但仍然足够高,应该具有该功能。
WindowsWeenie

作为返回对象的一部分,将navigationId添加一个值。如果未添加任何数据,则仅navigationId显示。要查看传递的数据,请返回或刷新您的应用,然后重新触发将数据添加到路线并导航到下一条路线的操作(例如,单击按钮)。然后,这会将您的数据添加到该对象。
阿尔夫·莫

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

我这另一种方法对这个问题不好。我觉得最好的方法是按角度查询参数Router它有两种方式:

直接传递查询参数

有了这个代码,你可以浏览到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,则必须使用ActivatedRoutelike:

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中不可见的某些内部状态(参数,查询),则可以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

您可以加密数据并将其放入参数中,然后在目标组件中进行加密。
蝎子

我已经创建了用于数据共享的服务
mpro

那是super.ngOnInit();为了什么
安德烈·格拉迪

5

第三种方法是在组件之间共享数据的最常用方法。您可以将要使用的项目服务注入相关组件中。

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您专门讲的是页面刷新时的内容,但是如果页面不刷新,则内存仍保存在服务中。这应该是默认行为,因为它将节省很多时间。
亚伦·拉比诺维兹

1
@AaronRabinowitz对。对困惑感到抱歉。非常抱歉。希望我现在可以撤消它。太晚了。我是angular 2的新手,尝试使用这种方法时遇到的问题是我有为许多组件提供的服务,而没有通过app模块提供的服务。
吉米·凯恩

3

使用JSON传递

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

7
如果您还可以显示参数如何在接收组件中使用-它将采用的整个路径,则将是一个更好的答案。这意味着如果某人不知道如何传递参数,那么他也将不知道如何在接收组件中使用此参数。:)
阿尔法·布拉沃

这样做的缺点是查询字符串的大小受到限制,有时您不希望对象属性在地址栏中可见。
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

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

注意:主题在这里不起作用,仅需要使用行为主题。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.