Ionic App with NodeJS, Express, MySQL, Sequelize?—?Taxi App [Part 3]
This is the concluding part for this series, where we will look at the Ionic 5 App integration with the Node JS server and the MySQL database. We will be learning how to make?HTTP requests?from the Ionic Application. There is a lot to be done in front-end logic to make a real-life?Taxi App — which we are going to skip for the sake of brevity here. So let's start and go with the flow.
Creating Ionic Application
This step might be familiar to most of you, If not, you can follow the below steps to create an Ionic Application. OR for complete details, you can go to our?beginners' blog.
ionic start taxiApp blank
This will create a new project in the working directory of your System and you can start/run the application with the below command
ionic serve
Integration with Backend?Server
In this part, we will be looking at connecting the Ionic application with the backend server — which we have been building in previous blogs of this series. But before that, we have to build some UI system to interact with user actions. Here we will take an example of a Register/Login System.
Register System
In this section we will build UI for the register flow, And will take required inputs from the user like?username,?email,?password,?mobile number,?etc. You can modify your requirement according to the?table structure/database model.
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button color="light"></ion-back-button>
</ion-buttons>
<ion-title>Register</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<form [formGroup]="userForm" (ngSubmit)="registerUser()">
<ion-grid padding-top>
<ion-row>
<ion-col size="6">
<ion-item class="item-input">
<ion-label position="stacked"
class="ion-padding-bottom"
color="medium">FIRST NAME</ion-label>
<ion-input type="text"
autofocus="true"
formControlName="first_name"></ion-input>
</ion-item>
</ion-col>
<ion-col size="6">
<ion-item class="item-input">
<ion-label position="stacked"
class="ion-padding-bottom"
color="medium">LAST NAME</ion-label>
<ion-input type="text"
formControlName="last_name"></ion-input>
</ion-item>
</ion-col>
</ion-row>
<ion-row>
<ion-col size="12">
<ion-item class="item-input">
<ion-label position="stacked"
padding-bottom color="medium">
EMAIL</ion-label>
<ion-input type="email"
inputmode="email" formControlName="email"></ion-input>
</ion-item>
</ion-col>
</ion-row>
<ion-row>
<ion-col size="12">
<ion-item class="item-input">
<ion-label position="stacked" class="ion-padding-bottom" color="medium">Mobile</ion-label>
<ion-input type="tel" inputmode="tel" formControlName="phone" ></ion-input>
</ion-item>
</ion-col>
</ion-row>
<ion-row>
<ion-col size="12">
<ion-item class="item-input">
<ion-label position="stacked" class="ion-padding-bottom" color="medium">PASSWORD</ion-label>
<ion-input type="password" formControlName="password"></ion-input>
</ion-item>
</ion-col>
</ion-row>
</ion-grid>
<ion-button type="submit"
expand="full" type="submit"
[disabled]="disabled" class="submit-button">Next
<span *ngIf="spinner">
<ion-spinner name="lines"></ion-spinner>
</span>
</ion-button>
</form>
</ion-content>
This above code will help in creating the UI for the register page with different inputs.
CSS code:-
ion-button.button-outline
--background:var( --ion-color-facebook) !important;
color: white !important;
--background-activated:var( --ion-color-facebook) !important;
}
ion-button.button-solid{
--background:var(--ion-color-google) !important;
color: white !important;
--background-activated:var(--ion-color-google) !important;
}
.horizontal-line{
width: calc(100% - 10%);
height: 3px;
background: #f4f4f4;
top: 28%;
display: flex;
align-self: center;
}
.label-class{
justify-content: center;
align-items: center;
display: flex;
align-self: center;
position: absolute;
}
.footer-image{
width: 35px;
}
.img-div{
width: 30px !important;
background: var(--ion-color-primary-contrast);
border-radius: 50%;
justify-content: center;
display: flex;
margin-left: 0% !important;
height: 30px !important;
align-items: center;
color: #86888fb8;
border: solid 1px;
font-size: 15px;
}
ion-col.row-div{
display: flex;
justify-content: center;
align-items: center;
height: 50px;
}
.button-width{
width: calc(50% - 8px);
}
Now the major logic of our register system resides on?page.ts?file and will go through it, don’t worry about some the lines as they are part of the big Taxi App, just look at the?forms?and?registerUser?function only —
import { Component, OnInit } from '@angular/core'
import { MenuController } from '@ionic/angular';
import { APIService } from '@app/services/api/api.service';
import { InitUserProvider } from '@app/services/inituser/inituser.service';
import { environment } from '@env/environment';
import { UtilService } from '@app/services/util/util.service';
import { User } from '@app/models/user';
import { FormGroup, FormControl, Validators, ValidationErrors }
from '@angular/forms';
import { PushService } from '@app/services/push/push.service';
@Component({
selector: 'app-register',
templateUrl: './register.page.html',
styleUrls: ['./register.page.scss']
})
export class RegisterPage implements OnInit {
public dialCodes = environment.COUNTRY_DIAL_CODES;
userForm = new FormGroup({
'first_name': new FormControl('', [Validators.required]),
'last_name': new FormControl('', [Validators.required]),
'phone': new FormControl('', [Validators.required]),
'email': new FormControl('', [Validators.required]),
'password': new FormControl('', [Validators.required])
});
spinner = false;
disabled = false;
customAlertOptions: any = {
header: 'Contact Number',
subHeader: 'Select Area Code',
translucent: true
};
constructor(
private userProvider: InitUserProvider,
private menuCtrl: MenuController,
private api: APIService,
private util: UtilService,
private push: PushService
) {}
ngOnInit() {
}
ionViewWillEnter() {
this.menuCtrl.enable(false);
}
ionViewWillLeave() {
this.menuCtrl.enable(true);
}
setSpinner() {
this.spinner = true;
this.disabled = true;
}
clearSpinner() {
this.spinner = false;
this.disabled = false;
}
async getFormValidationErrors() {
let error = '';
Object.keys(this.userForm.controls).forEach(key => {
const controlErrors: ValidationErrors =
this.userForm.get(key).errors;
if (controlErrors != null) {
Object.keys(controlErrors).forEach(async keyError => {
error += `${key} ${keyError} & `;
});
}
});
const errMsg = error.slice(0, -3);
const toast = await this.util.createToast(errMsg, false, 'top');
await toast.present();
}
async registerUser() {
if (!this.userForm.valid) {
this.getFormValidationErrors();
return;
}
this.setSpinner();
this.api.signUp(this.userForm.value)
.then(res => {
this.userProvider.setToken(res['token']);
this.api.getUser().subscribe((user: any) => {
this.push.saveToken();
this.userProvider.setLoggedInUser(user);
this.clearSpinner();
this.util.goToNew('/home');
});
}).catch(async err => {
const toast = await this.util.createToast(
err.error.message || err.statusText, false, 'top'
);
await toast.present();
this.clearSpinner();
});
}
};
In this, we have used the?Angular Reactive forms, Which helps us in Validating our input data using custom or predefined validators. And maintains the integrity of the database (Avoiding entering wrong data into the database).
this.api.signUp(this.userForm.value).then(res => {
...
this.api.getUser().subscribe((user: any) => {
... // Save JWT token here
this.util.goToNew('/home');
// move to home page once authenticated
});
})
The above piece of code is calling?signUp?function which we have created in the?API Service file?(api.service.ts). That?signUp?function uses?HTTPClient?Service of Angular to call the backend APIs. You can see the code below how we send the userInfo to the register API endpoints (/clients/register).
领英推荐
async signUp(user) {
const userInfo = {
'username': user['email'],
'email': user['email'],
'name': `${user['first_name']} ${user['last_name']}`,
'phone': user['phone'],
'password': user['password'],
'type': 'client',
'stripeId': user['stripeId']
};
...
return
this.http.post(`${this.url}/clients/register`,userInfo).toPromise();
}
?we made the?HTTP POST?request to the server by providing the?userInfo?into the body, Here?this.url?is?https://localhost:8100?OR?it can be your remote Server Address also, you can always set that in your?environment?files.
?Once you start the Node/Express server using?npm start ( node index) your?API will be activated. When you call the above API — it will send data into the?Users?relation of the Database (checkout the register API we created in the previous part of this series to know more).
In the codes above, you can also notice an API endpoint to get user profile which returns saved user information to App, so you can manage your user sessions. Also, we save some JWT tokens locally using?localStorage — so to authenticate our API calls, this is needed as we have set JWT auth in previous blogs.
Login System
For the Login procedure of the Ionic Application, we have to input the?email?and?password. We create a similar set of HTML templates using Ionic Tags and connect it with JS files.?
UI for Login System
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button color="light"></ion-back-button>
</ion-buttons>
<ion-title>Log In</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<div style="margin-top:20px">
<ion-item class="item-input">
<ion-label position="stacked"
padding-bottom color="medium">EMAIL</ion-label>
<ion-input type="email" [(ngModel)]="user.email"></ion-input>
</ion-item>
<ion-item class="item-input">
<ion-label position="stacked" padding-bottom color="medium"
>PASSWORD</ion-label>
<ion-input type="password" [(ngModel)]="user.password">
</ion-input>
</ion-item>
</div>
<ion-button expand="full" type="submit"
[disabled]="disabled" class="submit-button" padding-vertical
(click)="login()">Log In
<span *ngIf="spinner">
<ion-spinner name="lines"></ion-spinner>
</span>
</ion-button>
<ion-button margin-top color="medium" fill="clear"
type="submit" class="forgot-button"
routerLink="/forgotpassword">Forgot Password</ion-button>
</ion-content>
In this we have used two input fields (Email and Password) of the User, The Node server will verify and send the response.
UI CSS
.sc-ion-buttons-md-s ion-button {
--margin-start: 5px;
--margin-end: 5px;
margin-left: 0px;
margin-right: 0px;
--padding-start: 5.5em;
--padding-end: 5.5em;
--height: 35px;
--box-shadow: none;
font-size: 12px;
font-weight: 500;
padding-top: 1.5em;
padding-bottom: 3em;
}
ion-button.button.button-outline.button-outline {
--border-color:var( --ion-color-facebook) !important;
color: var( --ion-color-facebook) !important;
}
ion-button.button.button-outline.button-solid {
--border-color: var(--ion-color-google) !important;
color: var(--ion-color-google)!important;
}
ion-input{
background: rgb(250, 255, 189);
}
.horizontal-line{
width: calc(100% - 10%);
height: 3px;
background: #f4f4f4;
top: 28%;
display: flex;
align-self: center;
}
.label-class{
justify-content: center;
align-items: center;
display: flex;
align-self: center;
position: absolute;
}
.footer-image{
width: 35px;
}
.img-div{
width: 30px !important;
background: var(--ion-color-primary-contrast);
border-radius: 50%;
justify-content: center;
display: flex;
margin-left: 0% !important;
height: 30px !important;
align-items: center;
color: #86888fb8;
border: solid 1px;
font-size: 15px;
}
ion-col.row-div{
display: flex;
justify-content: center;
align-items: center;
height: 50px;
}
.button-width{
width: calc(50% - 8px);
}
This above code will add the styles to the Login Page, Now the most important part the Logic of the Login System in?login.page.ts
import { Component, OnInit } from '@angular/core';
import { MenuController } from '@ionic/angular';
import { APIService } from '@app/services/api/api.service';
import { InitUserProvider } from '@app/services/inituser/inituser.service';
import { User } from '@app/models/user';
import { UtilService } from '@app/services/util/util.service';
import { PushService } from '@app/services/push/push.service';
import {
AlertController,
} from '@ionic/angular';
@Component({
selector: 'app-login',
templateUrl: './login.page.html',
styleUrls: ['./login.page.scss']
})
export class LoginPage implements OnInit {
user: any = { email: '', password: '' };
spinner = false;
disabled = false;
constructor(
private userProvider: InitUserProvider,
private menuCtrl: MenuController,
private api: APIService,
private util: UtilService,
private push: PushService,
private alertCtrl: AlertController
) { }
ngOnInit() {
}
ionViewWillEnter() {
this.menuCtrl.enable(false);
}
ionViewWillLeave() {
this.menuCtrl.enable(true);
}
setSpinner() {
this.spinner = true;
this.disabled = true;
}
clearSpinner() {
this.spinner = false;
this.disabled = false;
}
resolveRec(val) {
}
async login() {
// cosnt feedbackModal = await
this.util.createModal(FeedbackComponent, {}, '');
// feedbackModal.present();
this.setSpinner();
this.api.logIn(this.user.email, this.user.password)
.subscribe(
res => {
this.userProvider.setToken(res['token']);
this.api.getUser().subscribe((responseUser: any) => {
this.push.saveToken();
this.userProvider.setLoggedInUser(responseUser);
this.clearSpinner();
this.util.goToNew('/home');
});
},
async err => {
const toast = await
this.util.createToast(
err.error.info.message, false, 'top'
);
await toast.present();
this.clearSpinner();
}
);
}
};
It contains the complete code for calling server and verifying that USER exists or not, If yes setting user values globally. The important part of the code is explained below in this snippet.
this.api.logIn(this.user.email, this.user.password).subscribe(res => {
this.userProvider.setToken(res['token']); // Saving JWT Token
this.api.getUser().subscribe((responseUser: any) => {
this.userProvider.setLoggedInUser(responseUser);
// Saving user Info locally in a Service
this.util.goToNew('/home');
// Moving to Home page on successful login
});
}
This code passes the entered input (Email and Password) to the?logIn?function, which defined in?API service?in the project. API Service has some code as shown below, which makes?HTTP POST?request and passes email, password in the body.
logIn(username: string, password: string): Observable<any> {
return this.http.post(
`${this.url}/clients/login`,
{ username, password }
);
}
This code will work if you have started the server already and?this.url?is properly set to point to the?Local OR Remote Server.?Once again go back to our previous blogs to understand what the login API is accepting on the Server-side and how it is returning a JWT token in response.
Conclusion?:
In this series of tutorials, we have learned about an important concept IONIC FRAMEWORK + NodeJS + MySQL + SEQUELIZE, Which many times proved a Stable Tech stack to work on. Now you are fully prepared to make your own application with great features and UI. The taxi code has a lot of features and plugin — obviously, it can take months to develop a fully working app. If you want to save your time and get an Application Template or starter you can visit our website?Enappd.com