
In Part 10 – A, we developed the login API in node.js, in this part, We will develop the client-side in Angular. We will develop the Login/Logout functionality and learn how to use Angular JWT and browser local storage. The only authenticated user would be able to access the admin section after this part.
Introduction
In Part 10 – A, we developed the login API in node.js, used bcrypt to compare the hashed password and jsonwebtoken to create the public key that is embeded in successful login API response as a token. In this part, we will develop the LoginComponent and login & logout functionality.
Let’s Start
It is strongly recommended to read all previous parts before moving to this one, I can’t promise you would understand all the steps I am going to list down without knowledge of previous parts.
- Make sure Part 10 – A is working fine without any error.
- First, let’s create the
logininterface, in the mazharncoweb folder, right-click on src -> app -> model and select an option, New File, enter the file name login.ts. Add the following code in it:
- This is a very simple interface with two fields
UserNameandPasswordthat will act as a model class for us. - Since we are using JSON Web Token (JWT) to avoid CSRF, we also need to angular-jwt package for some useful methods e.g. to check if a token is still valid or expired. Right click on a mazharncoweb folder and select the option, Open in Terminal, in TERMINAL tab, enter the command:
npm install @auth0/angular-jwt@^1.1.0 --save, I am using the specific version because the latest version has some conflicts and errors. - Once the angular-jwt is successfully installed, we need to create the
AuthService, though we will only make one POST call in this service I just want to keep it sperate fromDataServicebecause we also need to add some specific methods related to authentication. In the same TERMINAL tab, enter the command:ng g s service/auth/auth --m app.module.ts - Go to src -> app -> service -> auth folder, edit the auth.service.ts file and add the following content in it:
- AuthService has five functions, let’s briefly understand each:
- authenticateUser(): This is the main Login API function that takes the Login API URL and user object (
UserNameandPassword), calls the API and get the response back. This response includes the successful text message andtokenas discussed in the last part. - storeUserData(): This method stores the token and expiration time in
localStorage. ThelocalStorageis an HTML 5 feature that stores the values as a key-value pair in the browser, e.g.access_tokenis the key that has thetokenvalue returns from Login API. The built-inlocalStorage.setItem()method is used to set the value to a key. - getToken(): This method returns the
tokenby key. The built-inlocalStorage.getItem()method is used to get the key’s value. - isLoggedIn(): We are using the angular-jwt package we installed earlier to check if the
tokenis still valid by checking its expiry time. - logout(): This method will clear everything from
localStorageand redirect to the home page, since for each admin page, the guard will callisLoggedIn()method, after clearinglocalStorage, this method will returnfalseand would not let the user enter in the admin section. You would see it in action when we will develop the guard.
- authenticateUser(): This is the main Login API function that takes the Login API URL and user object (
- Next, let’s create the
LoginComponent, you can see the Login button at a top right corner from the beginning of series. Finally, this would get activated and open the login window in a modal popup. If you are not already in TERMINAL tab pointing to a mazharncoweb folder, right click on the mazharncoweb folder and select option, Open in Terminal, enter the command:ng g c client/login - Edit the src -> app -> client -> login -> login.component.css and add the following CSS in it:
.header{
background: #3B5C89;
border-bottom: 5px solid #28527F;
height: 50px;
padding-left: 5px;
}
.header_title{
vertical-align:baseline;
padding-top: 10px;
padding-bottom: 0px;
padding-left: 5px;
font-size: 16pt;
font-family: Arial, Helvetica, sans-serif;
color: rgba(255, 255, 255, 0.85);
}
mat-card{
margin: 0px;
padding: 0px;
}
mat-card-content{
margin: 0px;
padding: 0px;
width: 400px;
height: 200px;
}
.mat-dialog-container {
padding: 0 !important;
margin: 0 !important;
}
.frm-ctrl {
width: 100%;
}
.card_cntnt
{
padding: 20px;
}
- Edit the src -> app -> client -> login -> login.component.html and add the following code in it:
<mat-card>
<mat-card-header class="header">
<mat-card-title class="header_title"><i class="material-icons"></i> Admin Login</mat-card-title>
</mat-card-header>
<mat-card-content class="card_cntnt" >
<div>
<form novalidate (ngSubmit)="onSubmitLgn(lgnFrm)" [formGroup]="lgnFrm">
<div>
<mat-form-field class="frm-ctrl">
<input matInput placeholder="Email Address" formControlName="EmailAddress">
<mat-error *ngIf="lgnFrm.controls['EmailAddress'].errors?.required">
User Name is required!
</mat-error>
</mat-form-field>
</div>
<div>
<mat-form-field class="frm-ctrl">
<input matInput type="password" placeholder="Password" formControlName="Password">
<mat-error *ngIf="lgnFrm.controls['Password'].errors?.required">
Password is required!
</mat-error>
</mat-form-field>
</div>
<div>
<button color="primary" type="submit" [disabled]="lgnFrm.invalid" mat-raised-button>Login</button>
<button color="warn" type="button" mat-raised-button (click)="dialogRef.close()">Cancel</button>
</div>
</form>
</div>
</mat-card-content>
</mat-card>
- This is pretty straightforward HTML for
LoginComponent, we have Model-Driven formlgnFrmthat only has two controls,EmailAddressandPassword. We also specified the nice Angular Materialmat-errorcontrol for required filed validation. TheonSubmitLgn()is a function that will trigger when we will hit the Login button to submit the form. Since this component would open in a modal pop-up, the cancel button will hide the modal pop-up control by callingdialogRef.close()method. - Edit the src -> app -> client -> login -> login.component.ts and add the following code in it:
- Let’s understand the code:
- On the top, we have the Login API URL and
FormGroupobject for Model-Driven form. - constructor(): In the
constructor,- We have
formBuilderservice parameter for creating Model-Driven form, AuthServicethat we created earlier to useauthenticateUser()and other methods,Utilto open the snackbar component at the bottom for success or failure login API response,MatDialogRefto open theLoginComponentin a modal pop-up andRouterto redirect to the Admin section.
- We have
- ngOnInit(): We are defining the
lgnFrmwith two controlsEmailAddressandPasswordalong with validations. Thanks to Angular Model-Driven form built-in validation for length and email address. - onSubmitLgn(): This is login form submit function that takes the user’s entered an email address and password, calls the
authenticateUser()function fromAuthServiceand check the response. If user credentials are authenticated, we are calling astoreUserData()method fromAuthServiceto store thetoken,expireIn(token expiration time) and user information. After that, the user is redirected to the admin home page.
- On the top, we have the Login API URL and
- The
LoginComponentwill be open in modal pop-up control so we will explicitly add it inAppModule‘sentryComponents, edit the src -> app -> app.module.ts and replace its content with the following:
- Now we have developed the LoginComponent, its time to enable the login button on the top right corner, this button is defined in
AppComponentso edit the src -> app -> app.component.html and replace its content with following:
<div class="main_div">
<mat-grid-list cols="10" rowHeight="2:1">
<div>
<mat-grid-tile>
<div style="padding-left: 5px;">
<a [routerLink]="['home']"> <img src='assets/images/logo1.png' class="mnc-logo" /></a>
</div>
</mat-grid-tile>
<mat-grid-tile [colspan]=8>
<div class="socail_btn_padding">
<a target="_blank" href="https://www.facebook.com/mazhar.mahmood.16"><img src="assets/images/fb_btn1.jpg" class="socail_btn image" /></a>
<a href="skype:plug-shop?mazharmahmood786"><img src="assets/images/skype_btn1.jpg" class="socail_btn image" /></a>
<img src="assets/images/twtr_btn1.jpg" class="socail_btn image" />
<img src="assets/images/lkdin_btn1.jpg" class="socail_btn image" />
</div>
<div style="padding-left: 100px" *ngIf="_authService.isloggedIn();">
<img src="assets/images/admin.png" style="width:150px; height: 48px" class="lgn_btn image" (click)="redirectToAdmin()" />
</div>
</mat-grid-tile>
<mat-grid-tile [colspan]=1>
<div style="text-align: right" *ngIf="!_authService.isloggedIn(); else logout">
<img src="assets/images/login_btn.png" class="lgn_btn image" (click)="openDialog()" />
</div>
<ng-template #logout>
<div>
<img src="assets/images/logout_btn.png" class="lgn_btn image" (click)="_authService.logout()" />
</div>
</ng-template>
</mat-grid-tile>
</div>
<mat-grid-tile [colspan]=10 [rowspan]=3>
<ngx-carousel [inputs]="carouselBanner" [moveToSlide]="1">
<ngx-item NgxCarouselItem>
<div><img src='assets/images/banner_1.jpg' width="100%" /></div>
</ngx-item>
<ngx-item NgxCarouselItem>
<div><img src='assets/images/banner_2.jpg' width="100%" /></div>
</ngx-item>
<button NgxCarouselPrev><</button>
<button NgxCarouselNext>></button>
</ngx-carousel>
</mat-grid-tile>
</mat-grid-list>
<div>
<nav class="navbar navbar-default navbar_shadow">
<div class="container-fluid">
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li><a [routerLink]="['home']">Home</a></li>
<li class="dropdown" *ngFor="let menu of mainMenus" style="cursor: pointer;">
<a [routerLink]="['page', menu.MenuCode]" class="dropdown-toggle" role="button" aria-haspopup="true" aria-expanded="false">{{menu.MenuName}}<span *ngIf="getChildMenu(menu.MenuCode).length>0" class="caret"></span></a>
<ul class="dropdown-menu" *ngIf="getChildMenu(menu.MenuCode).length>0">
<li *ngFor="let chldMenu of getChildMenu(menu.MenuCode)">
<a [routerLink]="['page', chldMenu.MenuCode]">{{chldMenu.MenuName}}</a>
</li>
</ul>
</li>
<li><a [routerLink]="['contact']">Contact Us</a></li>
</ul>
<ul class="nav navbar-nav navbar-right">
<form class="navbar-form navbar-left" novalidate (ngSubmit)="onSubmit(searchFrm)" [formGroup]="searchFrm">
<div class="form-group">
<input type="text" class="form-control" formControlName="TextSearch" placeholder="Search">
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
</ul>
</div>
<!-- /.navbar-collapse -->
</div>
<!-- /.container-fluid -->
</nav>
</div>
<div class="clearfix" style="padding-top:10px; padding-bottom: 10px">
<router-outlet></router-outlet>
</div>
<app-footer></app-footer>
<br>
</div>
- You can go through the login div, we are calling the
AuthService‘sisLoggedIn()function to check if the user is not logged in (by!operator), if Yes, we are showing the login button else we are showing the logout template’s div that has logout button with aclickfunctionlogout()fromAuthService. Also, there would be one more button on top with the title Admin Login to redirect control to the admin home page. So pretty much we have used all methods fromAuthServicenow. - Next, edit the src -> app -> app.component.ts and replace its code with the following:
- Just look for
openDialog()function, we are openingLoginComponentin a modal popup. That’s the only change we need inAppComponentin order to make login button functional. - We need two more images in the asset folder. Save these two images and copy them in src -> assets -> images folder:
- That’s it for Part 10 – B, in the upcoming part(s) we will develop the guard to protect the admin section routes, HTTP interceptor to embed the token to API requests,
ErrorComponentand Search functionality.