【问题标题】:Passing parameters in named outlets is not working在命名插座中传递参数不起作用
【发布时间】:2021-09-07 14:01:21
【问题描述】:

伙计们,我在尝试使这段代码正常工作时遇到了一些麻烦。

基本上,我想将一些参数传递给嵌套并命名为router-outlet

来自回购:https://stackblitz.com/edit/angular-hsezw8-rp5ne9?file=src/app/crisis-center/crisis-list/crisis-list.component.html

我正在使用以下routerLink

    <a [routerLink]="[{ outlets: { test: [ crisis.id] } }]">
      <span class="badge">{{ crisis.id }}</span>{{ crisis.name }}
    </a>
...
<router-outlet name="test"></router-outlet>

我的路线如下:

{
        path: '',
        component: CrisisListComponent,
        children: [
          {
            path: ':id',
            component: CrisisDetailComponent,
            canDeactivate: [CanDeactivateGuard],
            resolve: {
              crisis: CrisisDetailResolverService
            },
            outlet: 'test'
          },
          {
            path: '',
            component: CrisisCenterHomeComponent
          }
        ]
      }

在 stackoverflow 中搜索后,根据答案,这应该可以工作,但事实并非如此。有人知道我所缺少的吗? 提前致谢

【问题讨论】:

  • 你的问题启发了我写一篇文章,here 是。谢谢!
  • @AndreiGătej 我肯定会看看并与我的同事分享。谢谢

标签: angular router angular-routing angular-router router-outlet


【解决方案1】:

编辑

我写了一篇文章,详细介绍了 Angular 11 附带的新修复:Angular Router: empty paths, named outlets and a fix that came with Angular 11


TLDR;

Working demo.


详细解释

为什么它一开始就不起作用

一个 TLRD 是具有(嵌套)空路径的命名出口并不总是运行良好(至少在旧版本中)。这是其中一种情况。

首先,重要的是要提到 Angular 路由器如何处理路由及其更改。例如,它借助名为 UrlTree1 的数据结构跟踪当前 URL。 UrlTree 可以被认为是 URL 的反序列化版本,它是一个字符串。给定一个UrlTree,将基于这个UrlTree 遍历路由配置数组(以DFS 方式)。 UrlTree 的根是另一个结构,UrlSegmentGroup

constructor(
  /** The URL segments of this group. See `UrlSegment` for more information */
  public segments: UrlSegment[],
  /** The list of children of this group */
  public children: {[key: string]: UrlSegmentGroup}) {
forEach(children, (v: any, k: any) => v.parent = this);

如您所见,它类似于常规的树结构。 segments 数组中的 a 段包含给定路径的 path(例如 'crisis-center')和矩阵参数。至于children 属性,它的键是出口的名称(例如'test'),值是UrlSegmentGroup 实例。通过使用这些结构,Angular Router 可以表示 URL 字符串,并可以提供许多功能和自定义。

给定来自crisis-center-routing.module.ts的路由配置:

const crisisCenterRoutes: Routes = [
  {
    path: '', // (1)
    component: CrisisCenterComponent,
    children: [
      {
        path: '', // (2)
        component: CrisisListComponent,
        children: [
          {
            path: ':id', // (3)
            component: CrisisDetailComponent,
            canDeactivate: [CanDeactivateGuard],
            resolve: {
              crisis: CrisisDetailResolverService
            },
            outlet: 'test'
          },
          {
            path: '',
            component: CrisisCenterHomeComponent
          }
        ]
      }
    ]
  }
];

将路由配置对象与path: ':id' 匹配的UrlTree 大致如下所示:

// Note: For simplicity, I've only used the `path` to represent the `segments` property
UrlTree {
  // UrlSegmentGroup
  root: {
    children: {
      primary: {
        children: {
          primary: {
            children: {
              test: {
                children: {}
                segments: [1], // Matches (3)
              }
            },
            segments: [''], // Matches (1) and (2)
          }
        }
        segments: ['crisis-center'],
      }
    }
    segments: [],
  }
}

注意:插座的默认名称是'primary'

上面的UrlTree可以这样实现:

<a [routerLink]="['/crisis-center', { outlets: { primary: ['', { outlets: { test: [crisis.id] } }] } }]">
  <span class="badge">{{ crisis.id }}</span>{{ crisis.name }}
</a>

虽然理论上上述方法应该有效,但它没有。这是因为'' 未被消费,当这种情况发生时,primary 出口将被选中。更具体地说,来自 primary: ['' ...'' 将与(1) 匹配,但不会消耗第二个''(来自(2))。 但是,这似乎是to be fixed in Angular v12

// At this point, not all paths of the `segment` array have been consumed.
// This time, the route's outlet is taken into account.
const matchedOnOutlet = getOutlet(route) === outlet;
const expanded$ = this.expandSegment(
    childModule, segmentGroup, childConfig, slicedSegments,
    matchedOnOutlet ? PRIMARY_OUTLET : outlet, true);

但是this 是以前的样子:

// At this point, not all paths of the `segment` array have been consumed.
// As you can see, it always chooses the `primary` outlet
const expanded$ = this.expandSegment(
    childModule, segmentGroup, childConfig, slicedSegments, PRIMARY_OUTLET, true);

由于您是旧版本,我们需要找到另一种方法。

当前的方法也会发生类似的事情:

<a [routerLink]="[{ outlets: { test: [ crisis.id] } }]">
  <span class="badge">{{ crisis.id }}</span>{{ crisis.name }}
</a>

但这一次导航不会成功,因为出口之间会出现不匹配。它将在(2)失败,其中路由的出口是primary,但根据当前UrlSegmentGroup的出口是test

解决方案

首先,我注意到CrisisCenterComponent 没有多大作用:

<h2>CRISIS CENTER</h2>
<router-outlet></router-outlet>

所以,我们将摆脱它。这就是我们将剩下的:

crisis-center-routing.module.ts

const crisisCenterRoutes: Routes = [
  {
    // path: '',
    // component: CrisisCenterComponent,
    // children: [
    //   {
        path: '',
        component: CrisisListComponent,
        children: [
          {
            path: ':id',
            component: CrisisDetailComponent,
            canDeactivate: [CanDeactivateGuard],
            resolve: {
              crisis: CrisisDetailResolverService
            },
            outlet: 'test'
          },
          {
            path: '',
            component: CrisisCenterHomeComponent
          }
        ]
      }
  //   ]
  // }
]

现在,由于CrisisListComponent 充当容器,我们可以进行以下替换:

crisis-center-routing.module.ts

const crisisCenterRoutes: Routes = [
  {
    // path: '',
    // component: CrisisCenterComponent,
    // children: [
    //   {
        // path: '',
        // component: CrisisListComponent,
        // children: [
        //   {
            path: ':id',
            component: CrisisDetailComponent,
            canDeactivate: [CanDeactivateGuard],
            resolve: {
              crisis: CrisisDetailResolverService
            },
            outlet: 'test'
          },
          {
            path: '',
            component: CrisisCenterHomeComponent
          }
      //   ]
      // }
  //   ]
  // }
]

app-routing.module.ts

  {
    path: 'crisis-center',
    loadChildren: () =>
      import('./crisis-center/crisis-center.module').then(
        mod => mod.CrisisCenterModule
      ),
    data: { preload: true },
    // !
    component: CrisisListComponent
  },

因此,不会有额外的''(这有时会导致问题,正如我们所见)匹配。

并且视图中的routerLink 将保持不变:

<a [routerLink]="[{ outlets: { test: [ crisis.id] } }]">
  <span class="badge">{{ crisis.id }}</span>{{ crisis.name }}
</a>

附带说明,现在当您选择一个危机中心时,CrisisCenterHomeComponent 也将激活。如果你想避免这种情况,你可以这样做:

crisis-center-routing.module.ts

/* ... */
  {
    path: ':id'
    component: CrisisDetailComponent,
    canDeactivate: [CanDeactivateGuard],
    resolve: {
      crisis: CrisisDetailResolverService
    },
    outlet: 'test'
  },
  {
    path: '',
    // Uncomment this if you want these 2 Route objects
    // to be mutually exclusive(either one of them, not both at the same time)
    pathMatch: 'full',
    component: CrisisCenterHomeComponent
  }
/* ... */

1:Angular Router: Getting to know UrlTree, ActivatedRouteSnapshot and ActivatedRoute

如果您想了解更多关于 Angular 路由器及其内部工作/功能的信息,我建议您:

【讨论】:

  • 谢谢,安德烈,你让它看起来很简单。
  • 很高兴能帮上忙!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-02-21
  • 1970-01-01
  • 2016-09-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多