简体   繁体   中英

In Angular2 how do you change a sidebar based on the active route?

I'm writing an SPA using Angular 2 (RC5) and Typescript. I have a main navbar on top (using Bootstrap 4 Alpha 3), and a custom-designed sidebar. Here's a pic:

在此处输入图片说明

The intention here is to navigate to a main "application" using the top navbar. Currently I have 3 main apps mapped out - Tracker, Proboard and Equipment. Once a main application has been activated, the links on the sidebar would change. In the picture above, the sidebar is showing links for the 'Proboard' application.

How can I change the contents of the sidebar based on the active route without having a bunch of '*ngIf' declarations in my HTML?

I would like to have a JSON object in my code somewhere (perhaps retrieved from a backend API) that would list the links for each app. Something like:

[
  { proboard: [
    { label: 'Home', link: '/proboard', icon: 'fa-home'},
    { label: 'Candidates', link: '/proboard/candidates', icon: 'fa-users'},
    { label: 'New', link: '/proboard/candidate/new', icon: 'fa-new-user; } ]
  },
  { tracker: [...] },
  { equipment: [...] }
]

Then when the user would navigate to one of the links on the top navbar, the sidebar would be populated with the contents in the array above, using an NgFor in the HTML.

Any suggestions or links would be appreciated.

Here's one possible solution.

Since each of your three apps is meant to be completely separate (as you've defined it), you could make a completely independent 'SideBarComponent' that you can reuse in each app.

Since you can give a component any information you want with @Input() variables, you can provide it the list of labels, links, and icons it needs. For example:

EquipmentComponent.ts

sideBarLinks: any = { proboard: [
  { label: 'Home', link: '/proboard', icon: 'fa-home'},
  { label: 'Candidates', link: '/proboard/candidates', icon: 'fa-users'},
  { label: 'New', link: '/proboard/candidate/new', icon: 'fa-new-user; } ]
}

EquipmentComponent.html

<side-bar [buttons]='sideBarLinks'></side-bar>

SideBarComponent.ts

@Input()
buttons: any;

SideBarComponent.html

// Use 'buttons' to construct your buttons in the sidebar. Possibly using an ng repeater in this template.

Even if you don't end up implementing this exactly, I hope this gets you thinking in the direction of using reusable components with Angular 2.

Hopefully this is helpful!

Here's more than everything you would need to know about component interactions: https://angular.io/docs/ts/latest/cookbook/component-communication.html .

I've done the same thing and here is how I solved it.

  1. Created the menu in the top-most app component
  2. Created a service and kept a common routing table in the service
  3. Created a subscribable subject stream for changing the route
  4. Subscribed to that stream from the component where the menu resides
  5. Issue subcription.next from the current component which sets the selected route in the component with the menu.

You can achieve this with other type of component interactions. Here is more info on that from the Angular 2 docs: https://angular.io/docs/ts/latest/cookbook/component-communication.html

2 possible ways

1 - In sidebar component, subscribe to router changes, but you will end up using *ngIf

@Input()
data: any;

constructor(private router: Router) {
    router.changes.subscribe((val) => {
        if(val === 'noSidebarRoute'){
            this.navButtons= false;
        }
        else {
            this.navButtons= navData;
        }
    })
}

then in parent component template

@Component({
    selector: 'parent',
    template: `<side-bar [data]='navData'></side-bar>`
});

export class Parent{

navData = [
     { proboard: [
       { label: 'Home', link: '/proboard', icon: 'fa-home'},
       { label: 'Candidates', link: '/proboard/candidates', icon: 'fa-users'},
       { label: 'New', link: '/proboard/candidate/new', icon: 'fa-new-user; }
     ]},
     { tracker: [...] },
     { equipment: [...] }
]}

2 - in your app component (or sidebar parent component), use resolver.resolveComponent to dynamically add the sidebar component to your app, this way you won't be using data binding ( *ngIf ).

sidebarCmpRef:ComponentRef<any>;

constructor(private router: Router,
            private viewContainerRef:ViewContainerRef,
            private resolver:ComponentResolver) {

    router.changes.subscribe((val) => {
        if(val === 'noSidebarRoute'){
            if(this.sidebarCmpRef){
               this.sidebarCmpRef.destroy();
               this.sidebarCmpRef
            }
        }
        else{
            this.AddSidebar();
        }
    })
}

AddSidebar(){
    if(!this.cmpRef){
        this.resolver.resolveComponent(<Type>SideBar)
          .then((factory:ComponentFactory<any>) => {
            this.cmpRef = this.viewContainerRef.createComponent(factory);
            this.cmpRef.instance.buttons= navData;
        });
    }
}

I would prefer to go with angular basic approach ie loading children inside the routes.

Define your main.route as follows

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { PageNotFoundComponent } from './shared/components/pagenotfound.component';

export const appRoutes: Routes = [
  {
    path: 'tracker',
    loadChildren: 'app/tracker/module#trackermodule',
  },
  {
    path: 'proboard',
    loadChildren: 'app/proboard/module#proboard',
  },
 {
    path: 'equiment',
    loadChildren: 'app/equiment/module#equiment',
  },
  {
    path: '**',
    component: PageNotFoundComponent
  }
];

@NgModule({
    imports: [RouterModule.forRoot(appRoutes)],
    declarations: [
        PageNotFoundComponent
    ],
    exports: [RouterModule]
})

export class AppRouterModule {
}

then in your child/sub-module routing configuration should be

    import { RouterModule } from '@angular/router';
import {  CssComponent } from './view';
import { RadioAndCheckBoxComponent } from './radio-and-checkbox/view';

export const RouteComponents = [
  CssComponent
];

export const Routes = RouterModule.forChild([
  {
    path: '',
    component: equimentComponent,
    children: [
      {
        path: 'Home',
        component: HomeComoponent
      },
      {
        path: 'Candidates',
        component: CandidatesComoponent
      }
    ]
  }
]);

I have same situation (Main Menu Links and their respected sub menu items), I have done this in following manner.

Inside the root component (app.component.ts) constructor, I subscribe to router events, and based on the redirect route URL, I hide and show the Main application sub menus. Here is code:

 this.router.events.subscribe(event => {
  if (event instanceof NavigationEnd) {

    this.currentUrl = event.urlAfterRedirects;
    console.log(event.urlAfterRedirects);

    if (this.currentUrl.includes("/MainMenuLink1")) {
      this.link1SubMenu = true;
      this.link2SubMenu = false;
    } else if (this.currentUrl.includes("/MainMenuLink2")) {
   this.link2SubMenu = true;
      this.link1SubMenu = false;
    }

  }
});

And based on that [link1SubMenu] boolean values, I show and hide the Sub menu in app.component.html using *ngIf='link1SubMenu'

It needs to be refactored, but this is quick win.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM