ChatGPT解决这个技术问题 Extra ChatGPT

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

在我的 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
    }
}

通常我会通过

 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>

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

第三种方法

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

在 FirstComponent 中的 routeWithData() 上存储数据 (_firstComponentService.storeData())

在 SecondComponent 的 ngOnInit() 中检索数据 (_firstComponentService.retrieveData())

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

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

谢谢@Prashobh。 Pass data using Query Parameters 是我一直在寻找的。您的 link 拯救了我的一天。
Angular 7.2 现在具有使用 state 在路由之间传递数据的新功能,请检查 PR 了解更多详细信息。一些有用的信息here
@Prashobh 非常感谢。您分享的链接非常有用

G
Günter Zöchbauer

更新 4.0.0

有关详细信息,请参阅 Angular Angular Router - Fetch data before navigating

原来的

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

请参阅角度 Angular Cookbook Component Communication - 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();
  }
}

另请参阅 Plunker


这个答案对 Angular 2.1.0 仍然有效吗?
RC.4 路由器数据仅用于静态数据。您不能将不同的数据发送到同一条路线它总是必须是相同的数据我错了吗?
不,请为此用例使用共享服务。
无论如何,在 Angular 5 中,您应该能够... ngOnInit() { this.myVar = this.route.snapshot.data['some_data']; }
如果您能够使用 Angular v7.2,它现在允许使用 NavigationExtras - stackoverflow.com/a/54879389/1148107 在路由器中传递状态
U
Utpal Kumar Das

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

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

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

从第一页组件传递对象:

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

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

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

这是一个示例:plunker


这很漂亮,但是这在 Ng2 社区中有多普遍?我不记得在文档中阅读过它...
与 url 参数或其他浏览器存储等其他选项相比,这在我看来更好。我也没有在任何文档中看到像这样工作。
当用户打开一个新选项卡并复制粘贴第二个组件路由时它是否有效?我可以获取 this.hero = this.dataService.hero 吗?我会得到这些值吗?
这确实非常简单,每个 Angular 开发人员都知道,但问题是一旦您刷新服务中的松散数据。用户将不得不再次做所有的事情。
@SantoshKadam 问题是“如何将数据传递给 Angular 路由组件?”所以通过 ngOnDestroy 和 ngOnInit 函数传递数据是一种方法,而且总是简单是最好的。如果用户需要在重新加载后获取数据,则需要将数据保存在永久存储中并从该存储中再次读取。
W
Washington Braga

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

对我不起作用,window.history.state 返回类似 {navigationId: 2} 的内容,而不是返回我传入的对象。
@Louis 您使用的是哪个 Angular 版本?
我正在使用角度版本 8.1.0
我看到的和 Louis 一样,版本比他的低,但仍然足够高,应该有这个功能。
状态对象的浏览器数据大小限制为 640k。 stackoverflow.com/questions/24425885/…
R
Rohit Sharma
<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/


如果我想发送一个复杂的对象怎么办?我不想让我的路线膨胀到无法维护的废话:(
@cmxl 然后使用共享服务。
@cmxl 仅发送 id 或简单字符串作为数据的想法是使 URL 更“可共享”并且易于被机器人等抓取。因此,您的应用程序的用户可以共享生成的链接。对于发送更大的对象,服务将更有效。
S
Shashank Agrawal

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

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

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

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

https://i.stack.imgur.com/vIVig.png

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

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

https://i.stack.imgur.com/bSx7S.png

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

https://i.stack.imgur.com/JGWc6.png


extras ?那是你刚刚定义的东西还是角度属性?
正是我正在寻找的谢谢队友..这是给你的赞成票;)我也在 ionic5 proj 中使用它
精彩的答案!请务必记住,访问 state(在路由到新页面之后)对我来说仅在 constructor 上有效,在 ngOnInit 内无效。那是因为 getCurrentNavigation() 为空。
@Itay 我同意。我正在使用 Angular 11。当前导航范围在 ngOnInit() 之前结束。所以我不得不从构造函数中获取状态值。
如果页面刷新,数据会丢失
P
PeS

现在是 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 的人有所帮助。


用户刷新时state不会丢失吗?能够原生地坚持它会很有趣。
这实际上是对我有用的唯一方法。谢谢👍
A
AmirReza-Farahlagha

我这是另一种方法不适合这个问题。我认为最好的方法是 Query-Parameter 通过 Router 角度有两种方式:

直接传递查询参数

使用此代码,您可以通过 html 代码中的 params 导航到 url

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

通过路由器传递查询参数

您必须在 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;
      });
  }

this.router.navigate(['/product-list'], { queryParams: { serviceId: serviceId} });可以替换为 this.router.navigate(['/product-list'], { queryParams: { serviceId} });
t
terary

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

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

如评论中的 here 所示。

我希望有人会觉得这很有用


刚刚发现了这一点,我觉得我需要一些stackoverflow点:)
s
scorpion

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

}

这行得通,但是如果我不想公开 URL 中的所有数据怎么办?
您可以加密数据并将其放入参数中,然后在目标组件中加密。
我有 created the service 用于数据共享。
super.ngOnInit(); 是做什么用的?
谢谢你。发送端的 JSON.stringify() 和接收端的 JSON.parse() 对我有用。
O
O-9

路线:

{ 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() 可能会更好


a
ahankendi

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

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
}

该服务在切换路由时被实例化。所以你丢失了数据
@JimmyKane您特别谈到页面刷新时,但如果它不刷新,则内存仍保存在服务中。这应该是默认行为,因为它将多次保存加载。
@AaronRabinowitz 对。对困惑感到抱歉。并对投反对票感到抱歉。希望我现在可以撤消它。为时已晚。对 angular 2 来说是新手,我在尝试您的方法时遇到的问题是我为许多组件提供了服务,而不是通过 app 模块提供。
佚名

使用 JSON 传递

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

如果您可以显示参数在接收组件中的使用方式,这将是一个更好的答案 - 它采用的整个路线。这意味着如果有人不知道如何传递参数,他也不会知道如何在接收组件中使用此参数。 :)
这样做的一个缺点是查询字符串有大小限制,有时您不希望在地址栏中显示对象属性。
A
Amin Adel

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

N
Nelson Bwogora

说你有

组件1.ts 组件1.html

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

在 component1.ts 中是一个带有数据的变量 //component1.ts item={name:"Nelson", bankAccount:"1百万美元"} //component1.html //line routerLink="/meter-readings/{ {item.meterReadingId}}" 与 // 无关,将其替换为您要导航到 查看 //component2.ts import { ActivatedRoute} from "@angular/router";导入'rxjs/add/operator/filter'; /*类名等和类样板 */ data:any //将保存我们传递给构造函数的最终对象(私有路由:ActivatedRoute,){} ngOnInit() { this.route.queryParams .filter(params => params .reading) .subscribe(params => { console.log(params); // 数据将是一个 JSON 字符串 - 我们解析返回我们的 //OBJECT this.data = JSON.parse(params.item) ; console. log(this.data,'PASSED DATA'); //给出 {name:"Nelson", bankAccount:"1 //百万美元"} }); }


s
sushil suthar

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

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


R
Ralf Hannuschka

默认情况下,我不会为此使用警卫,更重要的是我可以进入路线还是离开路线。这不是在他们之间共享数据。

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

作为非常基本的示例:

解析器

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

这种方法的缺点是,如果他之前没有获得用户数据,他将首先进入路线,这确保用户的数据已经加载并在组件启动时准备好,但您将留在旧页面上数据加载时间长(加载动画)


J
Jeff G.

一个很好的解决方案是使用 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 中。通过 tha 守卫使数据可用于当前路线。我没有检查子路由是否保留数据。


S
Sandip Wagh

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


点评来源: 你能提供一些示例代码吗?
J
JuergenG

如果您有一个公式集合来处理几个 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; 

组件A.ts

现在在首页

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

app.component.html

和方法 OnClickNav(Selected: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;

应用程序.commonet.ts

NavFlags 类很简单

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

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

导航标志.ts

这样,“个人”页面不会留下任何数据丢失。我没有重复的商店。完整的示例可以访问 https://angulartool.de。通过单击该按钮,可以在组件中浏览页面而不会丢失数据。

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