在我的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
}
}
通常我会通过
this._router.navigate(['SecondComponent']);
最终传递数据
this._router.navigate(['SecondComponent', {p1: this.property1, p2: property2 }]);
而带有参数的链接的定义将是
@RouteConfig([
// ...
{ path: '/SecondComponent/:p1:p2', name: 'SecondComponent', component: SecondComponent}
)]
这种方法的问题是,我猜我不能在url中传递复杂的数据(例如property3这样的对象);
2方法
另一种方法是在FirstComponent中包含SecondComponent作为指令。
<SecondComponent [p3]="property3"></SecondComponent>
然而,我想路由到该组件,不包括它!
第三个方法
我在这里看到的最可行的解决方案是使用一个服务(例如FirstComponentService)来
将数据(_firstComponentService.storeData())存储在FirstComponent的routeWithData()上
在SecondComponent的ngOnInit()中检索数据(_firstComponentService.retrieveData())
虽然这种方法似乎完全可行,但我想知道这是否是实现目标的最简单/最优雅的方法。
一般来说,我想知道我是否错过了在组件之间传递数据的其他潜在方法,特别是在代码量可能较少的情况下
更新4.0.0
更多细节请参见Angular Angular Router -在导航前获取数据。
原始
使用服务是正确的选择。在路由参数中,您应该只传递希望在浏览器URL栏中反映的数据。
参见Angular Cookbook组件通信-双向服务。
RC.4附带的路由器重新引入了数据
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();
}
}
参见Plunker。
第三种方法是在组件之间共享数据的最常用方法。您可以在相关组件中注入您想要使用的项目服务。
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
}
<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 like
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/
我认为另一种方法对这个问题不好。
我认为最好的方法是路由器角度的查询参数,有两种方式:
直接传递查询参数
有了这段代码,你可以在html代码中通过参数导航到url:
<a [routerLink]="['customer-service']" [queryParams]="{ serviceId: 99 }"></a>
通过路由器传递查询参数
你必须像这样在构造函数中注入路由器:
constructor(private router:Router){
}
现在用这个短语:
goToPage(pageNum) {
this.router.navigate(['/product-list'], { queryParams: { serviceId: serviceId} });
}
现在,如果你想从另一个组件的路由器读取,你必须使用ActivatedRoute:
constructor(private activateRouter:ActivatedRouter){
}
并订阅:
ngOnInit() {
this.sub = this.route
.queryParams
.subscribe(params => {
// Defaults to 0 if no query param provided.
this.page = +params['serviceId'] || 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;
}
这种方法的缺点是,如果他之前没有得到用户数据,他将首先进入路由,这确保用户的数据已经加载并在组件开始时准备好了,但只要数据已经加载,你就会停留在旧页面上(加载动画)
如果你有一个公式集合,它处理了几个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不是完美的,所以也许会有更好的方法来解决这个角度问题。