在我的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())

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

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


当前回答

路线:

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

其他回答

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

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

举个非常基本的例子:

解析器

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

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

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

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

使用JSON传递

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

我认为另一种方法对这个问题不好。 我认为最好的方法是路由器角度的查询参数,有两种方式:

直接传递查询参数

有了这段代码,你可以在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 { 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
}