为什么 routerLink 和 router.navigate() 的行为不同?
Why routerLink and router.navigate() act differently?
在 HTML 中使用此代码时:
<button [routerLink]="[{ outlets: { flow: ['step1'] } }]">click me to show step1</button>
它正确导航到“/child/(flow:step1)”!!!
尝试在 Typescript 中使用此代码时:
this.router.navigate([{ outlets: { flow: ['step1'] } }]);
它试图导航到错误的路径“/child(flow:step1)”!!!
它只是缺少斜线。
服务:
import { Injectable } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { EventBusService } from '../../../services/eventBus/eventBus.service';
import { RouterService } from '../../../services/router.service';
@Injectable()
export class FlowManagerService {
constructor(private router: Router, private r: ActivatedRoute, private eventBus: EventBusService, private routerService: RouterService) {
}
initValidStep() {
return (parseInt(this.routerService.currentUrlName.substr(this.routerService.currentUrlName.indexOf('step'), 5).replace('step', ''), 10) === 1);
}
goToFirstStep() {
this.router.navigate([{ outlets: { flow: ['step1'] } }], {relativeTo: this.r});
this.eventBus.off(this.eventBus.globalEvents.FLOW.FLOW_STEP_NEXT);
this.eventBus.off(this.eventBus.globalEvents.FLOW.FLOW_STEP_BACK);
}
next(params) {
const currentStep = this.routerService.currentUrlName.substr(this.routerService.currentUrlName.indexOf('step'), 5).replace('step', '');
this.eventBus.emit(this.eventBus.globalEvents.FLOW.FLOW_STEP_CHANGE, ({
type: 'NEXT'
}));
this.router.navigate([{ outlets: { flow: [`step${parseInt(currentStep, 10) + 1}`, params] } }], {relativeTo: this.r});
this.eventBus.off(this.eventBus.globalEvents.FLOW.FLOW_STEP_NEXT);
this.eventBus.off(this.eventBus.globalEvents.FLOW.FLOW_STEP_BACK);
}
back(params) {
const currentStep = this.routerService.currentUrlName.substr(this.routerService.currentUrlName.indexOf('step'), 5).replace('step', '');
this.eventBus.emit(this.eventBus.globalEvents.FLOW.FLOW_STEP_CHANGE, ({
type: 'NEXT'
}));
this.router.navigate([{ outlets: { flow: [`step${parseInt(currentStep, 10) - 1}`, params] } }], {relativeTo: this.r});
this.eventBus.off(this.eventBus.globalEvents.FLOW.FLOW_STEP_NEXT);
this.eventBus.off(this.eventBus.globalEvents.FLOW.FLOW_STEP_BACK);
}
}
这是使用上述服务的模块:
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { FlowManagerService } from './service/flowManager.service';
import { CommonModule } from '@angular/common';
@NgModule({
providers: [FlowManagerService],
imports: [
RouterModule,
CommonModule
]
})
export class FlowManagerModule {
}
因为 routerLink
隐含地使用了 relativeTo
选项:
export class RouterLink {
...
get urlTree(): UrlTree {
return this.router.createUrlTree(this.commands, {
relativeTo: this.route, <----
您需要在router.navigate
中明确提供:
constructor(private route: ActivatedRoute)
this.router.navigate([{ outlets: { flow: ['step1'] } }], {relativeTo: this.route});
这里是plunker和完整的工作代码:
import { Component, NgModule } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
import { RouterModule, Routes, Resolve, Router, ActivatedRoute } from '@angular/router';
import { APP_BASE_HREF } from '@angular/common';
@Component({
selector: 'my-app',
template: `
<div id='my-app'>
<router-outlet></router-outlet>
</div>
`,
})
export class App {
constructor() {
}
}
@Component({
selector: 'master-page',
template: `
<div id='master-page'>
<div>Master Component</div>
<button (click)='clickFirst()'>Inner Section 1</button>
<button (click)='clickSecond()'>Inner Section 2</button>
<router-outlet name='child'></router-outlet>
</div>
`
})
export class Master {
constructor(private router: Router, private activeRouter: ActivatedRoute) {
}
clickFirst() {
this.router.navigate([{outlets: {child: 'details1'}}], {relativeTo: this.activeRouter});
}
clickSecond() {
this.router.navigate([{outlets: {child: 'details2'}}], {relativeTo: this.activeRouter});
}
}
@Component({
template: `
<div>
This content is in the "Inner" page (1)
</div>
`
})
export class Details1 {
constructor() {
}
}
@Component({
template: `
<div>
This content is in the "Inner" page (2)
</div>
`
})
export class Details2 {
constructor() {
}
}
const routes: Routes = [
{
path: 'master',
component: Master,
children: [
{
path: 'details1',
component: Details1,
outlet: 'child'
},
{
path: 'details2',
component: Details2,
outlet: 'child'
}
]
},
{
path: '',
pathMatch: 'prefix',
redirectTo: 'master'
}
];
@NgModule({
imports: [BrowserModule, RouterModule.forRoot(routes)],
declarations: [App, Master, Details1, Details2],
providers: [{
provide: APP_BASE_HREF,
useValue: '/'
}],
bootstrap: [App]
})
export class AppModule {
}
在 HTML 中使用此代码时:
<button [routerLink]="[{ outlets: { flow: ['step1'] } }]">click me to show step1</button>
它正确导航到“/child/(flow:step1)”!!!
尝试在 Typescript 中使用此代码时:
this.router.navigate([{ outlets: { flow: ['step1'] } }]);
它试图导航到错误的路径“/child(flow:step1)”!!!
它只是缺少斜线。
服务:
import { Injectable } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { EventBusService } from '../../../services/eventBus/eventBus.service';
import { RouterService } from '../../../services/router.service';
@Injectable()
export class FlowManagerService {
constructor(private router: Router, private r: ActivatedRoute, private eventBus: EventBusService, private routerService: RouterService) {
}
initValidStep() {
return (parseInt(this.routerService.currentUrlName.substr(this.routerService.currentUrlName.indexOf('step'), 5).replace('step', ''), 10) === 1);
}
goToFirstStep() {
this.router.navigate([{ outlets: { flow: ['step1'] } }], {relativeTo: this.r});
this.eventBus.off(this.eventBus.globalEvents.FLOW.FLOW_STEP_NEXT);
this.eventBus.off(this.eventBus.globalEvents.FLOW.FLOW_STEP_BACK);
}
next(params) {
const currentStep = this.routerService.currentUrlName.substr(this.routerService.currentUrlName.indexOf('step'), 5).replace('step', '');
this.eventBus.emit(this.eventBus.globalEvents.FLOW.FLOW_STEP_CHANGE, ({
type: 'NEXT'
}));
this.router.navigate([{ outlets: { flow: [`step${parseInt(currentStep, 10) + 1}`, params] } }], {relativeTo: this.r});
this.eventBus.off(this.eventBus.globalEvents.FLOW.FLOW_STEP_NEXT);
this.eventBus.off(this.eventBus.globalEvents.FLOW.FLOW_STEP_BACK);
}
back(params) {
const currentStep = this.routerService.currentUrlName.substr(this.routerService.currentUrlName.indexOf('step'), 5).replace('step', '');
this.eventBus.emit(this.eventBus.globalEvents.FLOW.FLOW_STEP_CHANGE, ({
type: 'NEXT'
}));
this.router.navigate([{ outlets: { flow: [`step${parseInt(currentStep, 10) - 1}`, params] } }], {relativeTo: this.r});
this.eventBus.off(this.eventBus.globalEvents.FLOW.FLOW_STEP_NEXT);
this.eventBus.off(this.eventBus.globalEvents.FLOW.FLOW_STEP_BACK);
}
}
这是使用上述服务的模块:
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { FlowManagerService } from './service/flowManager.service';
import { CommonModule } from '@angular/common';
@NgModule({
providers: [FlowManagerService],
imports: [
RouterModule,
CommonModule
]
})
export class FlowManagerModule {
}
因为 routerLink
隐含地使用了 relativeTo
选项:
export class RouterLink {
...
get urlTree(): UrlTree {
return this.router.createUrlTree(this.commands, {
relativeTo: this.route, <----
您需要在router.navigate
中明确提供:
constructor(private route: ActivatedRoute)
this.router.navigate([{ outlets: { flow: ['step1'] } }], {relativeTo: this.route});
这里是plunker和完整的工作代码:
import { Component, NgModule } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
import { RouterModule, Routes, Resolve, Router, ActivatedRoute } from '@angular/router';
import { APP_BASE_HREF } from '@angular/common';
@Component({
selector: 'my-app',
template: `
<div id='my-app'>
<router-outlet></router-outlet>
</div>
`,
})
export class App {
constructor() {
}
}
@Component({
selector: 'master-page',
template: `
<div id='master-page'>
<div>Master Component</div>
<button (click)='clickFirst()'>Inner Section 1</button>
<button (click)='clickSecond()'>Inner Section 2</button>
<router-outlet name='child'></router-outlet>
</div>
`
})
export class Master {
constructor(private router: Router, private activeRouter: ActivatedRoute) {
}
clickFirst() {
this.router.navigate([{outlets: {child: 'details1'}}], {relativeTo: this.activeRouter});
}
clickSecond() {
this.router.navigate([{outlets: {child: 'details2'}}], {relativeTo: this.activeRouter});
}
}
@Component({
template: `
<div>
This content is in the "Inner" page (1)
</div>
`
})
export class Details1 {
constructor() {
}
}
@Component({
template: `
<div>
This content is in the "Inner" page (2)
</div>
`
})
export class Details2 {
constructor() {
}
}
const routes: Routes = [
{
path: 'master',
component: Master,
children: [
{
path: 'details1',
component: Details1,
outlet: 'child'
},
{
path: 'details2',
component: Details2,
outlet: 'child'
}
]
},
{
path: '',
pathMatch: 'prefix',
redirectTo: 'master'
}
];
@NgModule({
imports: [BrowserModule, RouterModule.forRoot(routes)],
declarations: [App, Master, Details1, Details2],
providers: [{
provide: APP_BASE_HREF,
useValue: '/'
}],
bootstrap: [App]
})
export class AppModule {
}