I'm trying to show the navigation bar, once the user successfully do.
For example:
How To Change "showMenu" property in "AppComponent" inside the "LoginComponent"? Important: I am using routes.
app.ts:
@Component({
selector: 'app',
template: `<div *ngIf="showMenu">
<fnd-menu-nav></fnd-menu-nav>
</div>
<router-outlet></router-outlet>
`,
directives: [ROUTER_DIRECTIVES, MenuNavComponent]
})
@RouteConfig([
{ path: '/login', name: 'Login', component: LoginComponent, useAsDefault: true },
{ path: '/welcome', name: 'Welcome', component: WelcomeComponent }
])
export class AppComponent {
public showMenu : boolean;
}
login.component.ts:
@Component({
selector: 'fnd-login',
templateUrl: './fnd/login/components/login.component.html',
providers: [LoginService]
})
export class LoginComponent {
/* .. other properties */
constructor(private _router: Router, private _loginService: LoginService ) {
}
/* .. other methods */
/* .. other methods */
private onLoginSuccessfully(data : any) : void {
/* --> HERE: Set showMenu in AppComponent to true. How? */
this._router.navigate(['Welcome']);
}
}
Or this design is not the best way to solve it?
I recently did something similar and here is how I did it. First, you need to create a NavBarComponent at the root of your app. And in the NavBarComponent you reference (what I call) a GlobalEventsManager which is a service that you inject where you need it.
Here is a look at the GlobalEventsManager:
import { Injectable } from '@angular/core';
import { BehaviorSubject } from "rxjs/BehaviorSubject";
import { Observable } from "rxjs/Observable";
@Injectable()
export class GlobalEventsManager {
private _showNavBar: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
public showNavBarEmitter: Observable<boolean> = this._showNavBar.asObservable();
constructor() {}
showNavBar(ifShow: boolean) {
this._showNavBar.next(ifShow);
}
}
Now you inject the GlobalEventsManger service into your login component (something like this)
import {GlobalEventsManager} from "./../GlobalEventsManager";
@Component({
selector: 'fnd-login',
templateUrl: './fnd/login/components/login.component.html',
providers: [LoginService]
})
export class LoginComponent {
/* .. other properties */
constructor(private _router: Router, private _loginService: LoginService, private globalEventsManager: GlobalEventsManager) {
}
/* .. other methods */
/* .. other methods */
private onLoginSuccessfully(data : any) : void {
/* --> HERE: you tell the global event manger to show the nav bar */
this.globalEventsManger.showNavBar(true);
this._router.navigate(['Welcome']);
}
}
In your NavBarComponent you subscribe to the showNavBar Event Emitter:
import {Component, OnInit} from "@angular/core";
import {GlobalEventsManager} from "../GlobalEventsManager";
@Component({
selector: "navbar",
templateUrl: "app/main/topNavbar/TopNavbar.html"
})
export class TopNavbarComponent {
showNavBar: boolean = false;
constructor(private globalEventsManager: GlobalEventsManager) {
this.globalEventsManager.showNavBarEmitter.subscribe((mode)=>{
this.showNavBar = mode;
});
}
}
use *ngIf="showNavBar" in the template HTML to hide/show the Nav bar.
Your app component then looks something like this:
@Component({
selector: 'app',
template: `<navbar></navbar>
<router-outlet></router-outlet>
`
})
export class AppComponent {
//This doesn't belong here --> public showMenu : boolean;
}
Also the GlobalEventsManager must be registered when you boot the app:
import { GlobalEventsManager } from './GlobalEventsManager';
import { TopNavbarComponent } from './TopNavbarComponent';
@NgModule({
bootstrap: [App],
declarations: [
App,
TopNavbarComponent
],
imports: [
BrowserModule
],
providers: [GlobalEventsManager]
})
export class AppModule {
}
That should do it.
UPDATE: I have updated this answer to reflect the more accepted way of using events outside of a component, ie in a service; which entails using BehaviorSubject/Observable instead of EventEmitter
Actually there is a totally different approach that does not use any of event emitters / listeners. I have nothing against the events and I use both approaches (the below one and @brando's one) according to the particular project needs / complexity.
The method: we have 2 application modules (areas): public (that has no navbar) and protected (the one that has one).
Public module contains all public routes:
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';
import { LoginComponent } from './login/login.component';
import { RegistrationComponent } from './registration/registration.component';
import { ResetPasswordComponent } from './reset-password/reset-password.component';
@NgModule({
imports: [
CommonModule,
RouterModule.forChild([
{ path: 'login', component: LoginComponent },
{ path: 'registration', component: RegistrationComponent },
{ path: 'reset-password', component: ResetPasswordComponent }
])
],
declarations: [
LoginComponent,
RegistrationComponent,
ResetPasswordComponent
]
})
export class PublicModule { }
This is what you already should have, there is nothing unusual here.
Then we have a protected area
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';
import { NavbarWrapperComponent } from './navbar-wrapper/navbar-wrapper.component';
import { UserProfileComponent } from './user-profile/user-profile.component';
@NgModule({
imports: [
CommonModule,
RouterModule.forChild([
{ path: '', redirectTo: '/login', pathMatch: 'full' }, // point 1
{
path: '', // point 2
component: NavbarWrapperComponent, // point 3
children: [
{ path: 'profile', component: UserProfileComponent }
]
}
])
],
declarations: [
NavbarWrapperComponent,
UserProfileComponent
]
})
export class ProtectedModule { }
and here the magic starts.
First of all, pay attention to the point 1:
{ path: '', redirectTo: '/login', pathMatch: 'full' },
We need this right here. If we put it to the AppModule it will be ignored. There is nothing crucial here, it might be even more logical to have this redirect in the protected module.
Point 2 allows us to proxy all the children routes into the NavbarWrapperComponent
(point 3) which takes care of rendering of all our children. Here is a navbar component's template:
<nav class="navbar navbar-toggleable-md navbar-light bg-faded">
<!-- nav content here -->
</nav>
<router-outlet></router-outlet>
This <router-outlet>
will handle all the children routes.
Possible problems you might face and their solution:
- you might want to put the redirect to the
AppModule
- just change the path in the point 2 to be some real name e.g. protected
. This will prefix all of your protected urls with this value, which you might not want. You have 2 options to choose.
- you might want to have more than just one module inside of the protected area - just use lazy routing
- you might want to hide / show the navigation, pass parameters etc. - just combine it with the events solution.
This way might seem not that flexible, however it really works and covers nearly all the cases you might need. It does not have the events complexity and fully utilises the Router
features. The killer thing here - it is dumb simple and very easy to understand and maintain.
best modern way in angular4, with new router, with children routes, just needed use UrlSerializer-class to remove parenthesis, https://angular.io/docs/ts/latest/api/router/index/UrlSerializer-class.html,
anyone did uses it?
export const ROUTES: Routes = [
{ path: 'login', component: LoginComponent },
{
path : '',
children: [
{
path: '', component: DefaultLayoutComponent,
children: [
{ path: '', component: HomeComponent, canActivate: [AuthGuard] },
{ path: 'users', component: UsersListComponent, canActivate: [AuthGuard] },
{ path: 'users-add', component: UsersAddComponent, canActivate: [AuthGuard] },
{ path: 'users-view/:id', component: UsersViewComponent, canActivate: [AuthGuard] },
{ path: 'users-edit/:id', component: UsersEditComponent, canActivate: [AuthGuard] },
]
}
]
},
// otherwise redirect to home
{ path: '**', redirectTo: '' }
]