Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular How to change navmenu header based on route

I am working on a Dashboard layout in my current project with Angular 4+.

When the user navigates between the different sections of the app I need the navmenu header title to update to reflect the current section of the app.

enter image description here

As an example when the user visits settings "Page Title" should change to "Settings"

enter image description here

The project is based on the .net core 2 Angular template Below is the code I have to makeup the app routing as well as the dashboard routing.

navigation.service

import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';

@Injectable()
export class NavigationService {
    private titleSource = new BehaviorSubject<string>("Page Title");
    currentTitle = this.titleSource.asObservable();

    constructor() {
    }

    changeTitle(title: string) {
        this.titleSource.next(title);
    }
}

app-routing.module

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';

import { HomeComponent } from "./components/home/home.component";

const appRoutes: Routes = [
    { path: '', redirectTo: 'home', pathMatch: 'full' },
    { path: 'home', component: HomeComponent },
    { path: '**', redirectTo: 'home' }
]

@NgModule({
    imports: [
        RouterModule.forRoot(appRoutes)],
    exports: [RouterModule]
})
export class AppRoutingModule { }

app.module.shared

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';

import { AppRoutingModule } from './app-routing.module';
import { DashboardModule } from "./components/dashboard/dashboard.module";

import { UtilsModule } from "./components/shared/shared.module";
//app components
import { AppComponent } from './components/app/app.component';

import { NavigationService } from "./services/navigation.service";

@NgModule({
    declarations: [
        AppComponent,

    ],
    imports: [
        SharedModule,
        DashboardModule,
        AppRoutingModule,
        DevExtremeModule,
        CommonModule,
        HttpModule,
        FormsModule
    ],
    providers: [NavigationService]
})
export class AppModuleShared {
}

dashboard-routing.module

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';

import { DashboardComponent } from "./dashboard.component";
import { SettingsProfileComponent } from "./settings/settingsProfile/settings.profile.component";
import { SettingsEmailComponent } from "./settings/settingsEmail/settings.email.component";
import { UsersListComponent } from "./users/users.list.component";

export const dashboardRoutes: Routes = [
    {
        path: 'dashboard',
        component: DashboardComponent,
        children: [{
            path: 'settings',
            data: { title: 'Settings' },

            children: [{
                path: 'profile',
                component: SettingsProfileComponent
            },
            {
                path: 'email',
                component: SettingsEmailComponent
            }]
        }, {
                path: 'users',
                data: { title: 'Users' },
                children: [{
                    path: '',
                    component: UsersListComponent
                }]
        }]
    }
];

@NgModule({
    imports: [
        RouterModule.forChild(dashboardRoutes)
    ],
    exports: [RouterModule]
})
export class DashboardRoutingModule { }

dashboard.module

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { UtilsModule } from "../shared/shared.module";

import { DashboardRoutingModule } from "./dashboard-routing.module";

import { DashboardComponent } from './dashboard.component';
import { SettingsProfileComponent } from "./settings/settingsProfile/settings.profile.component";
import { SettingsEmailComponent } from "./settings/settingsEmail/settings.email.component";
import { UsersListComponent } from "./users/users.list.component";

@NgModule({
    imports: [
        CommonModule,
        UtilsModule,
        DashboardRoutingModule
    ],
    declarations: [
        DashboardComponent,
        SettingsProfileComponent,
        SettingsEmailComponent,
        UsersListComponent,
    ],
    providers: []
})
export class DashboardModule {
}

I am trying to avoid having a OnInit in each component. Is it possible to do this only in the routing? The project is early on so if there are any other recommendations on how to accomplish this we are open to change.

like image 220
gboh Avatar asked Jan 25 '18 15:01

gboh


2 Answers

There are many ways to handle this. One possible solution is to listen to router events and based on them decide about what content should be displayed.

This approach would reguire importing navigation state first, for example, when route change ended. Like this:

import { Router, NavigationEnd } from '@angular/router';

then you need to subscirbe to that, for example like this.

constructor ( private router:  Router ) {
router.events.subscribe( (event) => ( event instanceof NavigationEnd ) && this.handleRouteChange() )

}

Once you are able to detect change in navigation you can see, what route it is and based on that make further deccisions. For example like this:

  handleRouteChange = () => {
    if (this.router.url.includes('some_key_part_of_url') {
     do what ever you want to do with your content...
    }
  };

You do not necessarily analyse the actual content of the url, you may decide on other router properties. Please, read the docs on router.

like image 89
Kristian Avatar answered Nov 17 '22 15:11

Kristian


Another option may be a route guard/route resolver. You could add a guard/resolver to each route and have it read your data property.

Here is an example resolver:

import { Injectable } from '@angular/core';
import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';

import { Observable } from 'rxjs/Observable';

import { IMovie } from './movie';
import { MovieService } from './movie.service';

@Injectable()
export class MovieResolver implements Resolve<IMovie> {

    constructor(private movieService: MovieService,
                private navigationService: NavigationService) { }

    resolve(route: ActivatedRouteSnapshot,
            state: RouterStateSnapshot): boolean {
        const title = route.data['title'];
        this.navigationService.changeTitle(title);
        return true;
    }
}

Something like the above.

like image 23
DeborahK Avatar answered Nov 17 '22 16:11

DeborahK