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

时间:2016-04-25 08:10:46

标签: angular typescript

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

first.component.html

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

我的目标是要实现:

  

按钮点击 - &gt;在保留数据的同时路由到另一个组件,而不使用其他组件作为指令。

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

第一个方法

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

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

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

2ND APPROACH

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

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

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

3RD APPROACH

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

  • 存储第一个组件中的routeWithData()上的数据(_firstComponentService.storeData())
  • 检索 第二组件
  • ngOnInit()中的数据(_firstComponentService.retrieveData())

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

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

18 个答案:

答案 0 :(得分:129)

更新4.0.0

有关详细信息https://angular.io/guide/router#fetch-data-before-navigating

,请参阅Angular文档

<强>原始

使用服务是可行的方法。在路径参数中,您应该只传递要在浏览器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

上的Plunker

答案 1 :(得分:51)

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

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

String.getBytes()

从第一页组件传递对象:

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

从第二页组件中获取对象:

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

以下是一个示例:plunker

答案 2 :(得分:12)

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

答案 3 :(得分:6)

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

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

如评论中的here所示。

我希望有人会觉得有用

答案 4 :(得分:6)

我浏览了此页面上的每个解决方案(并尝试了几种),但我不相信我们必须采取一种hack-ish的方式来实现路由之间的数据传输。

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

因此,在我的 Angular v10 (Ionic v5)应用程序中,我做到了-

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

enter image description here

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

打印数据
ngOnInit() {
    console.log('>>authenticate-username:41:',
        this.router.getCurrentNavigation().extras.state);
}

enter image description here

我得到了所需的数据,已通过-

enter image description here

答案 5 :(得分: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
}

答案 6 :(得分:4)

使用JSON传递

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

答案 7 :(得分:4)

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

}

答案 8 :(得分:4)

我这是另一种不适用于此问题的方法。 我觉得最好的方法是按Router的角度使用 Query-Parameter ,它有两种方式:

  

直接传递查询参数

使用此代码,您可以在HTML代码中按url导航到params

<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的{​​{1}}中读取内容,则必须像这样使用Component

ActivatedRoute

constructor(private activateRouter:ActivatedRouter){ }

subscribe

答案 9 :(得分:1)

使用共享服务来存储带有自定义索引的数据。然后使用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']];
        }
    );
}

答案 10 :(得分:1)

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

答案 11 :(得分:1)

现在是2019年,根据您想做什么,这里的许多答案都会起作用。如果您想传递URL中不可见的某些内部状态(参数,查询),则可以使用7.2以来的state(因为我今天有learned)。

在博客中(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的人有所帮助。

答案 12 :(得分:1)

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

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

答案 13 :(得分:0)

路线:

{ path: 'foo-route', component: FooComponent, data: { myData: false } },

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

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
  });
}
  • 记得导入需要的东西

答案 14 :(得分:0)

在需要将数据传递到另一个 Route 的情况下,最好和最简单的解决方案是使用 { window.localStorage }。此外,不要记住在使用结束后从本地存储中删除数据。 我使用 ngOnDestroy 的 destroy() 方法来清理这些数据。 这也解决了页面刷新导致数据丢失的问题。

答案 15 :(得分:0)

默认情况下,我不会为这个人使用警卫,我可以输入更多路线还是可以离开路线。他们之间不能共享数据。

如果要在进入路由之前加载数据,只需在此路由上添加一个解析器,这也是路由器的一部分。

作为一个非常基本的示例:

解析器

import { Resolve, ActivatedRoute } from "@angular/router";
import { Observable } from "rxjs";
import { Injectable } from "@angular/core";
import { take } from "rxjs/operators";

@Injectable()
export class UserResolver implements Resolve<User> {

    constructor(
        private userService: UserService,
        private route: ActivatedRoute
    ) {}

    resolve(): Observable<firebase.User> {
        return this.route.params.pipe(
            switchMap((params) => this.userService.fetchUser(params.user_id)),
            take(1)
        );
    }
}

放入路由器:

RouterModule.forChild([
{
    path: "user/:user_id",
    component: MyUserDetailPage,
    resolve: {
        user: UserResolver
    }
  }
}]

在我们的组件中获取数据

ngOnInit() {
    const user: firebase.User = this.activatedRoute.snapshot.data.user;
}

这种方法的缺点是,如果他不早之前获得用户数据,他将首先输入路径,这可确保为用户加载的数据已经准备好并在组件启动时就准备好了,但是您将停留在旧页面,只要已加载数据(正在加载动画)

答案 16 :(得分:0)

一个好的解决方案是使用canActivate方法实现Guard。在这种情况下,您可以从给定的api获取数据,并让用户访问路由文件中描述的组件。同时,可以设置路由对象的data属性并在组件中检索它。

假设您有此路由配置:

Paper

您的保护文件中可能有:

      import React, { useRef, useState } from "react";
import { withRouter } from "react-router-dom";
import gsap from 'gsap';

const LanguageDesc = (props) => {

    const [state, setState] = useState({
        isActive: false,
        languageName: 'English'
    });

    let english = useRef(null);
    let french = useRef(null);

    const handleLanguage = () => {
        if (state.isActive === false) {
            setState({
                isActive: true,
                languageName: 'French'
            })
            gsap.from(english.current, {
                duration: 5,
                opacity: 0,
            })
        } else if (state.isActive === true) {
            setState({
                isActive: false,
                languageName: 'English'
            })
            gsap.from(french.current, {
                duration: 1,
                opacity: 0,
            });
        } else if (state.isActive === false) {
            setState({
                isActive: !state.isActive,
                languageName: 'French'
            })
            gsap.from(english.current, {
                duration: 5,
                opacity: 0,
            })
        }
    };


    return (
        <div>
            {state.isActive ?
                <p ref={english} className="eng-text">{props.state.desc_en}</p>
                : null}
            {state.isActive ?
                null
                : <p ref={french} className="fr-text">{props.state.desc_fr}</p>}
            <button className="btn-language" onClick={handleLanguage} > {state.languageName} text</button>
        </div >
    )

};
export default withRouter(LanguageDesc);

}`

然后在您的组件中

const routes: Routes = [
    { path: "/:projectName", component: ProjectComponent, canActivate: [ProjectGuard] }
]`

这种方式与通过服务有点不同,因为只要不取消设置,服务会将值保留在behaviorSubject中。通过防护装置传递数据可用于当前路线。我没有检查子路由是否保留数据。

答案 17 :(得分: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"}
       });
      }