Angular 5 Route Takes Me to Page Then Right Back Again
Angular: Hide Navbar Carte from Login page
In this article nosotros will learn two approaches to hibernate the Navbar Menu when displaying the Login page in Angular projects.
Update December 2017: code updated to Angular v5 and Material v5.
Update May 2018: code updated to Angular v6. Stackblitz link besides bachelor at the stop of this article.
Contents
- Example 1: Using *ngIf to "hide" the NavBar
- The app-material module
- Creating the Login component
- Creating the Home component
- Creating the AuthService
- Configuring the Router and the AuthGuard
- Updating the AppComponent
- Creating the Navigation Bar
- Example 2: Using different layouts and routing config
- Creating the HomeLayout page
- Creating the LoginLayout page
- Source code + Stackblitz
For both examples in this tutorial we will use Angular Material as our UI library. I've covered how to setup an Angular project with Athwart Material in this post.
In this get-go example we will have only one page layout and we will verify if the user is logged in and utilize *ngIf to verify if the awarding should display the navigation bar or not. This is the about common case nosotros find when searching for how to hibernate the navbar when displaying the login page.
And then let's start creating our first example with Angular CLI:
ng new angular-login-hide-navbar-ngif --routing --style =scss
For this example we will need to create some components, a module, a service, a route baby-sit and a model interface:
ng yard m app-material ng g s auth/auth --module =app.module ng g k auth/auth --module =app.module ng m i auth/user ng g c header -is ng g c home -is -information technology ng yard c login
With the commands above all components and services will be created and the declarations
and providers
metadata of app.module.ts
will as well be updated.
Don't forget to setup the CSS framework or UI library for your project too!
The paradigm below shows the projection src
binder for this first example and also for our second example. Both examples use basically the same components, notwithstanding, the second example has two extra components.
The application shall brandish the navigation bar only when the user is logged in as demonstrated by the following screenshot:
The toolbar from the screenshot above is displaying both Login and Logout buttons. But we'll become in that location to fix this later on.
If the user is not logged in, the application shall display the login page without the navigation bar every bit demonstrated below:
Ok, nosotros have our requirenments, let'south beginning coding!
The app-fabric module
To develop this simple application, nosotros will need some UI components. Since nosotros are using Athwart Material, nosotros volition need the following Fabric modules to be imported by our application:
import { NgModule } from ' @angular/core ' ; import { MatToolbarModule } from ' @angular/material/toolbar ' ; import { MatCardModule } from ' @angular/material/card ' ; import { MatButtonModule } from ' @angular/cloth/button ' ; import { MatInputModule } from ' @angular/material/input ' ; import { MatFormFieldModule } from ' @angular/material/form-field ' ; @ NgModule ({ exports : [ MatToolbarModule , MatCardModule , MatInputModule , MatFormFieldModule , MatButtonModule ] }) consign class AppMaterialModule {}
We cannot forget to update our app.module.ts
and import AppMaterialModule
.
In your application, import the
AppMaterialModule
in all modules that your components belongs to. Since this is a very pocket-size project, we only accept one module with components which is the app.module.
Since we imported MatInputModule
, this means nosotros will work with forms. So nosotros also need to import ReactiveFormsModule
(or FormsModule
if y'all adopt to work with template driven forms):
import { BrowserAnimationsModule } from ' @angular/platform-browser/animations ' ; import { ReactiveFormsModule } from ' @athwart/forms ' ; import { AppMaterialModule } from ' ./app-material/app-material.module ' ; @ NgModule ({ // ... imports : [ // ... ReactiveFormsModule , BrowserAnimationsModule , AppMaterialModule ], // ... }) consign class AppModule { }
Creating the Login component
The code for the login.component.ts
is presented below:
import { Component , OnInit } from ' @angular/core ' ; import { FormGroup , FormBuilder , Validators } from ' @angular/forms ' ; import { AuthService } from ' ./../auth/auth.service ' ; @ Component ({ selector : ' app-login ' , templateUrl : ' ./login.component.html ' , styleUrls : [ ' ./login.component.scss ' ] }) consign form LoginComponent implements OnInit { form : FormGroup ; // {1} private formSubmitAttempt : boolean ; // {ii} constructor ( private fb : FormBuilder , // {3} private authService : AuthService // {iv} ) {} ngOnInit () { this . grade = this . fb . group ({ // {five} userName : [ '' , Validators . required ], password : [ '' , Validators . required ] }); } isFieldInvalid ( field : string ) { // {6} render ( ( ! this . course . get ( field ). valid && this . form . go ( field ). touched ) || ( this . class . get ( field ). untouched && this . formSubmitAttempt ) ); } onSubmit () { if ( this . form . valid ) { this . authService . login ( this . form . value ); // {7} } this . formSubmitAttempt = true ; // {8} } }
Our login screen is going to be a uncomplicated form with two fields: user name and password and they are both required ({5}
). And then first we need to declare a course
variable ({1}
). I like to use the FormBuilder
instead of instantiating each FormGroup
and FormControl
, then in this case we also need to inject information technology in our component's contructor ({3}
).
Since we are in the constructor, when the user clicks on the login push and the form is valid, we will submit its values ({7}
) to the AuthService
that will be responsible for the login logic. So we also need to inject it in the component'southward contructor ({4}
). This service was declared as provider in the app.module
when we used the ng grand southward auth/auth --module
control.
To display some validation fault messages in our form, we'll verify if the field is invalid or has been touched (received focus) ({vi}
). We'll too exist using the submit endeavour flag approach ({2}
and {8}
).
To learn more well-nigh the submit endeavor flag arroyo yous can read this tutorial.
Our class will apply some custom CSS styles as well:
mat-bill of fare { max-width : 400px ; margin : 2em auto ; text-marshal : center ; } .signin-content { padding : 60px 1rem ; } .full-width-input { width : 100% ; }
And at present that we accept the Angular grade in identify, let's take a look at the login template built with Angular Material:
<div class= "signin-content" > <mat-card> <mat-card-content> <class [formGroup]= "form" (ngSubmit)= "onSubmit()" > <p>Delight login to continue</p> <mat-form-field class= "full-width-input" > <input matInput placeholder= "User" formControlName= "userName" required > <mat-mistake *ngIf= "isFieldInvalid('userName')" > Delight inform your user name </mat-error> </mat-form-field> <mat-form-field class= "full-width-input" > <input matInput blazon= "password" placeholder= "Password" formControlName= "countersign" required > <mat-error *ngIf= "isFieldInvalid('userName')" > Please inform your password </mat-error> </mat-class-field> <button mat-raised-button color= "primary" >Login</push> </form> </mat-card-content> </mat-carte du jour> </div>
Our login widget is a Material menu with maximum width of 400 pixels, with two required form fields and a login button.
Creating the Dwelling house component
Our Dwelling house component is going to be very simple. Simply a message confirming the user is logged in:
import { Component } from ' @angular/core ' ; @ Component ({ selector : ' app-habitation ' , template : ' <p>Yay! You are logged in!</p> ' , styles : [] }) export class HomeComponent {}
Creating the AuthService
For the purpose of this case, we volition not integrate the service with any backend API.
import { Injectable } from ' @angular/core ' ; import { Router } from ' @angular/router ' ; import { BehaviorSubject } from ' rxjs ' ; import { User } from ' ./user ' ; @ Injectable () consign grade AuthService { private loggedIn = new BehaviorSubject < boolean > ( faux ); // {ane} get isLoggedIn () { return this . loggedIn . asObservable (); // {two} } constructor ( private router : Router ) {} login ( user : User ){ if ( user . userName !== '' && user . password !== '' ) { // {3} this . loggedIn . next ( true ); this . router . navigate ([ ' / ' ]); } } logout () { // {iv} this . loggedIn . next ( false ); this . router . navigate ([ ' /login ' ]); } }
To control if the user is logged in or not, nosotros will use a BehaviorSubject
({1}
). We will as well create a getter to betrayal but the get method publicly ({2}
) every bit besides expose the Subject as an Observable
. The BehaviorSubject
keeps the latest value buried (in our case when the service is created the initial value is going to be false
). So when an Observer subscribes to the isLoggedIn()
, the buried valued is going to be emitted right away.
When the user clicks on the login button from the course, the login
method is going to exist chosen receiving the class values. Our validation is very uncomplicated: we are only checking if the values are non empty (of course the all-time beliefs here is to submit the values to a server and maybe also utilize JWT). If we received a userName and a countersign ({3}
), then nosotros cosign the user. This means we need to emit that the user is now logged in and also redirect the routing to the HomeComponent
.
And in instance the user logs out of the application ({iii}
4, we volition emit that the user is no longer logged in and also redirect to the login folio.
The User
interface matches the FormControl names from our LoginComponent grade:
export interface User { userName : string ; password : cord ; }
Configuring the Router and the AuthGuard
These volition exist the routes nosotros will use in this example:
const routes : Routes = [ { path : '' , component : HomeComponent , canActivate : [ AuthGuard ] }, { path : ' login ' , component : LoginComponent } ];
Now let'south have a await the auth/auth.guard
:
import { Injectable } from ' @athwart/core ' ; import { ActivatedRouteSnapshot , CanActivate , Router , RouterStateSnapshot } from ' @angular/router ' ; import { Appreciable } from ' rxjs ' ; import { map , take } from ' rxjs/operators ' ; import { AuthService } from ' ./auth.service ' ; @ Injectable () export class AuthGuard implements CanActivate { constructor ( private authService : AuthService , private router : Router ) {} canActivate ( side by side : ActivatedRouteSnapshot , state : RouterStateSnapshot ): Observable < boolean > { return this . authService . isLoggedIn // {i} . pipe ( take ( i ), // {ii} map (( isLoggedIn : boolean ) => { // {3} if ( ! isLoggedIn ){ this . router . navigate ([ ' /login ' ]); // {four} render false ; } return true ; }); ) } }
Outset nosotros are going to recall the isLoggedIn
({ane}
) getter from the AuthService
, which is an Observable. Since we are but interested in checking the value from the Observalbe a single time (if the user is logged in or not), nosotros will employ the take
operator ({2}
). We volition verify the value emitted by the BehaviorSubject
({3}
) and if not logged in nosotros will navigate to the login screen ({4}
) and return false. The AuthGuard volition return true in example the user is logged in, significant the user can access the route (path: '') which renders the HomeComponent.
Updating the AppComponent
For this instance, we AppComponent will exist our main component:
import { Component } from ' @angular/core ' ; @ Component ({ selector : ' app-root ' , template : ` <app-header></app-header> <router-outlet></router-outlet> ` , styles : [] }) export grade AppComponent {}
It volition display the app-header
which is our navigation bar and the component co-ordinate to the routing config.
Creating the Navigation Bar
Let's start creating the navigation bar with the simplest template:
<mat-toolbar color= "primary" > <span> <img src= "assets/img/angular_whiteTransparent.svg" class= "angular-logo" > Angular NavBar + Login Example #01 </span> <bridge course= "fill-remaining-space" ></span> <button mat-button >Bill of fare Option 01</push> <button mat-push button >Menu Selection 02</button> <push button mat-button routerLink= "login" >Login</push> <push mat-push button (click)= "onLogout()" >Logout</button> </mat-toolbar>
Our navbar has a championship, two menu options (simply to look prettier) and a Login + Logout buttons.
If try to execute the application now (ng serve
) and access the /login
route we volition have the following event:
We still need to hide the navigation bar.
So let's implement the required logic in the header.component
:
import { Observable } from ' rxjs/Observable ' ; import { AuthService } from ' ./../auth/auth.service ' ; import { Component , OnInit } from ' @angular/cadre ' ; @ Component ({ selector : ' app-header ' , templateUrl : ' ./header.component.html ' , styles : [ `.angular-logo { margin: 0 4px 3px 0; height: 35px; vertical-align: middle; } .make full-remaining-infinite { flex: i 1 auto; } ` ] }) export form HeaderComponent implements OnInit { isLoggedIn$ : Observable < boolean > ; // {1} constructor ( individual authService : AuthService ) { } ngOnInit () { this . isLoggedIn$ = this . authService . isLoggedIn ; // {ii} } onLogout (){ this . authService . logout (); // {3} } }
Starting time, we will declare an Observable ({1}
) to receive the value emitted from the AuthService ({2}
). We are using $
at the end of the Observable identifier. This helps us while reading the code to identify which variables are Observables or not.
Nosotros will not subscribe to the Appreciable in the HeaderComponent TypeScript lawmaking. We will simply store the Observable reference (
{2}
). We will utilise theasync
pipage in the HTML template to subscribe/unsubscribe to/from the Observable automatically. This is considered a all-time practice by the Angular/RxJS community.
And at last, when the user clicks on the logout we will call the logout method ({3}
) from the AuthService.
So at present let's back to the HTML template and use *ngIf to brandish the navbar or not:
<mat-toolbar colour= "principal" *ngIf= "isLoggedIn$ | async" >
The code above is enough to display or non the navbar.
The *ngIf does not prove or hibernate the DOM element, it creates (show) or destroys it. Nosotros could also utilize the hidden HTML property, merely in this case, if the user does not have admission to the application, it is better to not create the DOM Element for security purposes instead of merely hiding information technology.
Just for the purpose of this example, suppose we also want to verify if the user is logged in and display the Logout button:
<button mat-push button (click)= "onLogout()" *ngIf= "isLoggedIn$ | async" >Logout</push button>
Note that the expression is the same as used in the navbar. We are subscribing to the isLoggedIn$
twice. We tin write a ameliorate code and only subscribe one time by using the as
alias introduced in Angular v4.0 enhanced *ngIf and *ngFor:
<mat-toolbar color= "primary" *ngIf= "isLoggedIn$ | async as isLoggedIn" > <!-- more than HTML template code --> <button mat-button (click)= "onLogout()" *ngIf= "isLoggedIn" >Logout</button> </mat-toolbar>
The code higher up ways we are subscribing to the isLoggedIn$
and storing its value in the isLoggedIn
local template variable. Then we can use this variable in other parts of the template such as the Logout button!
Now we have the expected behavior for our example:
Case 2: Using different layouts and routing config
For the second example the lawmaking is basically the same as the example 1, but with a few changes. Instead of using *ngIf to hibernate the navbar, we are going to use different folio layouts with child routes. All the control will exist in the routing config.
The outset change is regarding the home.component
. We practice not demand the conditional *ngIf="isLoggedIn$ | async as isLoggedIn"
to "hide" the navbar.
Nosotros likewise demand to create ii new components:
ng g c layouts/home-layout -is -it ng g c layouts/login-layout -is -it
Creating the HomeLayout folio
For the HomeLayout, we want to display the navbar, then our component will take the following code:
import { Component } from ' @angular/cadre ' ; @ Component ({ selector : ' app-home-layout ' , template : ` <app-header></app-header> <router-outlet></router-outlet> ` , styles : [] }) export class HomeLayoutComponent {}
Creating the LoginLayout page
For the LoginLayout, we practice not desire to display the navbar, so our component will accept the post-obit code:
```jsimport { Component, OnInit } from '@athwart/core';
@Component({ selector: 'app-login-layout', template: `
The component only has the `router-outlet` to display the LoginComponent. ### Configuring the routes and kid routes Our routing config will exist different from the get-go instance: ```js const routes: Routes = [ { path: '', // {one} component: HomeLayoutComponent, canActivate: [AuthGuard], // {2} children: [ { path: '', component: HomeComponent // {3} } ] }, { path: '', component: LoginLayoutComponent, // {4} children: [ { path: 'login', component: LoginComponent // {5} } ] } ];
When we endeavor to admission "htpp://localhost:4200/"
, Angular volition evaluate the route paths. The start part of the path for the first road configured is ""
({one}
), so let's continue evaluating the second part which is besides ""
({2}
), so we have a friction match! But the guard ({3}
) still need to verify if the user tin access the HomeLayoutComponent
(if user is logged in). If the user is logged in, so the access is granted and the user will see the HomeLayoutComponent
(the navbar forth with the HomeComponent
being rendered in the router-outlet
), otherwise information technology is redirected to "htpp://localhost:4200/login"
.
So suppose we are trying to access "htpp://localhost:4200/login"
. Angular will evaluate the route path again. The first part of the path for the first road configured is ""
({1}
), so permit'south continue evaluating the second part which is "login"
, so we practise non accept a friction match with ({3}
). We demand to evaluate the next route configured. Let's go again! The first office of the path for the get-go road configured is ""
({4}
this fourth dimension), so let'due south go on evaluating the second part which is "login"
and nosotros have a lucifer ({5}
). In this case the LoginLayoutComponent
is displayed. Since at that place is only a router-outlet
in the HTML template, only the LoginComponent
will be displayed.
And we have the aforementioned output as the example 1!
This approach where we control the layouts using the router configuration can be used in applications that accept unlike folio layouts such as a patently page, or a page with a header navbar, or a folio with a sidebar (very used in admin layouts).
Source lawmaking + Stackblitz
Source code available on GitHub
Stackblitz online demo - 1st instance
Stackblitz online demo - 2d instance
References:
- RxJS Subject classes
- Athwart Routing & Navigation docs
Happy coding!
This site uses cookies. By continuing to scan the site, yous are like-minded to our use of cookies.OKMore info
Source: https://loiane.com/2017/08/angular-hide-navbar-login-page/
0 Response to "Angular 5 Route Takes Me to Page Then Right Back Again"
Post a Comment