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.
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.
Share Improve this question edited Dec 13, 2017 at 12:49 Freego 4667 silver badges18 bronze badges asked Aug 23, 2016 at 18:52 John DiblingJohn Dibling 102k32 gold badges191 silver badges332 bronze badges5 Answers
Reset to default 1I've done the same thing and here is how I solved it.
- Created the menu in the top-most app ponent
- Created a service and kept a mon routing table in the service
- Created a subscribable subject stream for changing the route
- Subscribed to that stream from the ponent where the menu resides
- Issue subcription.next from the current ponent which sets the selected route in the ponent with the menu.
You can achieve this with other type of ponent interactions. Here is more info on that from the Angular 2 docs: https://angular.io/docs/ts/latest/cookbook/ponent-munication.html
Here's one possible solution.
Since each of your three apps is meant to be pletely separate (as you've defined it), you could make a pletely independent 'SideBarComponent' that you can reuse in each app.
Since you can give a ponent 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 ponents with Angular 2.
Hopefully this is helpful!
Here's more than everything you would need to know about ponent interactions: https://angular.io/docs/ts/latest/cookbook/ponent-munication.html.
2 possible ways
1 - In sidebar ponent, 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 ponent 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 ponent (or sidebar parent ponent), use resolver.resolveComponent
to dynamically add the sidebar ponent 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 i.e 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/ponents/pagenotfound.ponent';
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: '**',
ponent: 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: '',
ponent: equimentComponent,
children: [
{
path: 'Home',
ponent: HomeComoponent
},
{
path: 'Candidates',
ponent: CandidatesComoponent
}
]
}
]);
I have same situation (Main Menu Links and their respected sub menu items), I have done this in following manner.
Inside the root ponent (app.ponent.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.ponent.html using *ngIf='link1SubMenu'
It needs to be refactored, but this is quick win.