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

dra*_*mnl 216 typescript angular

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

first.component.html

<div class="button" click="routeWithData()">Pass data and route</div>
Run Code Online (Sandbox Code Playgroud)

我的目标是实现:

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

这就是我试过的......

第一种方法

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

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
    }
}
Run Code Online (Sandbox Code Playgroud)

一般情况下我路由SecondComponent通过

 this._router.navigate(['SecondComponent']);
Run Code Online (Sandbox Code Playgroud)

最终传递数据

 this._router.navigate(['SecondComponent', {p1: this.property1, p2: property2 }]);
Run Code Online (Sandbox Code Playgroud)

而带参数的链接的定义是

@RouteConfig([
      // ...
      { path: '/SecondComponent/:p1:p2', name: 'SecondComponent', component: SecondComponent} 
)]
Run Code Online (Sandbox Code Playgroud)

这种方法的问题是我猜我无法传递复杂数据(例如像property3这样的对象)in-url;

第二种方法

另一种方法是在FirstComponent中包含SecondComponent作为指令.

  <SecondComponent [p3]="property3"></SecondComponent>
Run Code Online (Sandbox Code Playgroud)

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

第三种方法

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

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

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

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

Gün*_*uer 159

更新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) {}
Run Code Online (Sandbox Code Playgroud)
const routes: RouterConfig = [
  {path: '', redirectTo: '/heroes', pathMatch : 'full'},
  {path : 'heroes', component : HeroDetailComponent, data : {some_data : 'some value'}}
];
Run Code Online (Sandbox Code Playgroud)
class HeroDetailComponent {
  ngOnInit() {
    this.sub = this.route
      .data
      .subscribe(v => console.log(v));
  }

  ngOnDestroy() {
    this.sub.unsubscribe();
  }
}
Run Code Online (Sandbox Code Playgroud)

另见Plunker,网址:https://github.com/angular/angular/issues/9757#issuecomment-229847781

  • RC.4路由器数据仅用于静态数据.您不能将不同的数据发送到它始终具有的相同路径,我错了吗? (6认同)
  • 无论如何,在Angular 5中,你应该能够......`ngOnInit(){this.myVar = this.route.snapshot.data ['some_data']; }` (6认同)
  • 这个答案对Angular 2.1.0仍然有效吗? (4认同)
  • 如果您能够使用Angular v7.2,则现在可以使用“ NavigationExtras”在路由器中传递状态-/sf/answers/3841557261/ (4认同)

Utp*_*Das 60

我想因为我们在角度2中没有$ rootScope类似于角度1.x. 我们可以使用angular 2共享服务/类,而在ngOnDestroy中将数据传递给服务,并在路由后从ngOnInit函数中的服务获取数据:

这里我使用DataService来共享英雄对象:

import { Hero } from './hero';
export class DataService {
  public hero: Hero;
}
Run Code Online (Sandbox Code Playgroud)

从第一页组件传递对象:

 ngOnDestroy() {
    this.dataService.hero = this.hero; 
 }
Run Code Online (Sandbox Code Playgroud)

从第二页组件获取对象:

 ngOnInit() {
    this.hero = this.dataService.hero; 
 }
Run Code Online (Sandbox Code Playgroud)

这是一个例子:plunker

  • 当用户打开一个新选项卡并复制粘贴第二个组件路线时,它是否起作用?我可以获取`this.hero = this.dataService.hero`吗?我会得到值吗? (2认同)
  • 这确实非常简单,每个 Angular 开发人员都知道,但问题是一旦您刷新服务中的松散数据。用户将不得不再次执行所有操作。 (2认同)

Was*_*aga 46

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' } });
  }
}
Run Code Online (Sandbox Code Playgroud)

或者:

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

要读取状态,您可以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))
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 对我来说不起作用,“window.history.state”返回类似“{navigationId:2}”的内容,而不是返回我传入的对象。 (10认同)
  • 浏览器对状态对象的数据大小有 640k 的限制。/sf/ask/1709811981/ (2认同)

Dan*_*ila 21

<div class="button" click="routeWithData()">Pass data and route</div>
Run Code Online (Sandbox Code Playgroud)

以角度6或其他版本执行此操作的最简单方法我希望只需使用您要传递的数据量来定义路径

{path: 'detailView/:id', component: DetailedViewComponent}
Run Code Online (Sandbox Code Playgroud)

正如你从我的路线定义中看到的那样,我已经/:id通过路由器导航添加了要传递给组件的数据.因此,您的代码看起来像

<a class="btn btn-white-view" [routerLink]="[ '/detailView',list.id]">view</a>
Run Code Online (Sandbox Code Playgroud)

为了读取id组件上,只需要导入ActivatedRoute

import { ActivatedRoute } from '@angular/router'
Run Code Online (Sandbox Code Playgroud)

并在ngOnInit是您检索数据

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

你可以在这篇文章中阅读更多内容 https://www.tektutorialshub.com/angular-passing-parameters-to-route/

  • 如果我想发送一个复杂的对象怎么办?我不想让我的路线膨胀到无法维持的废话:( (10认同)
  • @cmxl 那么使用共享服务。 (2认同)

PeS*_*PeS 21

现在是 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))
  }
Run Code Online (Sandbox Code Playgroud)

晚了,但希望这对最近使用 Angular 的人有所帮助。


Sha*_*wal 12

我从这个页面查看了每个解决方案(并尝试了一些),但我不相信我们必须实现一种黑客式的方式来实现路由之间的数据传输。

simple 的另一个问题history.state是,如果您在state对象中传递特定类的实例,则在接收它时它将不是该实例。但它将是一个简单的 JavaScript 对象。

所以在我的Angular v10 (Ionic v5) 应用程序中,我做了这个——

this.router.navigateByUrl('/authenticate/username', {
    state: {user: new User(), foo: 'bar'}
});
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

在导航组件 ( '/authenticate/username') 中,在ngOnInit()方法中,我使用this.router.getCurrentNavigation().extras.state-

ngOnInit() {
    console.log('>>authenticate-username:41:',
        this.router.getCurrentNavigation().extras.state);
}
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

我得到了传递的所需数据-

在此处输入图片说明

  • 精彩的答案!重要的是要记住,访问“state”(在路由到新页面之后)对我来说仅适用于“constructor”,而不是在“ngOnInit”内部。那是因为 ```getCurrentNavigation()``` 为空。 (6认同)
  • @Itay 我同意。我正在使用 Angular 11。当前导航范围在 ngOnInit() 之前结束。所以我必须从构造函数中获取状态值。 (2认同)
  • 如果刷新页面,数据会丢失 (2认同)

O95*_*O95 11

路线:

{ path: 'foo-route', component: FooComponent, data: { myData: false } },
Run Code Online (Sandbox Code Playgroud)

在组件中访问数据对象一次:

pipe(take(1))立即取消订阅,因此不会出现内存泄漏,无需手动取消订阅

constructor(private activatedRoute: ActivatedRoute) { ... }

ngOnInit(): void {
  this.activatedRoute.data.pipe(take(1)).subscribe((data) => {
    console.log(data); // do something with the data
  });
}
Run Code Online (Sandbox Code Playgroud)
  • 记得导入需要的东西

编辑:新的firstValueFrom()可能会更好


sco*_*ion 9

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

}
Run Code Online (Sandbox Code Playgroud)

在目标组件中接收您的对象:

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

}
Run Code Online (Sandbox Code Playgroud)

  • 那行得通,但是如果我不想公开 URL 中的所有数据怎么办? (3认同)

ter*_*ary 9

不是我的一些超级聪明人(tmburnell)建议重写路线数据:

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

可以看出这里的评论。

我希望有人会觉得有用

  • 刚刚发现这一点,我觉得我需要一些 stackoverflow 点:) (24认同)

小智 7

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

直接传递查询参数

有了这个代码,你可以浏览到url通过params在HTML代码中:

<a [routerLink]="['customer-service']" [queryParams]="{ serviceId: 99 }"></a>
Run Code Online (Sandbox Code Playgroud)

通过传递查询参数 Router

您必须在自己的路由器中注入路由器constructor

constructor(private router:Router){

}
Run Code Online (Sandbox Code Playgroud)

现在像这样使用:

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

现在,如果您想阅读Router其他内容Component,则必须使用ActivatedRoutelike:

constructor(private activateRouter:ActivatedRouter){

}
Run Code Online (Sandbox Code Playgroud)

并且subscribe

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


小智 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
}
Run Code Online (Sandbox Code Playgroud)

  • 切换路由时,服务会被实例化.所以你丢失了数据 (3认同)