Building the frontend Food order system with Angularjs (building angular authentication).Part8
Contribution Repository
https://github.com/angular/angular
Disclaimer
Before understanding this tutorial, you must have a little knowledge of SASS and Bootstrap 4
In our last tutorial we where able to signup a user for our application.
In this series, we are going to work on the login feature of the application and also learn how to the local storage to save data that needs to be accessed he in the application.
Project structure.
- node_modules/
- src/
- app
- app-navbar/
- item/
- items-feed/
- signup/
- login/
- app-routing.module
- app.component.html
- app.component.sass
- app.component.ts
- app.module.ts
- auth.service.ts
- item.service
- cache.service
- asset/
- environments/
- item.ts
- user.ts
- auth.ts
- app
- .angular-cli.json
- .editorconfig
+.....
Rating
This tutorial is rated Intermediate.
Requirement.
- Typescript version 2.4.2
- Node version version 8.9.4
- Npm version 5.6.0
- Bootstrap 4.0
- Visual Studio Code IDE
- Voltront front client Repository
What would i learn? / Table of content.
- Recap of the previous tutorial.
- Building the login component.
- Creating the class interface for authentication.
- Building the login markup and styles.
- Adding a login method to the Auth service.
- Building the logout method.
- Building a cache service.
- Logging a user in the application and validation.
- Adding the logout method to the navbar component
Recap of the previous tutorial.
We were able to generate a signup component no our application, the data for signing up a user was gotten from the form input using two way binding and the data was sent as a post request to the auth service we created. The response gotten from the server was displayed using a message
variable by interpolation on the signup.component.html
Schematic diagram of relationships for the present tutorial
Building the login component.
Now that we are done with signing up a user for our application, lets login the said user into our application.
Login involve a series of steps;
- Validation of the user input;
- Two way data binding of the input into the component.
- Sending a post request with the user data and listening for response.
- If response was successful, save the
JWT
token and the user object using a cache service on the browsers' local storage.
Lets begin by generating our login component
ng generate component login
Creating the class interface for authentication
Next up, we also need to create an interface for our login data , in the src
directory, create a file called auth.ts
add the code for the type on the interface.
export interface Auth {
email: string;
password:string;
}
Both properties are expected to have a type of string
Building the login markup and styles
We are going to be using the bootstrap form template for this feature, open up the login.component.html
file in the login folder and add the markup below.
<div class="login__container">
<div class="container">
<div>
<form class="form-signin" (ngSubmit)="login()" #loginForm="ngForm">
<h1 class="h3 mb-3 font-weight-normal">Please sign in</h1>
<label for="inputEmail" class="sr-only">Email address</label>
<input type="email" name="email" id="inputEmail" class="form-control" placeholder="Email address" autofocus #email="ngModel" required minlength="6" #spy>
</div>
<label for="inputPassword" class="sr-only">Password</label>
<input type="password" id="inputPassword" class="form-control" placeholder="Password" required>
<div class="checkbox mb-3">
<label>
<input type="checkbox" value="remember-me"> Remember me
</label>
</div>
<button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
</form>
</div>
</div>
</div>
All we simply did was to create two input with different labels and a check box to remember the user.
lets add the styles to the login.component.sass
, this just basic styling, where we make the body 100% and use flex box for responsiveness.
html
height: 100%;
body
height: 100%;
display: -ms-flexbox;
display: -webkit-box;
display: flex;
-ms-flex-align: center;
-ms-flex-pack: center;
-webkit-box-align: center;
align-items: center;
-webkit-box-pack: center;
justify-content: center;
padding-top: 40px;
padding-bottom: 40px;
background-color: #f5f5f5;
.form-signin
width: 100%;
max-width: 330px;
padding: 15px;
margin: 0 auto;
.form-signin .checkbox
font-weight: 400;
.form-signin .form-control
position: relative;
box-sizing: border-box;
height: auto;
padding: 10px;
font-size: 16px;
.form-signin .form-control:focus
z-index: 2;
.form-signin input[type="email"]
margin-bottom: -1px;
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
.form-signin input[type="password"]
margin-bottom: 10px;
border-top-left-radius: 0;
border-top-right-radius: 0;
.login__container
margin-top: 80px;
The result on the DOM beow.
Building the login service.
In our last tutorial, we generated and built the auth service for our application. In this series we are going to work on the method responsible for logging in and logging out a user
Open the auth.service.ts
file and import the newly created auth interface.
import { Auth } from '../auth';
Lets add some variable for the login data, login endpoint, and a variable LoggedIn which is set to a Boolean false
loginEndpoint = 'http://localhost:3000/api/users/authenticate';
loginData: any;
private loggedIn = false;
Lets create the method to send the post request to the server with the loginData in its request.
We would need to toggle the variable loggedIn
to true
then send a post request with the loginData to the login endpoint.
Note; data must be JSON stringed before sending to the endpoint.
login (loginData ): Observable<{} | Auth>{
this.loggedIn = true;
return this.http.post<Auth>(this.loginEndpoint,JSON.stringify(loginData), this.httpOptions)
}
We would have to monitor a stream of data and announce to the application that the data has been mutated. In this case, we need to announce a logged in user to the application.
Add two variables, one as a new Subject and the other as an observable.
private authAnnouncerSource = new Subject<object|null>();
authAnnouncer$ = this.authAnnouncerSource.asObservable()
Right now, we are watching the authAnnouncer which is an observable. lets create a method that announces that there is a new data in the observable.
announceLogin(user) {
return this.authAnnouncerSource.next(user);
}
The method next
is used to access the next object in the observable
Building the logout method
Logging out is simple a check to know if there is a user and a JWT
token in the database.
If there is a Jwt token and a user object, to logout we need to delete the items from local storage, so we need a method to remove the jwt and the user object. Lastly we need to pass the observable a null value to let it know that the variable has changed.
logOut () {
localStorage.removeItem('jwtToken');
localStorage.removeItem('user');
this.announceLogin(null)
}
Building a cache service
We are going to need a cache service which can check, get and set a specific item in the local storage.
ng g service cache
Open the new created service and add the following methods;
Check local storage
hasItem(user:string) {
return typeof localStorage.getItem(user);
}
Get item from local storage
getItem (user:string) {
return localStorage.getItem(user);
}
Set item in local storage.
setItem(user:object) {
return localStorage.setItem('user', JSON.stringify(user));
}
Here we have the different methods to set, check and get the user from the local storage.
Complete cache.service.ts
import { Injectable } from '@angular/core';
@Injectable()
export class CacheService {
constructor() { }
hasItem(user:string) {
return typeof localStorage.getItem(user);
}
getItem (user:string) {
return localStorage.getItem(user);
}
setItem(user:object) {
return localStorage.setItem('user', JSON.stringify(user));
}
}
Logging a user in the application and validation.
We are going to need three import to this component, we need the auth service to send the post request that includes the login data, the interface to type hint and finally a router to navigate after the authentication is successful.
import { AuthService } from '../auth.service';
import { Router } from "@angular/router";
import {Auth} from '../../auth';
Lets create an object and a variable to hold the authentication data and the message from the server respectively. Finally add another variable data to hold the response data from the server.
data: any;
loginData: Auth = {
"email": "",
"password": ""
}
message : String = '';
Note; All the values are set to an empty string since it would be gotten from the form through two way data binding.
Next up instantiate the the Router and the authService in the constructor method.
constructor(private AuthService: AuthService, private router: Router) {}
Login method
The login method uses the login method on the auth service and pass the authData to the method and subscribe for a response from the server.
In a call back function, the res.data.token
and res.data.user
is save to local storage. Lastly, a new logged in user is announced using the observable in the authService.
login(): void {
this.AuthService.login(this.loginData)
.subscribe((res:any):any => {
localStorage.setItem('jwtToken', res.data.token);
localStorage.setItem('user',JSON.stringify(res.data.user));
this.AuthService.announceLogin(res.data.user)
this.router.navigate(['items']);
}, err => {
this.message = err.error.errors[0].detail;
});
}
Now that our login method is ready, let work on the two way data binding to get the data from the input into the login.component.ts
.
Open the login.component.html and update the form with the [(ngModel)]
and [ngModelOptions]
on the input tags
<input type="email" name="email" id="inputEmail" class="form-control" placeholder="Email address" [(ngModel)]="loginData.email" [ngModelOptions]="{standalone: true}"
<input type="password" id="inputPassword" class="form-control" [(ngModel)]="loginData.password" [ngModelOptions]="{standalone: true}" placeholder="Password" required>
The above ngModel
is used for two way data binding to bind the input to the components' authData object.
Validation on the email input
Just below the email input we need to display some validation messages,
we only display when the email is invalid, dirty or untouched. To understand more on form validation, i advice you read the official documentation on form validation
<div *ngIf="email.invalid && (email.dirty || email.touched)" class="alert alert-danger">
<div *ngIf="email.errors.required">
Email is required.
</div>
<div *ngIf="email.errors.minlength">
email must be at least 6 characters long.
</div>
</div>
Displaying flash message from the server.
We add a snippet of code on the login.component.html
and use the directive *ngIf to toggle the display of error or success messages.
Note; the message variable is displayed using interpolation
<div *ngIf="message !==''" class="alert alert-danger alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert"aria-label="Close"><span aria-hidden="true">×</span></button>
{{message}}
</div>
Lastly we add the handler to submit our form on the click of the login button
(ngSubmit)="login()"
complete login.component.html.
<div class="login__container">
<div class="container">
<div [hidden]="submitted">
<form class="form-signin" (ngSubmit)="login()" #loginForm="ngForm">
<div *ngIf="message !==''" class="alert alert-danger alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert"aria-label="Close"><span aria-hidden="true">×</span></button>
{{message}}
</div>
<h1 class="h3 mb-3 font-weight-normal">Please sign in</h1>
<label for="inputEmail" class="sr-only">Email address</label>
<input type="email" name="email" id="inputEmail" class="form-control" placeholder="Email address" [(ngModel)]="loginData.email" [ngModelOptions]="{standalone: true}" #email="ngModel" required minlength="6" #spy>
<div *ngIf="email.invalid && (email.dirty || email.touched)" class="alert alert-danger">
<div *ngIf="email.errors.required">
Email is required.
</div>
<div *ngIf="email.errors.minlength">
email must be at least 6 characters long.
</div>
</div>
<label for="inputPassword" class="sr-only">Password</label>
<input type="password" id="inputPassword" class="form-control" [(ngModel)]="loginData.password" [ngModelOptions]="{standalone: true}" placeholder="Password" required>
<div class="checkbox mb-3">
<label>
<input type="checkbox" value="remember-me"> Remember me
</label>
</div>
<button class="btn btn-lg btn-primary btn-block" [disabled]="!loginForm.form.valid" type="submit">Sign in</button>
</form>
</div>
</div>
</div>
Adding the logout method to the navbar component
Open up the navbar.component.ts
and import the authservice and the router to navigate to the login route after the logout is triggered.
import {Router} from '@angular/router';
import { AuthService } from '../auth.service';
create a variable called authUser to hold the authenticated user and set it to null
private authUser:object = null;
instantiate both import in the constructor method and use the authservices' authAnnouncer method to get the present user and set it to the authUser variable
constructor(
private router: Router,
private authService: AuthService,
) {
authService.authAnnouncer$.subscribe(
user => {
this.authUser = user;
}
)
}
Lastly we create the logout method which uses the auth service. When triggered we navigate to the login route.
logout () {
this.authService.logOut();
this.router.navigate(['login']);
}
complete navbar.component.ts
import { Component, OnInit, Input } from '@angular/core';
import {Router} from '@angular/router';
import { AuthService } from '../auth.service';
@Component({
selector: 'app-navbar',
templateUrl: './app-navbar.component.html',
styleUrls: ['./app-navbar.component.sass'],
})
export class AppNavbarComponent implements OnInit {
private authUser:object = null;
constructor(
private router: Router,
private authService: AuthService,
) {
this.cartItemsCount = 0
authService.authAnnouncer$.subscribe(
user => {
this.authUser = user;
}
)
}
ngOnInit() {
}
logout () {
this.authService.logOut();
this.router.navigate(['login']);
}
}
Finally we update our navbarcomponent.html to call the method and display differently when there is an authenticated user.
<ul class="navbar-nav mr-4">
<li class="nav-item active " *ngIf="authUser" >
<a class="nav-link" >Logout</a>
</li>
<li class="nav-item active" *ngIf="!authUser" >
<a class="nav-link" routerLink="/login">Login</a>
</li>
<li class="nav-item active" *ngIf="!authUser">
<a class="nav-link" routerLink="/signup">Signup</a>
</li>
</ul>
In the links, we only show a logout button when the user is logged in i.e, there is a jwt
token and a user object in the local storage else we show the login and signup button if none of the former available.
finally we add the login handler to the form.
<a class="nav-link" (click)="logout()" >Logout</a>
Complete navbar.component.html
<nav class="navbar navbar-expand-md navbar-dark bg-dark fixed-top">
<a class="navbar-brand" href="#">Voltron</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarsExampleDefault" aria-controls="navbarsExampleDefault" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarsExampleDefault">
<ul class="navbar-nav mr-auto ">
<li class="nav-item active">
</li>
</ul>
<ul class="navbar-nav mr-4">
<li class="nav-item active " *ngIf="authUser" >
<a class="nav-link" (click)="logout()" >Logout</a>
</li>
<li class="nav-item active" *ngIf="!authUser" >
<a class="nav-link" routerLink="/login">Login</a>
</li>
<li class="nav-item active" *ngIf="!authUser">
<a class="nav-link" routerLink="/signup">Signup</a>
</li>
</ul>
<div class="mr-4 text-white">
<a routerLink="/items/order"><i class="fa fa-cart-arrow-down"><span class="badge badge-light">{{ cartItemsCount }}</span></i></a>
</div>
</div>
</nav>
Conclusion
Finally, we are done building building the authentication system and toggling the display on the navbar component . In the next tutorial, we are going work on pushing our item into a cart using a cart service.
Curriculum
- Building a food order system with MEAN Stack (MongoDB, Express, AngularJs and NodeJs). pt1.
- Building a food order system with MEAN Stack (MongoDB, Express, AngularJs and NodeJs). pt2.
- Building a food order system with MEAN Stack (MongoDB, Express, AngularJs and NodeJs). pt3.
- Pt4 Building a food order system with MEAN Stack (MongoDB, Express, AngularJs and NodeJs).
- Building the frontend Food order system with Angularjs. Part 5
- Building the frontend Food order system with Angularjs. Part 6
- Building the frontend Food order system with Angularjs. Part7
Resources
- Angular Docs
- Bootstrap 4 documentation
- Remember to check out the Repo for this project
- Official documentation on form validation
Thank you for your contribution.
Looking forward to your upcoming tutorials.
Your contribution has been evaluated according to Utopian rules and guidelines, as well as a predefined set of questions pertaining to the category.
To view those questions and the relevant answers related to your post,Click here
Need help? Write a ticket on https://support.utopian.io/.
Chat with us on Discord.
[utopian-moderator]
Hey @sirfreeman
Thanks for contributing on Utopian.
We’re already looking forward to your next contribution!
Contributing on Utopian
Learn how to contribute on our website or by watching this tutorial on Youtube.
Want to chat? Join us on Discord https://discord.gg/h52nFrV.
Vote for Utopian Witness!
Kindly follow back and upvote