Skip to content

Instantly share code, notes, and snippets.

@muhammedaltug
Last active November 18, 2020 14:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save muhammedaltug/b35f72555900e2920bc8c1f87b7739cc to your computer and use it in GitHub Desktop.
Save muhammedaltug/b35f72555900e2920bc8c1f87b7739cc to your computer and use it in GitHub Desktop.
abp-routes component replacement in abp app pro template

Abp routes component can be replaced with these steps

  1. Run the following command to create a new component called RoutesComponent:

    yarn ng generate component routes --inlineStyle
  2. Open the generated routes.component.ts file in src/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);
          });
      }
    }
  1. 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>
  1. Open app.component.ts in src/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
        });
      }
    }

Changing Collapse Animation

  1. 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
    })
  1. 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>
    ...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment