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 the async 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: ` `, styles: [] }) consign course LoginLayoutComponent {}

                          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

johnsonsartur.blogspot.com

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

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel