我正在构建一个角度为6的app,应该有两个特定的路由器插座:
<router-outlet name="navbar"></router-outlet>
<router-outlet></router-outlet>
应用程序由URL中指定的令牌驱动:
http://my.app/*token*/dashboard
所以我有这个特定的路由:
const routes: Routes = [
{
path: ':token', component: CoreComponent, children: [
{ path: 'dashboard', component: DashboardComponent },
{ path: 'app1', component: App1Component },
{ path: 'app2', component: App2Component },
{ path: '', component: NavbarComponent, outlet: 'navbar' }
]
}
];
@NgModule({
imports: [
CommonModule,
RouterModule.forRoot(routes)
],
exports: [RouterModule],
declarations: []
})
在每个组件中,我从URL检索token参数并对其执行一些检查。
constructor(public route: ActivatedRoute,
public router: Router) {
}
ngOnInit() {
this.route.params.subscribe(params => {
let _token: string = params['token'];
// do something
});
}
它在NavbarComponent中完美运行,但我无法在其他组件中获取参数。就像参数在导航栏中处理后丢失一样。
我还没有找到关于这种现象的任何可靠文件。
这是stackblitz的一个例子
https://stackblitz.com/edit/angular-skvpsp?file=src%2Fapp%2Fdashboard.component.ts
并输入此测试网址:https://angular-skvpsp.stackblitz.io/2345abcf/inbox有关详细信息,请参阅控制台。
任何的想法?谢谢。
[编辑]
试过很多解决方案,但仍未找到正确答案。
我看到有一个数据属性可以在路由模块中使用。我会检查一下。
所以,经过一些调试我发现了问题。 很抱歉,在解决问题后,我只是为您的代码添加了一些tipp。
我为你准备了一个优化的Stackblitz,检查一下,看看所有的变化!
https://stackblitz.com/edit/angular-l1hhj1
看看你的路线:
const routes: Routes = [
{
path: ':token', component: AppComponent, children: [
{ path: 'dashboard', component: DashboardComponent },
{ path: 'inbox', component: InboxComponent },
{ path: '', component: NavbarComponent, outlet: 'navbar' }
]
}
];
:token
param只给AppComponent。
以下可能解决方案<router-outlet>
标记。由于AppComponent已经是根组件,因此您将AppComponent加载到自身的RouterOutlet中。:token
路径下面作为子路径,它也会得到:token
参数,因为你读它的逻辑位于BasePage.ts`里面,它也可以读取它。
所以你的导航栏只能偶然工作。您最简单的解决方案是进入BasePage.ts
并更改以下代码
this.queryParams = this.route.snapshot.queryParams
this.routeParams = this.route.snapshot.params;
至
this.queryParams = this.route.parent.snapshot.queryParams
this.routeParams = this.route.parent.snapshot.params;
以下是ActivatedRoute类的文档: https://angular.io/guide/router#activated-route
不要发布Stackblitz和在stackblitz内部调用的链接,而是将stackblitz中的链接添加为默认路由。只需将此路线添加到您的路线阵列,您的Stackblitz将自动加载您想要的路线:
{ path: '', pathMatch: 'full', redirectTo: '2345abcf/inbox' }
请改用ActivatedRoute.paramMap和ActivatedRoute.queryParamMap。 请参阅官方角度文档:https://angular.io/guide/router#activated-route
您应该每次使用paramMaps作为Observable而不是snapshot.paramMaps。
快照的问题如下:
请参阅:https://angular.io/guide/router#router-links这可以避免手动构建路径时可能出现的错误。通常,具有两个路由器插座的路由如下所示:
https://some-url.com/home(conference:status/online)
/home
是主要路线,(conference:status/online)
说<router-outlet name=‚conference‘>
应该加载路线/状态/在线。
由于您省略了第二个插座的最后一个部分,因此不能立即清楚,角度将如何处理路线。
这使事情变得比他们需要的更复杂。您无需单独浏览导航内容,也不需要?如果您不需要单独导航,则可以更轻松地将菜单直接包含在AppComponent模板中。
当你有一个侧边栏用于聊天,你可以选择联系人,聊天窗口和聊天设置页面,而所有都独立于你的主要方面时,第二个路由器插座是有意义的。那你需要两个路由器插座。
您通过以下方式将您的BasePage作为服务提供给Angular
@Injectable({
providedIn: ‚root‘
})
使用可注入服务作为基类似乎是一个非常奇怪的设计。我想不出一个原因,我想用它。要么我使用BaseClass作为BaseClass,它独立于角度工作。或者我使用角度服务并将其注入到需要服务功能的组件中。
注意:您不能简单地创建一个服务,它会注入Router
和ActivatedRoute
,因为它会获取这些组件的根对象,这些组件是空的。 Angular为每个组件生成它自己的ActivatedRoute
实例,并将其注入组件。
解决方案:请参阅下面的“更好的解决方案:TokenComponent with Token Service”。
我现在能想到的最好的解决方案是与TokenService一起构建令牌组件。您可以在Stackblitz查看此创意的工作副本:
https://stackblitz.com/edit/angular-l1hhj1
我的TokenService看起来像这样:
import { OnInit, Injectable } from '@angular/core';
import { Observable, ReplaySubject } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class TokenService {
/**
* Making the token an observable avoids accessing an 'undefined' token.
* Access the token value in templates with the angular 'async' pipe.
* Access the token value in typescript code via rxjs pipes and subscribe.
*/
public token$: Observable<string>;
/**
* This replay subject emits the last event, if any, to new subscribers.
*/
private tokenSubject: ReplaySubject<string>;
constructor() {
this.tokenSubject = new ReplaySubject(1);
this.token$ = this.tokenSubject.asObservable();
}
public setToken(token: string) {
this.tokenSubject.next(token);
}
}
此TokenService用于ngOnInit中的TokenComponent:
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router, ParamMap } from '@angular/router';
import { Observable } from 'rxjs';
import { map, tap } from 'rxjs/operators';
import { TokenService } from './token.service';
@Component({
selector: 'app-token',
template: `
<p>Token in TokenComponent (for Demonstration):
{{tokenService.token$ | async}}
</p>
<!--
This router-outlet is neccessary to hold the child components
InboxComponent and DashboardComponent
-->
<router-outlet></router-outlet>
`
})
export class TokenComponent implements OnInit {
public mytoken$: Observable<string>;
constructor(
public route: ActivatedRoute,
public router: Router,
public tokenService: TokenService
) {
}
ngOnInit() {
this.route.paramMap.pipe(
map((params: ParamMap) => params.get('token')),
// this 'tap' - pipe is only for demonstration purposes
tap((token) => console.log(token))
)
.subscribe(token => this.tokenService.setToken(token));
}
}
最后,路由配置将这一切粘合在一起:
const routes: Routes = [
{
path: ':token', component: TokenComponent, children: [
{ path: '', pathMatch: 'full', redirectTo: 'inbox'},
{ path: 'dashboard', component: DashboardComponent },
{ path: 'inbox', component: InboxComponent },
]
},
{ path: '', pathMatch: 'full', redirectTo: '2345abcf/inbox' }
];
<p>Token: {{tokenService.token$ | async}} (should be 2345abcf)</p>
this.tokenService.token$
.subscribe(token => console.log(`Token in Inbox Component: ${token}`));