在Angular2中,有更好的方法获取祖先路由参数吗?

12

我需要从路由中获取参数,但它既不是父级也不是祖先级,而是在路径往根部的某个位置上。我知道像Angular 2: getting RouteParams from parent component或共享服务等方法,但我认为自己的情况并不适合这些方法。所以我得到了以下信息:

我懒加载了子模块,它需要从父级上下文中获取id。在路由配置中,情况大致如下:

// Parent/outer module routing
const outerRoutes: Routes = [
    {
        path: 'container/:containerId',
        component: ContainerComponent,
        children: [
            { path: 'model', loadChildren: 'app/container/model/model.module#ModelModule' },
            { path: '', component: ContainerDetailsComponent }
        ]
    }
];

// Child/inner module routing
const innerRoutes: Routes = [
    {
        path: '',
        component: ModelComponent,
        children: [
            { path: ':id', component: ModelDetailsComponent },
            { path: '', component: ModelsComponent }
        ]
    }
];

ModelsComponent 需要加载属于给定 Container 的所有 Model 实例。因此,我决定采用 .parent 方法,但写完第二个 .parent 后我的牙齿开始疼了,还有第三个要写:

this.route.parent.parent.parent.params
  .switchMap((params: Params) => this.modelService.getAllBelongingTo(params['containerId']))
  .subscribe(
    (models: Model[]) => this.models = models,
    error => this.raceEvent = null
  );

共享服务可能是一种选择,但由于子模块是按需加载的,我必须将其放在核心模块中,使其变得相当全局化(这不是我喜欢的)。

经过一番努力,我得到了以下代码,它简单地获取所有的ActivatedRoute直到根节点,组合它们,查找参数并使用它。我不喜欢它,因为我认为它太复杂,难以阅读,而且很糟糕(或者换句话说,非常恶心),但它有效,而且我不必担心嵌套结构的变化。我只需要确保:containerId在通向根节点的路径上的某个地方。

import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Params } from '@angular/router';
import { Observable } from 'rxjs/Observable';

import { Model } from './model';
import { ModelService } from './model.service';

@Component({
    ...
})
export class ModelsComponent implements OnInit {
    models: Model[];

    constructor(
        private route: ActivatedRoute,
        private modelService: ModelService) { }

    ngOnInit() {
        const paramsArr = this.route.pathFromRoot.map(route => route.params);

        Observable.combineLatest(paramsArr)
            .switchMap(arrOfParams => {
                const params = arrOfParams.find(p => p['containerId'] !== undefined),
                      id = params && params['containerId'];
                return id ? this.modelService.getAllBelongingTo(id) : [];
            })
            .subscribe(
                (models: Model[]) => {
                    // yay! we got sth
                    this.models = models;
                },
                error => {
                    // handle big nono
                }
            );
    }
}

有没有更好的方式来处理所描绘的情况?


1
状态管理技术是一个解决方案,如果它是一个庞大的应用程序。 - Aravind
暂时还没有,问题在于我不知道它会变得有多大,也不想过度投资...但另一方面,也许总的来说已经值得投资了,以避免以后可能出现的麻烦...你有什么具体的产品/方法吗?像Savkin's Tackling State这样的东西,或者一直到ngrx/store或Redux? - gaa
为什么使用共享服务是一个不好的做法?只需要几行代码,就可以大大简化您的实现。 - Fabio Antunes
1
@FabioAntunes 我并不是说这样做不好,我只是认为在这种情况下,当你有一个懒加载的子模块时,它必须要去CoreModule中才能跨越注入器边界可用,并且会使它变得非常全局化。当你有更多这样的模块配置(懒加载父级-懒加载子级)在各个深度上时,它会使CoreModule(或者另外的...根注入器)被这些“交换服务”污染得相当厉害。哦,忘了提到父级也是懒加载的,所以它的提供者不属于根注入器,对子级也不可见:P - gaa
你不需要“污染”核心。你可以在组件级别提供共享服务,而不是在模块级别提供,以避免服务实例混淆。你可以在具有父组件的模块中定义它,并在父组件中提供它。然后,你可以自由地将其注入到来自任何其他模块的子组件中,并且它将接收到“最近”的提供副本,在这种情况下,是在父组件级别提供的副本。 - bryan60
只是想补充一下,你在这里提出了互相排斥的想法。如果你的应用程序“不够大”以至于不需要状态管理解决方案,那么它肯定不足以需要懒加载模块。 - bryan60
2个回答

7
一般来说,我认为你的方法是有意义的。我不知道这段代码的具体背景,但感觉像一个子组件想要了解父实例数据的情况,我同意@Aravind的评论,最好使用全局状态管理解决方案,如redux或ngrx。话虽如此,如果你仅需要它,我理解你不想引入那种复杂度级别。
我只会对你的rxchain进行小调整,以使其更易读。
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Params } from '@angular/router';
import { Observable } from 'rxjs/Observable';

import { Model } from './model';
import { ModelService } from './model.service';

@Component({
    ...
})
export class ModelsComponent implements OnInit {
    models: Model[];

    constructor(
        private route: ActivatedRoute,
        private modelService: ModelService) { }

    ngOnInit() {
        const paramsArr = this.route.pathFromRoot;

        Observable.from(paramsArr)
            .map(route => route.params)
            .filter(params => params.containerId)
            .switchMap(containerId => this.modelService.getAllBelongingTo(containerId))
            .subscribe(
                (models: Model[]) => {
                    // yay! we got sth
                    this.models = models;
                },
                error => {
                    // handle big nono
                }
            );
    }
}

请注意,仅当存在父容器的参数containerId时,此实现才会设置this.models。
编辑1:修正了拼写错误。

子模块仅用于管理/导入等(CRUD)容器的一个方面,该方面是绑定到模型列表的。这个模块不会经常使用,但在交互的某个阶段是必不可少的(这就是为什么要懒加载)。在这种情况下,我只需要containerId(而不是整个容器)来获取所有绑定到它的模型。总之,你和@Aravind在状态管理方面是正确的,随着应用程序的增长,它迟早会到来,我只是认为现在还没有到时候。 - gaa
明白了。在这种情况下,除了对rxchain使用进行轻微的清理(顺便说一句,我刚刚在编辑中清理了我的清理),我不确定还有什么更好的建议。那么,我不确定你的设计有什么问题。我认为它是合适的。 - Ben Dadsetan
说实话,如果到赏金结束没有人提出突破性的东西,那就是你的了。问题在于它不能作为一般方法使用,因为当你有多个id时,它很快就会失控,比如:模型id仅在容器内唯一,因此要进一步操作模型细节,例如,你需要对 containerIdmodelId这样的内容进行操作,这些东西只是在rx链中简单地堆积起来。总而言之,正如你和@Aravind所说,使用状态管理比我想象的更接近。 - gaa
这不就是我们技术的美嘛? :-) 只有当需求变得更加混乱时,代码才会变得杂乱无章。可能应用状态存储变得更加重要,或者您想创建可以使用 .let() 轻松重复使用的链式组件... 无论哪种方式,当需求改变时,rxjs 链确实需要进行大量调整。我仍然觉得它们可以节省很多代码和时间来解决问题。我坚持我的回答。 :-) - Ben Dadsetan

1
我遇到了同样的问题,在找到这个答案后,我创建了一个名为getParams()的函数,它获取所有父级和当前根的参数。 它将所有参数组合起来,并将其减少为单个映射。
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { ServicosService } from '@service/core/servicos.service';
import { ActivatedRoute } from '@angular/router';
import { Observable } from 'rxjs/Observable';
import { map, combineAll } from 'rxjs/operators';

@Injectable()
export abstract class HttpServiceRouted {
  constructor(
    protected http: HttpClient,
    protected servicos: ServicosService,
    protected route: ActivatedRoute
  ) {}

  getParams() {
    return Observable.from(this.route.pathFromRoot.concat(this.route)).pipe(
      map(route => route.params),
      combineAll(),
      map((params: any[]) =>
        params.reduce(
          // tslint:disable-next-line:no-shadowed-variable
          (map: Map<string, string>, obj) => {
            Object.keys(obj).forEach(key => {
              map.set(key, obj[key]);
            });
            return map;
          },
          new Map<string, string>()
        )
      )
    );
  }
}

使用方法:

this.getParams().subscribe((params: ParamMap) => {
  const id = params.get('id');
});

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接