我正在构建一个角度为6的应用程序,该应用程序应具有两个特定的 router-outlet :
<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 有关更多信息,请参见控制台。
有什么主意吗?谢谢。
[编辑]
尝试了许多解决方案,但仍未找到正确的答案。
我看到路由模块中可以使用一个数据属性。我会检查的。
答案 0 :(得分:3)
因此,经过一些调试后,我发现了问题。
很抱歉,答案很长,在您解决问题后,我只是为您的代码添加了一些提示。
我为您准备了优化的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
参数仅提供给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。
查看官方的Angular文档: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‘>
应该加载路由/ status / online。
由于您省略了第二个出口的最后一部分,因此尚不清楚角度如何处理路线。
这使事情变得比实际复杂。 您不需要与内容分开导航,还是? 如果您不需要单独的导航,则可以轻松地将菜单直接包含到AppComponent模板中。
当您有诸如边栏聊天之类的东西时,第二个路由器插座才有意义,您可以在其中选择联系人,拥有聊天窗口和聊天设置页面,而所有这些都独立于您的主要方面。 然后,您将需要两个路由器出口。
您通过
将BasePage作为服务提供给Angular。@Injectable({
providedIn: ‚root‘
})
使用可注入服务作为基类似乎是一个非常奇怪的设计。 我想不出一个原因,我想使用它。 我要么使用BaseClass作为BaseClass,它独立于angular工作。 或者我使用角度服务并将其注入需要该服务功能的组件中。
注意:您不能简单地创建一个注入了Router
和ActivatedRoute
的服务,因为它将获取这些组件的根对象,即空的。
Angular为每个组件生成自己的ActivatedRoute
实例,并将其注入到组件中。
解决方案:请参阅下面的“更好的解决方案:具有令牌服务的TokenComponent”。
我现在能想到的最好的解决方案是与TokenService一起构建TokenComponent。 您可以在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}`));