Один из вариантов, который я попробовал, - это иметь 3 разделенные области, где в 1 из них есть выход для маршрутизатора.Другие 2 имеют в качестве содержимого компоненты counter и fetch-data.При использовании в качестве одностраничного приложения видна только 1-я разделенная область.
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 можно установить видимыми с помощью флажка в компоненте навигации, который выглядит ниже.Обратите внимание, что в моем случае необходимо контролировать, что компонент может быть виден только один раз в графическом интерфейсе.Это делается с помощью средств проверки подлинности для маршрутов и снятия флажков, упомянутых выше, чтобы предотвратить отображение области аплита для компонента, который уже виден в router-outlet.
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"]'>
<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;
}
}
Выше используется избыточность для управления изменениями состояния.Эта часть также находится ниже на случай, если кому-то интересно:
nav-menu.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;
}
}
}