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
我正在 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