-
Run the following command to create a new component called
RoutesComponent
:yarn ng generate component routes --inlineStyle
-
Open the generated
routes.component.ts
file insrc/app/routes
and replace content with following:
import { ABP, findRoute, getRoutePath, RoutesService, TreeNode } from '@abp/ng.core';
import { collapse } from '@abp/ng.theme.shared';
import {
ChangeDetectionStrategy,
Component,
EventEmitter,
Input,
OnInit,
Output,
TrackByFunction
} from '@angular/core';
import { Router } from '@angular/router';
@Component({
selector: 'app-routes',
templateUrl: './routes.component.html',
animations: [collapse],
})
export class RoutesComponent implements OnInit {
@Output() clickedToLink = new EventEmitter<MouseEvent>();
@Input() isMenuPlacementTop: boolean;
@Input() smallScreen: boolean;
readonly expandedRoutes = new Set<string>();
trackByFn: TrackByFunction<TreeNode<ABP.Route>> = (_, item) => item.name;
constructor(private router: Router, public readonly routes: RoutesService) {}
isDropdown(node: TreeNode<ABP.Route>) {
return !node?.isLeaf || this.routes.hasChildren(node.name);
}
ngOnInit() {
let node = findRoute(this.routes, getRoutePath(this.router));
node = { parent: node } as TreeNode<ABP.Route>;
while (node.parent) {
this.expandedRoutes.add(node.name);
node = node.parent;
}
this.expandedRoutes.add(node.name);
}
onNavigate({ parentName }: ABP.Route) {
const parents = [parentName];
let node = this.routes.find(item => item.name === parentName);
while (node?.parent) {
node = node.parent;
parents.push(node.name);
}
this.expandedRoutes.forEach(expanded => {
if (parents.indexOf(expanded) < 0) this.expandedRoutes.delete(expanded);
});
}
toggleExpand({ name, parentName }: ABP.Route) {
const has = this.expandedRoutes.has(name);
this.expandedRoutes[has ? 'delete' : 'add'](name);
if (!has) {
this.collapseDropdowns(name, parentName);
}
}
collapseDropdowns(name: string, parentName: string) {
this.routes.flat
.filter(route => route.name !== name && route.parentName === parentName && !route.invisible)
.forEach(route => {
this.expandedRoutes.delete(route.name);
});
}
}
- Open the generated
routes.component.html
file in `src/app/routes and replace content with following:
<div class="lp-sidebar-wrapper">
<nav role="navigation" class="lp-sidebar-navi">
<ul>
<ng-container
*ngFor="let route of routes.visible$ | async; trackBy: trackByFn"
[ngTemplateOutlet]="isDropdown(route) ? dropdownLink : defaultLink"
[ngTemplateOutletContext]="{ $implicit: route }"
>
</ng-container>
<ng-template #defaultLink let-route>
<li
routerLinkActive="active-page current"
[routerLinkActiveOptions]="{ exact: route.path === '/' }"
[abpPermission]="route.requiredPolicy"
(click)="$event.stopPropagation(); onNavigate(route)"
>
<a [routerLink]="[route.path]" (click)="clickedToLink.emit()">
<ng-container
*ngTemplateOutlet="linkContent; context: { $implicit: route }"
></ng-container>
</a>
</li>
</ng-template>
<ng-template #linkContent let-route>
<span *ngIf="route.iconClass" class="lp-icon">
<i [ngClass]="route.iconClass"></i>
</span>
<span class="lp-text">
{{ route.name | abpLocalization }}
</span>
</ng-template>
<ng-template #dropdownLink let-route>
<li
[abpPermission]="route.requiredPolicy"
[abpVisibility]="routeContainer"
class="has-drop"
[class.current]="expandedRoutes.has(route.name)"
>
<a
href="javascript:void(0)"
(click)="
$event.stopPropagation();
isMenuPlacementTop && !smallScreen ? null : toggleExpand(route)
"
>
<ng-container
*ngTemplateOutlet="linkContent; context: { $implicit: route }"
></ng-container>
<span class="lp-arrow-icon" [attr.for]="route.name">
<i class="fa fa-chevron-down"></i>
</span>
</a>
<ul
class="dropdown-ul"
[ngClass]="{
'd-block overflow-hidden': isMenuPlacementTop && !smallScreen ? false : true
}"
[id]="route.name"
>
<div
#routeContainer
[@collapse]="
!isMenuPlacementTop
? expandedRoutes.has(route.name)
? 'expanded'
: 'collapsed'
: ''
"
>
<ng-container
*ngFor="let child of route.children; trackBy: trackByFn"
[ngTemplateOutlet]="dropdownMenu"
[ngTemplateOutletContext]="{ $implicit: child }"
></ng-container>
</div>
</ul>
</li>
</ng-template>
<ng-template #dropdownMenu let-route let-parentName="parentName">
<ng-container
*ngTemplateOutlet="
isDropdown(route) ? dropdownLink : defaultLink;
context: { $implicit: route }
"
></ng-container>
</ng-template>
</ul>
</nav>
</div>
- Open
app.component.ts
insrc/app
folder and modify as shown below:
import { ReplaceableComponentsService } from '@abp/ng.core'; // added this line
import { Component } from '@angular/core';
import { RoutesComponent } from './routes/routes.component'; // added this line
import { eThemeLeptonComponents } from '@volo/abp.ng.theme.lepton'; // added this line
@Component({
selector: 'app-root',
template: `
<abp-loader-bar></abp-loader-bar>
<router-outlet></router-outlet>
`,
})
export class AppComponent {
constructor(private replaceableComponentsService: ReplaceableComponentsService) { // injected ReplaceableComponentsService
// added below
replaceableComponentsService.add({
component: RoutesComponent,
key: eThemeLeptonComponents.Routes
});
}
}
- Add your animation to routes component metadata in
routes.component.ts
@Component({
selector: 'app-routes',
templateUrl: './routes.component.html',
animations: [yourAnimation] //replace yourAnimation with collapse const
})
- Replace animation in
routes.component.html
...
<div
#routeContainer
[@yourAnimation]="Your Animation State" <!-- replace @collapse animation with @yourAnimation -->
>
<ng-container
*ngFor="let child of route.children; trackBy: trackByFn"
[ngTemplateOutlet]="dropdownMenu"
[ngTemplateOutletContext]="{ $implicit: child }"
></ng-container>
</div>
...