So, you don’t have to repeat calling token when call or consume RESTful API. For the RESTful API, we will use our existing Node, Express, Passport and MongoDB that you can get from our GitHub.
The following tools, frameworks, and modules are required for this tutorial:
Before going to the main steps, we assume that you have to install Node.js. Next, upgrade or install new Ionic 4 CLI by open the terminal or Node command line then type this command.
sudo npm install -g ionic
You will get the latest Ionic CLI in your terminal or command line. Check the version by type this command.
ionic --version
4.3.1
1. Create a new Ionic 4 and Angular 7 App
To create a new Ionic 4 and Angular 7 application, type this command in your terminal.
ionic start ionic4-angular7-interceptor blank --type=angular
If you see this question, just type N
for because we will installing or adding Cordova later.
Integrate your new app with Cordova to target native iOS and Android? (y/N) N
After installing NPM
modules and dependencies, you will see this question, just type N
because we are not using it yet.
Install the free Ionic Pro SDK and connect your app? (Y/n) N
Next, go to the newly created app folder.
cd ./ionic4-angular7-interceptor
As usual, run the Ionic 4 and Angular 7 app for the first time, but before run as lab
mode, type this command to install @ionic/lab
.
npm install --save-dev @ionic/lab
ionic serve -l
Now, open the browser and you will the Ionic 4 and Angular 7 app with the iOS, Android, or Windows view. If you see a normal Ionic 4 blank application, that’s mean you ready to go to the next steps.
2. Create a custom Angular 7 HttpInterceptor
Before creating a custom Angular 7 HttpInterceptor, create a folder under app
folder.
mkdir src/app/interceptors
Next, create a file for the custom Angular 7 HttpInterceptor.
touch src/app/interceptors/token.interceptor.ts
Open and edit that file the add this imports.
import {
HttpRequest,
HttpHandler,
HttpEvent,
HttpInterceptor,
HttpResponse,
HttpErrorResponse
} from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { map, catchError } from 'rxjs/operators';
import {
Router
} from '@angular/router';
import { ToastController } from '@ionic/angular';
Create a class that implementing HttpInterceptor method.
@Injectable()
export class TokenInterceptor implements HttpInterceptor {
}
Inject the required module to the constructor inside the class.
constructor(private router: Router,
public toastController: ToastController) {}
Implement a custom Interceptor function.
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const token = localStorage.getItem('token');
if (token) {
request = request.clone({
setHeaders: {
'Authorization': token
}
});
}
if (!request.headers.has('Content-Type')) {
request = request.clone({
setHeaders: {
'content-type': 'application/json'
}
});
}
request = request.clone({
headers: request.headers.set('Accept', 'application/json')
});
return next.handle(request).pipe(
map((event: HttpEvent<any>) => {
if (event instanceof HttpResponse) {
console.log('event--->>>', event);
}
return event;
}),
catchError((error: HttpErrorResponse) => {
if (error.status === 401) {
if (error.error.success === false) {
this.presentToast('Login failed');
} else {
this.router.navigate(['login']);
}
}
return throwError(error);
}));
}
Add function for call a toast controller.
async presentToast(msg) {
const toast = await this.toastController.create({
message: msg,
duration: 2000,
position: 'top'
});
toast.present();
}
Next, we have to register this custom HttpInterceptor and HttpClientModule. Open and edit src/app/app.module.ts
then add this imports.
import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http';
import { TokenInterceptor } from './inteceptors/token.interceptor';
Add those modules to the provider array of the @NgModule
.
providers: [
StatusBar,
SplashScreen,
{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy },
{
provide: HTTP_INTERCEPTORS,
useClass: TokenInterceptor,
multi: true
}
],
Now, the HTTP interceptor is ready to intercept any request to the API.
3. Create Ionic 4 Angular 7 Authentication and Book Services
First, create a folder for those services under src/app/
.
mkdir src/app/services
Create services or providers for authentication and book.
ionic g service services/auth
ionic g service services/book
Next, open and edit src/app/services/auth.service.ts
then replace all codes with this.
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, of } from 'rxjs';
import { catchError, tap } from 'rxjs/operators';
@Injectable({
providedIn: 'root'
})
export class AuthService {
apiUrl = 'http://localhost:3000/api/';
constructor(private http: HttpClient) { }
login (data): Observable<any> {
return this.http.post<any>(this.apiUrl + 'signin', data)
.pipe(
tap(_ => this.log('login')),
catchError(this.handleError('login', []))
);
}
logout (): Observable<any> {
return this.http.get<any>(this.apiUrl + 'signout')
.pipe(
tap(_ => this.log('logout')),
catchError(this.handleError('logout', []))
);
}
register (data): Observable<any> {
return this.http.post<any>(this.apiUrl + 'signup', data)
.pipe(
tap(_ => this.log('login')),
catchError(this.handleError('login', []))
);
}
private handleError<T> (operation = 'operation', result?: T) {
return (error: any): Observable<T> => {
// TODO: send the error to remote logging infrastructure
console.error(error); // log to console instead
// TODO: better job of transforming error for user consumption
this.log(`${operation} failed: ${error.message}`);
// Let the app keep running by returning an empty result.
return of(result as T);
};
}
/** Log a HeroService message with the MessageService */
private log(message: string) {
console.log(message);
}
}
Next, open and edit src/app/services/book.service
then replace all codes with this.
import { Injectable } from '@angular/core';
import { Book } from '../book/book';
import { HttpClient } from '@angular/common/http';
import { Observable, of } from 'rxjs';
import { catchError, tap } from 'rxjs/operators';
@Injectable({
providedIn: 'root'
})
export class BookService {
apiUrl = 'http://localhost:3000/api/';
constructor(private http: HttpClient) { }
getBooks (): Observable<Book[]> {
return this.http.get<Book[]>(this.apiUrl + 'book')
.pipe(
tap(_ => this.log('fetched books')),
catchError(this.handleError('getBooks', []))
);
}
private handleError<T> (operation = 'operation', result?: T) {
return (error: any): Observable<T> => {
// TODO: send the error to remote logging infrastructure
console.error(error); // log to console instead
// TODO: better job of transforming error for user consumption
this.log(`${operation} failed: ${error.message}`);
// Let the app keep running by returning an empty result.
return of(result as T);
};
}
/** Log a HeroService message with the MessageService */
private log(message: string) {
console.log(message);
}
}
4. Create an Ionic 4 and Angular 7 Book Page
Type this command to generate an Ionic 4 and Angular 7 page for the book.
ionic g page book
Create a Typescript file for handle Book object or as a model for Book object.
touch src/app/book/book.ts
Open end edit that file then replaces all codes with this.
export class Book {
_id: number;
isbn: string;
title: string;
author: number;
publisher: Date;
__v: number;
}
Next, open and edit src/app/book/book.page.ts
then add this imports.
import { Book } from './book';
import { BookService } from '../services/book.service';
import { AuthService } from '../services/auth.service';
import { Router } from '@angular/router';
Inject all required modules to the constructor.
constructor(private bookService: BookService,
private authService: AuthService,
private router: Router) { }
Declare this variable before the constructor.
books: Book[];
Create a function for consuming or get book list from the book service.
getBooks(): void {
this.bookService.getBooks()
.subscribe(books => {
console.log(books);
this.books = books;
});
}
Call this function from ngOnInit
.
ngOnInit() {
this.getBooks();
}
Add a function for log out the current session.
logout() {
this.authService.logout()
.subscribe(res => {
console.log(res);
localStorage.removeItem('token');
this.router.navigate(['login']);
});
}
Next, open and edit src/app/book/book.page.html
then replace all HTML tags with this.
<ion-header>
<ion-toolbar>
<ion-title>List of Books</ion-title>
<ion-buttons slot="end">
<ion-button color="secondary" (click)="logout()">
<ion-icon slot="icon-only" name="exit"></ion-icon>
</ion-button>
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content padding>
<ion-list>
<ion-item *ngFor="let b of books">
<ion-label>{{b.title}}</ion-label>
</ion-item>
</ion-list>
</ion-content>
The book page just shows the list of the book and log out button on the title bar.
5. Create the Ionic 4 and Angular 7 Pages for Login and Register
Before creating that pages, create this folder under src/app
.
mkdir src/app/auth
Now, generate the Ionic 4 and Angular 7 pages.
ionic g page auth/login
ionic g page auth/register
Next, open and edit src/app/auth/login/login.page.ts
then replace all codes with this.
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, NgForm, Validators } from '@angular/forms';
import { AuthService } from '../../services/auth.service';
import { Router } from '@angular/router';
import { ToastController } from '@ionic/angular';
@Component({
selector: 'app-login',
templateUrl: './login.page.html',
styleUrls: ['./login.page.scss'],
})
export class LoginPage implements OnInit {
loginForm: FormGroup;
constructor(private formBuilder: FormBuilder,
private router: Router,
private authService: AuthService,
public toastController: ToastController) { }
ngOnInit() {
this.loginForm = this.formBuilder.group({
'username' : [null, Validators.required],
'password' : [null, Validators.required]
});
}
onFormSubmit(form: NgForm) {
this.authService.login(form)
.subscribe(res => {
if (res.token) {
localStorage.setItem('token', res.token);
this.router.navigate(['book']);
}
}, (err) => {
console.log(err);
});
}
register() {
this.router.navigate(['register']);
}
async presentToast(msg) {
const toast = await this.toastController.create({
message: msg,
duration: 2000,
position: 'top'
});
toast.present();
}
}
Next, open and edit src/app/auth/login/login.page.html
then replace all HTML tags with this.
<ion-content padding>
<form [formGroup]="loginForm" (ngSubmit)="onFormSubmit(loginForm.value)">
<ion-card>
<ion-card-header>
<ion-card-title>Please, Sign In</ion-card-title>
</ion-card-header>
<ion-card-content>
<ion-item>
<ion-label position="floating">Username</ion-label>
<ion-input type="text" formControlName="username"></ion-input>
</ion-item>
<ion-item>
<ion-label position="floating">Password</ion-label>
<ion-input type="password" formControlName="password"></ion-input>
</ion-item>
<ion-item lines="none" lines="full" padding-top>
<ion-button expand="full" size="default" shape="round" color="success" type="submit" [disabled]="!loginForm.valid">Login</ion-button>
</ion-item>
<ion-item lines="none">
<ion-button type="button" expand="full" size="default" shape="round" color="medium" (click)="register()">Register</ion-button>
</ion-item>
</ion-card-content>
</ion-card>
</form>
</ion-content>
Next, open and edit src/app/auth/register/register.page.ts
then replace all codes with this.
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, NgForm, Validators } from '@angular/forms';
import { AuthService } from '../../services/auth.service';
import { Router } from '@angular/router';
import { ToastController, AlertController } from '@ionic/angular';
@Component({
selector: 'app-register',
templateUrl: './register.page.html',
styleUrls: ['./register.page.scss'],
})
export class RegisterPage implements OnInit {
registerForm: FormGroup;
constructor(private formBuilder: FormBuilder,
private router: Router,
private authService: AuthService,
public toastController: ToastController,
public alertController: AlertController) { }
ngOnInit() {
this.registerForm = this.formBuilder.group({
'username' : [null, Validators.required],
'password' : [null, Validators.required]
});
}
onFormSubmit(form: NgForm) {
this.authService.register(form)
.subscribe(_ => {
this.presentAlert('Register Successfully', 'Please login with your new username and password');
}, (err) => {
console.log(err);
});
}
async presentToast(msg) {
const toast = await this.toastController.create({
message: msg,
duration: 2000,
position: 'top'
});
toast.present();
}
async presentAlert(header, message) {
const alert = await this.alertController.create({
header: header,
message: message,
buttons: [{
text: 'OK',
handler: () => {
this.router.navigate(['login']);
}
}]
});
await alert.present();
}
}
Open and edit src/app/auth/login/login.page.html
then replace all HTML tags with this.
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button></ion-back-button>
</ion-buttons>
<ion-title>Register</ion-title>
</ion-toolbar>
</ion-header>
<ion-content padding>
<form [formGroup]="registerForm" (ngSubmit)="onFormSubmit(registerForm.value)">
<ion-card>
<ion-card-header>
<ion-card-title>Register here</ion-card-title>
</ion-card-header>
<ion-card-content>
<ion-item>
<ion-label position="floating">Username</ion-label>
<ion-input type="text" formControlName="username"></ion-input>
</ion-item>
<ion-item>
<ion-label position="floating">Password</ion-label>
<ion-input type="password" formControlName="password"></ion-input>
</ion-item>
<ion-item lines="none" padding-top>
<ion-button expand="full" size="default" shape="round" color="success" type="submit" [disabled]="!registerForm.valid">Register</ion-button>
</ion-item>
</ion-card-content>
</ion-card>
</form>
</ion-content>
6. Run and Test the Ionic 4 and Angular 7 Authentication App
Before the run the Ionic 4 and Angular 7 app, we should run the RESTful API server first. After you clone our Express.js API, run this command in the terminal.
npm i
Open another Terminal tabs if your MongoDB server not running then type this command to run it.
mongod
Back to Express API, type this command to run it.
nodemon
or
npm start
Next, we have to run the Ionic 4 and Angular 7 app by typing this command.
ionic serve -l
You should see these pages if everything working properly.
That it’s, the Ionic 4 and Angular 7 HttpIntercepter and HttpClient implementation to the Authentication app. You can find the full working source code on our GitHub.
We know that building beautifully designed Ionic apps from scratch can be frustrating and very time-consuming. Check Ion2FullApp ELITE - The Complete Ionic 3 Starter App and save development and design time.
That just the basic. If you need more deep learning about Ionic, Angular, and Typescript, you can find the following books:
Or take the following course:
☞ Angular 2 Firebase - Build a Web App with Typescript
☞ Master Angular 2 - The No Nonsense Course
☞ Angular 2 - The Complete Guide
☞ Angular 2 + Rails 5 Bootcamp
☞ Angular Tutorial - Learn Angular from Scratch
☞ JavaScript Programming Tutorial Full Course for Beginners
☞ Angular and Nodejs Integration Tutorial
☞ Learn JavaScript - Become a Zero to Hero