如果更改单页应用程序以使 2 条路由在拆分视图中都可见,是不是可以使用 Angular 路由?
Posted
技术标签:
【中文标题】如果更改单页应用程序以使 2 条路由在拆分视图中都可见,是不是可以使用 Angular 路由?【英文标题】:Can Angular Routing be used if single page application is changed so that 2 routes can be both visible w splitted view?如果更改单页应用程序以使 2 条路由在拆分视图中都可见,是否可以使用 Angular 路由? 【发布时间】:2019-03-27 10:56:52 【问题描述】:我有一个由VS2017 Angular template 创建的示例应用程序,它是一个单页应用程序,在 app.module.ts 中定义了 3 个路由
RouterModule.forRoot([
path: '', component: HomeComponent, pathMatch: 'full' ,
path: 'counter', component: CounterComponent ,
path: 'fetch-data', component: FetchDataComponent ,
])
在 app.component.html 中
<body>
<app-nav-menu></app-nav-menu>
<div class="container">
<router-outlet></router-outlet>
</div>
</body>
导航在 nav-menu.component.html 中被定义
<header>
<nav class='navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3'>
<div class="container">
<a class="navbar-brand" [routerLink]='["/"]'>my_new_app</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target=".navbar-collapse" aria-label="Toggle navigation"
[attr.aria-expanded]="isExpanded" (click)="toggle()">
<span class="navbar-toggler-icon"></span>
</button>
<div class="navbar-collapse collapse d-sm-inline-flex flex-sm-row-reverse" [ngClass]='"show": isExpanded'>
<ul class="navbar-nav flex-grow">
<li class="nav-item" [routerLinkActive]='["link-active"]' [routerLinkActiveOptions]=' exact: true '>
<a class="nav-link text-dark" [routerLink]='["/"]'>Home</a>
</li>
<li class="nav-item" [routerLinkActive]='["link-active"]'>
<a class="nav-link text-dark" [routerLink]='["/counter"]'>Counter</a>
</li>
<li class="nav-item" [routerLinkActive]='["link-active"]'>
<a class="nav-link text-dark" [routerLink]='["/fetch-data"]'>Fetch data</a>
</li>
</ul>
</div>
</div>
</nav>
</header>
选择计数器的正常情况如下所示(如果导航在侧面):
Home | Counter |
Counter (x) | |
Fetch | |
在某些情况下,我需要让 2 个“主级别”组件可见,以便路由器出口中不会有 1 个组件的区域,而是将区域分成 2 条,并且 2 条路由会以某种方式处于活动状态。
Home | Counter | Fetch |
Counter (x) | | |
Fetch (x) | | |
可以或应该通过角度路由来完成吗? 正常使用仍然是只有 1 条路由处于活动状态,并且路由器出口区域没有被分割。
这可以通过例如使用 ngIf 并使用(切换)按钮而不是路由器链接来完成。但是,我正在开发一个非常大的应用程序,并且如果可能的话,我对使用路由很感兴趣。
链接的“重复”是关于第二个路由器出口的,与此无关。在这里,我想要实现的是主路由器插座同时有 2 条路由处于活动状态并且内容被拆分。这可能是不可能的,但这就是想法,而不是一些侧边栏辅助导航。
【问题讨论】:
Angular2 multiple router-outlet in the same template的可能重复 @OneLunchMan:这是怎么重复的?如果我理解所问的内容,那就完全不同了。 【参考方案1】:我尝试过的一个选项是有 3 个分割区域,其中 1 个有一个路由器插座。其他 2 个将 counter 和 fetch-data 组件作为其内容。当用作单页应用程序时,只有第一个分割区域是可见的。
app.component.html
<body>
<app-nav-menu></app-nav-menu>
<div id="working" >
<as-split direction="horizontal">
<as-split-area>
<router-outlet></router-outlet>
</as-split-area>
<as-split-area *ngIf="secondSplitAreaVisible">
<app-counter-component></app-counter-component>
</as-split-area>
<as-split-area *ngIf="thirdSplitAreaVisible">
<app-fetch-data></app-fetch-data>
</as-split-area>
</as-split>
</div>
</body>
其他 2 个可以通过导航组件中的复选框设置为可见,如下所示。请注意,在我的情况下,必须控制组件只能在 GUI 中可见一次。这是通过对路由使用 auth 保护并禁用上述复选框来完成的,以防止显示在 router-outlet 中已经可见的组件的 aplit 区域。
导航菜单.component.html:
<header>
<nav class='navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3'>
<div class="container">
<a class="navbar-brand" [routerLink]='["/"]'>my_new_app</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target=".navbar-collapse" aria-label="Toggle navigation"
[attr.aria-expanded]="isExpanded" (click)="toggle()">
<span class="navbar-toggler-icon"></span>
</button>
<div class="navbar-collapse collapse d-sm-inline-flex flex-sm-row-reverse" [ngClass]='"show": isExpanded'>
<ul class="navbar-nav flex-grow">
<li class="nav-item" [routerLinkActive]='["link-active"]'>
<a class="nav-link text-dark" [routerLink]='["/home"]'><mat-checkbox [(ngModel)]="firstChecked" (change)="toggleTab('home')" [disabled]="firstDisabled"></mat-checkbox>Home</a>
</li>
<li class="nav-item" [routerLinkActive]='["link-active"]' [ngStyle]="'border-bottom' : secondChecked || secondActive ? '2px solid' : '0px' ">
<a class="nav-link text-dark" [routerLink]='["/counter"]'>
<mat-checkbox [(ngModel)]="secondChecked" (change)="toggleTab('counter', secondChecked)" [disabled]="secondActive"></mat-checkbox>Counter</a>
</li>
<li class="nav-item" [routerLinkActive]='["link-active"]' [ngStyle]="'border-bottom' : thirdChecked || thirdActive ? '2px solid' : '0px' ">
<a class="nav-link text-dark" [routerLink]='["/fetch-data"]'><mat-checkbox [(ngModel)]="thirdChecked" (change)="toggleTab('fetch-data', thirdChecked)" [disabled]="thirdActive"></mat-checkbox>Fetch data</a>
</li>
</ul>
</div>
</div>
</nav>
</header>
app.module.ts 路由定义
RouterModule.forRoot([
path: 'home', component: HomeComponent, canActivate: [AuthGuard],
path: 'counter', component: CounterComponent, canActivate: [AuthGuard] ,
path: 'fetch-data', component: FetchDataComponent, canActivate: [AuthGuard],
path: '', redirectTo: '/home', pathMatch: 'full'
和授权守卫:
@Injectable(
providedIn: 'root',
)
export class AuthGuard implements CanActivate
subscription;
outletUrl: string;
secondSplitAreaVisible: boolean = false;
thirdSplitAreaVisible: boolean = false;
constructor(
private router: Router,
private ngRedux: NgRedux<IAppState>,
private actions: TabActions)
this.subscription = ngRedux.select<string>('outletUrl')
.subscribe(newUrl => this.outletUrl = newUrl); // <- New
this.subscription = ngRedux.select<boolean>('secondOpen') // <- New
.subscribe(newSecondVisible => this.secondSplitAreaVisible = newSecondVisible); // <- New
this.subscription = ngRedux.select<boolean>('thirdOpen') // <- New
.subscribe(newThirdVisible => this.thirdSplitAreaVisible = newThirdVisible); // <- New
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean
if (state.url === '/counter' && this.secondSplitAreaVisible)
return false;
if (state.url === '/fetch-data' && this.thirdSplitAreaVisible)
return false;
return true;
以上使用 redux 来管理状态更改。该部分也在下面,以防有人感兴趣:
导航菜单.component.ts
@Component(
selector: 'app-nav-menu',
templateUrl: './nav-menu.component.html',
styleUrls: ['./nav-menu.component.css']
)
export class NavMenuComponent
firstChecked: boolean = false;
secondChecked: boolean = false;
thirdChecked: boolean = false;
firstDisabled: boolean = true;
secondActive: boolean = false;
thirdActive: boolean = false;
constructor(
private ngRedux: NgRedux<IAppState>,
private actions: TabActions,
private router: Router)
router.events.subscribe((event) =>
if (event instanceof NavigationEnd)
this.ngRedux.dispatch(this.actions.setOutletActiveRoute(event.url));
if (event.url.includes('counter'))
this.secondActive = true;
this.thirdActive = false;
this.firstChecked = false;
else if (event.url.includes('fetch'))
this.thirdActive = true;
this.secondActive = false;
this.firstChecked = false;
else
// home
this.secondActive = false;
this.thirdActive = false;
this.firstChecked = true;
);
isExpanded = false;
collapse()
this.isExpanded = false;
toggle()
this.isExpanded = !this.isExpanded;
toggleTab(name: string, isChecked : boolean)
this.ngRedux.dispatch(this.actions.toggleSplitArea( splitArea : name, isVisible: isChecked));
app.component.ts
@Component(
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
)
export class AppComponent implements OnDestroy
title = 'app';
secondSplitAreaVisible: boolean = false;
thirdSplitAreaVisible: boolean = false;
subscription;
constructor(
private ngRedux: NgRedux<IAppState>,
private actions: TabActions)
this.subscription = ngRedux.select<boolean>('secondOpen')
.subscribe(newSecondVisible =>
this.secondSplitAreaVisible = newSecondVisible;
);
this.subscription = ngRedux.select<boolean>('thirdOpen')
.subscribe(newThirdVisible =>
this.thirdSplitAreaVisible = newThirdVisible;
);
ngOnDestroy()
this.subscription.unsubscribe();
app.actions.ts
@Injectable()
export class TabActions
static TOGGLE_SPLIT_AREA = 'TOGGLE_SPLIT_AREA';
static SET_OUTLET_ACTIVE_ROUTE = 'SET_OUTLET_ACTIVE_ROUTE';
toggleSplitArea(splitAreaToggle: SplitAreaToggle): SplitAreaToggleAction
return
type: TabActions.TOGGLE_SPLIT_AREA,
splitAreaToggle
;
setOutletActiveRoute(url: string) : SetOutletActiveRouteAction
return
type: TabActions.SET_OUTLET_ACTIVE_ROUTE,
url
;
store.ts
export interface IAppState
outletUrl : string;
secondOpen : boolean;
thirdOpen : boolean;
;
export const INITIAL_STATE: IAppState =
outletUrl: 'home',
secondOpen : false,
thirdOpen : false
;
export function rootReducer(lastState: IAppState, action: Action): IAppState
switch(action.type)
case TabActions.SET_OUTLET_ACTIVE_ROUTE:
const setRouteAction = action as SetOutletActiveRouteAction;
const newState: IAppState =
...lastState,
outletUrl: setRouteAction.url
return newState;
case TabActions.TOGGLE_SPLIT_AREA:
const splitToggleAction = action as SplitAreaToggleAction;
console.log('rootreducer splitareatoggle:' + splitToggleAction.splitAreaToggle.splitArea);
if (splitToggleAction.splitAreaToggle.splitArea === 'counter')
const newState: IAppState =
...lastState,
secondOpen: splitToggleAction.splitAreaToggle.isVisible
return newState;
else
const newState: IAppState =
...lastState,
thirdOpen: splitToggleAction.splitAreaToggle.isVisible
return newState;
default :
return lastState;
【讨论】:
以上是关于如果更改单页应用程序以使 2 条路由在拆分视图中都可见,是不是可以使用 Angular 路由?的主要内容,如果未能解决你的问题,请参考以下文章