Angular routerLink 在 'nested' 个页面上导航到错误的路径

Angular routerLink navigating to wrong path on 'nested' pages

我正在 Angular 12 中构建一个基本的笔记应用程序,笔记存储在 Firebase 中。注释包含文本,但也可以作为更多注释的父节点,例如:

Note
Note
|- Note
  |- Note
  |- Note
    |- Note
|- Note
Note

您可以从 /notes/{{note.id}} 查看笔记的子笔记列表,并在 /edit/{{note.id}} 编辑笔记本身。如果 /notes/ 的 id 为 null,它会获取所有顶级注释。

问题: 一切正常,直到我尝试查看第二层的笔记列表。 routerLink 似乎将 /edit/{{note.id}}/notes/{{note.id}} 添加到导航堆栈,例如:

Note1
|- Note2 <-- click '>' button on note2 in list view
  |- Note3 <-- expect to see note3 in list view

我没有查看 notes/2,而是被带到了 edit/2。在浏览器中单击后退会将我带到我想要的 notes/2 的正确路线,让我可以在列表视图中看到 note3。为什么点击图标按钮会/edit/{{note.id}}添加到路由器?

编辑:StackBlitz link 在此处演示问题: https://stackblitz.com/edit/angular-hcmvab

相关代码片段如下:

app-routing.module.ts

const routes: Routes = [
  { path: '', redirectTo: 'notes', pathMatch: 'full' },
  { path: 'login', component: LoginComponent },
  { path: 'notes',
    children: [
      { path: '', component: NotesComponent },
      { path: ':id', component: NotesComponent },
    ],
  },
  { path: 'edit/:id', component: NoteDetailsComponent }
];

notes.component.ts

export class NotesComponent implements OnInit {
  parentID = this.route.snapshot.paramMap.get('id');
  notes: any;

  constructor(
    private route: ActivatedRoute,
    private notesService: NotesService
  ) {}

  ngOnInit(): void {
    this.retrieveNotes();
  }

  retrieveNotes(): void {
    this.notesService
      .getChildNotes(this.parentID)
      .snapshotChanges()
      .pipe(
        map((changes: any) =>
          changes.map((c: any) => ({
            id: c.payload.doc.id,
            ...c.payload.doc.data(),
          }))
        )
      )
      .subscribe((data) => {
        this.notes = data;
      });
  }
}

notes.component.html

<mat-list>
  <mat-list-item *ngFor="let note of notes" [routerLink]="['/edit', note.id]">
    <mat-icon fontSet="material-icons-outlined" mat-list-icon>description_outlined</mat-icon>
    <div mat-line>{{note?.title}}</div>
    <div class="spacer"></div>
    <button mat-icon-button *ngIf="note.children.length > 0" [routerLink]="['/notes', note.id]">
      <mat-icon>arrow_forward_ios</mat-icon>
    </button>
  </mat-list-item>
</mat-list>

问题是您将一个 [routerLink] 放在另一个中。 您应该将 [routerLink] 属性从 <mat-list-item> 移动到另一个地方,例如其中的一个按钮。你这样做的方式可能是先激活按钮点击,然后点击整个列表项。

试试改成这样:

     <mat-list-item *ngFor="let note of notes" >
        <mat-icon fontSet="material-icons-outlined" mat-list-icon>description_outlined</mat-icon>
        <div mat-line>{{note?.title}}</div>
        <div class="spacer"></div>
        <button mat-icon-button [routerLink]="['/notes', note.id]">
          <mat-icon>Some-Edit-Icon</mat-icon>
        </button>
        <button mat-icon-button *ngIf="note.children.length > 0" [routerLink]="['/notes', note.id]">
          <mat-icon>arrow_forward_ios</mat-icon>
        </button>
      </mat-list-item>
    </mat-list>

不如随心所欲地调整。 您可能遇到的另一个问题是您将 notes.id 传递给访问子注释的 [routerLink]。也许你应该放一个 *ngFor 并传递 children.id 而不是

如果此解决方案不起作用,请创建一个 stackBlitz 以便我们更清楚地看到问题。

您正在寻找的是参数数量可变的路线。类似于 /note/a/b/c/d/e。实现这一目标的一种方法是使用带有自定义 url 匹配器的路由,如果它以 /note (或您选择的前缀)开头,则接受该路由并收集所有其余部分并通过他们到组件。

路由的定义方式如下:

const variableRouteMatcher: UrlMatcher = (
  segments: UrlSegment[]
): UrlMatchResult => {
  if (segments.length >= 1 && segments[0].path === 'note') {
    const fullPath = segments
      .slice(1)
      .map(r => r.path)
      .join('/');
    return {
      consumed: segments,
      posParams: {
        fullPath: new UrlSegment(fullPath, {})
      }
    };
  }
  return null;
};

const routes: Routes = [
  { matcher: variableRouteMatcher, component: NoteComponent }
];

工作StackBlitz示例

编辑(基于评论) 在查看了你的 stackblitz 之后,我发现了两个问题,一个正如 Cantarzo 在他的回答中指出的那样,事实上有一个 link 元素在另一个 link 元素内。

但更严重的错误是您的 notes 组件不监听其路由参数的变化,它获取路由的快照,然后永远使用它。请记住,Angular 在路由中重复使用组件,因此无论何时导航到注释,都会使用相同的 NoteComponent 实例。

这意味着当您第一次导航到笔记时,它会获得它的 parent ID,然后无论您走到哪里,parent ID 都保持不变。

要解决此问题,您必须订阅 以更改已激活路线的参数并更新您的 parent id(以及您的 child 注释)因此

看看你的forked stacklitz