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

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

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

2方法

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

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

然而,我想路线到该组件,不包括它!

第三个方法

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

  • 商店 FirstComponent中的routeWithData()上的数据(_firstComponentService.storeData())
  • 检索 SecondComponent中的ngOnInit ()中的数据(_firstComponentService.retrieveData())

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

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

744828 次浏览

更新4.0.0

更多细节请参见Angular Angular路由器——在导航前获取数据

原始

使用服务是正确的选择。在路由参数中,您应该只传递希望在浏览器URL栏中反映的数据。

参见Angular 组件通信——双向服务

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

另见砰砰作响

我想既然角2中没有像角1.x中那样的rootScope美元。我们可以使用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;
}

下面是一个例子:砰砰作响

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

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
}

使用JSON传递

  <a routerLink = "/link"
[queryParams] = "{parameterName: objectToPass| json }">
sample Link
</a>
<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/ < / p >

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


}

使用共享服务使用自定义索引存储数据。然后用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']];
}
);
}
我认为另一种方法对这个问题不好。 我认为最好的方法是查询参数Router角,有两种方式:

直接传递查询参数

使用这段代码,你可以在html代码中通过params导航到url:

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

通过Router传递查询参数

你必须像这样在constructor中注入路由器:

constructor(private router:Router){


}

现在用这个短语:

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

现在,如果你想从另一个Component中读取Router,你必须像这样使用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;
});
}

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

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

正如在评论中看到的在这里

我希望有人会觉得这有用

说你有

  1. component1.ts
  2. component1.html

并且你想将数据传递给component2.ts

  • 在component1 < p >。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"}
    });
    }
    

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

现在是2019年,这里的许多答案都有用,这取决于你想做什么。如果你想传递一些URL中不可见的内部状态(params, query),你可以从7.2开始使用state(就像我今天使用学会了:))。

从博客(Tomasz Kula) -您导航到....路线

…from 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的人。

您可以使用BehaviorSubject在路由组件之间共享数据。 一个BehaviorSubject包含一个值。当它被订阅时,它立即发出该值。Subject没有值

在服役中。

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

注意:主语在这里无效,只需要使用行为主语。

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

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

所以在我的角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

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

假设你有这样的路由配置:

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

在你的警卫文件中,你可能有:

canActivate(next: ActivatedRouteSnapshot,state: RouterStateSnapshot)
: Observable<boolean> | Promise<boolean> | boolean {
return this.myProjectService.getProject(projectNameFoundElsewhere).pipe(
map((project) => {
if (project) {
next.data = project;
}
return !!project;
}),
);

} '

然后在你的组件中

constructor(private route: ActivatedRoute) {
this.route.data.subscribe((value) => (this.project = value));
}

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

默认情况下,我不会使用一个守卫这一个对我来说,这是更多的我可以进入路线或我可以离开它。这不是为了在它们之间共享数据。

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

举个非常基本的例子:

解析器

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

这种方法的缺点是,如果他之前没有得到用户数据,他将首先进入路由,这确保用户的数据已经加载并在组件开始时准备好了,但只要数据已经加载,你就会停留在旧页面上(加载动画)

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

路线:

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

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

如果你有一个公式集合,它处理了几个ng组件,这些ng组件基本上是建立在类对象的集合/数组上的。10个道具,例如,包括输入值,标称值,至少单位和布尔值…,所以保持页面状态(输入+结果)结束重复很多东西。

因此,我通过使用*ngif来模拟路由,以显示单个页面的相关部分(组件),但从不更改url。

<div *ngIf="visibleComponentA>
... All part of ComponetA
></div>

CpmponetA.html

<div *ngIf="visibleComponentB>
... All part of ComponetB
></div>

CpmponetB.html

这个布尔值将在组件的相关代码中设置:

@Input()visibleComponentA: boolean = true;

ComponetA.ts

现在在第一页

<div (click)="OnClickNav(visibleComponentA)" >ComponentA</div>
<div (click)="OnClickNav(visibleComponentB)" >ComponentB</div>

app.component.html

方法OnClickNav(选择:NavFlags)切换组件的正确可见状态。

OnClickNav(Selected:NavFlags){


Selected.NavStatus=!Selected.NavStatus


Selected.NavItem=='visibleComponetA'? this.visibleComponetA.NavStatus=Selected.NavStatus: this.visibleComponetA.NavStatus= false;
Selected.NavItem=='visibleComponetB'? this.visibleComponetB.NavStatus=Selected.NavStatus: this.visibleComponetB.NavStatus= false;

app.commonet.ts

NavFlags类很简单

export class NavFlags {
NavItem: string = '';
NavStatus: boolean = false;


constructor(NavItem: string, NavStatus: boolean) {
this.NavItem = NavItem;
this.NavStatus = NavStatus;
}
}

nav-flags.ts

由此,“个人”;页面不会留下,数据不会丢失。我没有复制商店。 完整的例子可以访问https://angulartool.de。 通过单击该按钮,可以在组件中导航页面,而不会丢失数据

这个hack不是完美的,所以也许会有更好的方法来解决这个角度问题。