Dalam tutorial kali ini kita akan belajar membuat sebuah aplikasi mobile dengan framework ionic 4 untuk login dan register user. Aplikasi yang akan kita buat menggunakan ionic 4 tipe angular.
Penggunaan web service untuk pertukaran data antar server dengan aplikasi client yang akan kita gunakan menggunakan slim framework yang pernah saya buatkan tutorial sebelumnya. Silahkan teman-teman pelajari dulu sebelum memulai membuat aplikasi login dan register ionic 4 ini. Membuat RESTFul Api Slim Framework dengan Authorization JWT .
Membuat Login dan Register Ionic 4
Buat aplikasi ionic 4 baru melalui cmd atau terminal dengan mengetikkan perintah berikut ini.
ionic start MyAppIonic4 blank --type=angular --cordova
Setelah aplikasi MyappIonic4 selesai dibuat, buka folder MyappIonic4 kemudian buat halaman baru untuk login dan register melalui terminal.
ionic g page login
ionic g page register
Selanjutnya buat service untuk pemanggilan rest api dan guard untuk mengamankan route halaman melalui terminal. Pilih CanActivate saat create guard.
ionic g service services/service
ionic g guard guards/auth
Buka app.module.ts kemudian tambahkan module seperti baris dibawah ini yang di beri highlight.
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouteReuseStrategy } from '@angular/router';
import { IonicModule, IonicRouteStrategy } from '@ionic/angular';
import { SplashScreen } from '@ionic-native/splash-screen/ngx';
import { StatusBar } from '@ionic-native/status-bar/ngx';
import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';
import { HttpClientModule } from '@angular/common/http';
import { RegisterPageModule } from './register/register.module';
@NgModule({
declarations: [AppComponent],
entryComponents: [],
imports: [
BrowserModule,
IonicModule.forRoot(),
AppRoutingModule,
HttpClientModule,
RegisterPageModule
],
providers: [
StatusBar,
SplashScreen,
{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy }
],
bootstrap: [AppComponent]
})
export class AppModule {}
Buka service.service.ts untuk membuat beberapa fungsi seperti cek token, login, register, logout dan autentikasi.
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { tap, timeout } from 'rxjs/operators';
import { ReplaySubject } from 'rxjs';
import { Platform, ToastController } from '@ionic/angular';
@Injectable({
providedIn: 'root'
})
export class ServiceService {
DataLogin:any;
DataCheckLogin:any;
authenticationState = new ReplaySubject();
token:any;
API_URL = 'http://localhost:88/slimrestjwt/public/';
TOKEN_KEY = 'token';
constructor(
private http: HttpClient,
private platform: Platform,
public toastController: ToastController
) {
this.platform.ready().then(() => {
this.checkToken();
});
}
//ika token tidak ada maka authenticationState=false
//jika token ada maka akan memanggil fungsi cekUser
checkToken() {
if(localStorage.getItem(this.TOKEN_KEY)==null || localStorage.getItem(this.TOKEN_KEY)=='') {
this.authenticationState.next(false);
}else{
this.CekUser().subscribe(data => {
this.DataCheckLogin=data;
if(this.DataCheckLogin.status=="success"){
this.authenticationState.next(true);
}else{
this.authenticationState.next(false);
}
},
err => {
this.authenticationState.next(false);
});
}
}
//cek user di sisi server dengan headers authorize bearer
//teman-teman dapat membuat fungsi baru untuk request data lainnya dengan header authorize bearer
CekUser(){
//ambil data dari localstorage
let dataStorage=JSON.parse(localStorage.getItem(this.TOKEN_KEY));
this.token=dataStorage.token;
const headers = new HttpHeaders({
'Content-Type': 'application/json',
'Authorization': "Bearer "+this.token
});
return this.http.get(this.API_URL + 'api/user/'+dataStorage.data.IdUser, { headers: headers }).pipe(
timeout(8000),
tap(Data => {
return Data;
})
);
}
//login
loginApi(credentials, type){
const headers = new HttpHeaders({
'Content-Type': 'application/json',
});
return this.http.post(this.API_URL + type, credentials, { headers: headers }).pipe(
tap(Data => {
this.DataLogin=Data;
if(this.DataLogin.status=="success"){
localStorage.setItem(this.TOKEN_KEY, JSON.stringify(Data));
this.authenticationState.next(true);
}else{
this.authenticationState.next(false);
}
return Data;
})
);
}
//register
RegisterApi(credentials, type){
const headers = new HttpHeaders({
'Content-Type': 'application/json',
});
return this.http.post(this.API_URL + type, credentials, { headers: headers }).pipe(
tap(Data => {
this.DataLogin=Data;
if(this.DataLogin.status=="success"){
localStorage.setItem(this.TOKEN_KEY, JSON.stringify(Data));
this.authenticationState.next(true);
}else{
this.authenticationState.next(false);
}
return Data;
})
);
}
//logout
logout() {
this.authenticationState.next(false);
}
}
Buka auth.guard.ts, auth guard ini berfungsi untuk autentikasi route halaman.
import { Injectable } from '@angular/core';
import { CanActivate } from '@angular/router';
import { ServiceService } from '../services/service.service';
@Injectable({
providedIn: 'root'
})
export class AuthGuard implements CanActivate {
authstatus:any;
constructor(
private serviceService: ServiceService
) {}
canActivate(): boolean {
this.serviceService.authenticationState.subscribe((data) => {
this.authstatus=data;
});
return this.authstatus;
}
}
Selanjutnya buka app.component.ts, kita akan menambahkan fungsi auth. fungsi ini akan memanggil fungsi yang berada di service dan mengecek status autentikasi true atau false. jika true maka akan di redirect ke halaman home jika false akan di redirect ke halaman login.
import { Component } from '@angular/core';
import { Platform, NavController } from '@ionic/angular';
import { SplashScreen } from '@ionic-native/splash-screen/ngx';
import { StatusBar } from '@ionic-native/status-bar/ngx';
import { ServiceService } from './services/service.service';
@Component({
selector: 'app-root',
templateUrl: 'app.component.html',
styleUrls: ['app.component.scss']
})
export class AppComponent {
constructor(
private platform: Platform,
private splashScreen: SplashScreen,
private statusBar: StatusBar,
private serviceService: ServiceService,
private navCtrl: NavController,
) {
this.initializeApp();
}
initializeApp() {
this.platform.ready().then(() => {
this.statusBar.styleDefault();
this.splashScreen.hide();
this.Auth();
});
}
Auth(){
this.serviceService.authenticationState.subscribe((data) => {
if (data==true) {
this.navCtrl.navigateRoot(['home']);
} else {
this.navCtrl.navigateRoot(['login']);
}
});
}
}
Untuk mengamankan setiap rute halaman, kita tambahkan guard yang tadi telah dibuat di app-routing.module.ts, perhatikan baris yang diberi highlight. teman-teman dapat menambahkan halaman baru dengan canActive untuk autentikasi.
import { NgModule } from '@angular/core';
import { PreloadAllModules, RouterModule, Routes } from '@angular/router';
import { AuthGuard } from './guards/auth.guard';
const routes: Routes = [
{ path: '', redirectTo: 'home', pathMatch: 'full' },
{ path: 'home',
canActivate: [AuthGuard],
loadChildren: () => import('./home/home.module').then( m => m.HomePageModule)},
{
path: 'login',
loadChildren: () => import('./login/login.module').then( m => m.LoginPageModule)
},
{
path: 'register',
loadChildren: () => import('./register/register.module').then( m => m.RegisterPageModule)
},
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
Buka login.module.ts, tambahkan reactive form module seperti dibawah ini.
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { IonicModule } from '@ionic/angular';
import { LoginPageRoutingModule } from './login-routing.module';
import { LoginPage } from './login.page';
import { ReactiveFormsModule } from '@angular/forms';
@NgModule({
imports: [
CommonModule,
FormsModule,
IonicModule,
LoginPageRoutingModule,
ReactiveFormsModule,
],
declarations: [LoginPage]
})
export class LoginPageModule {}
Selanjutnya buka login.page.ts, kita akan menambahkan formgrup login, fungsi request login, dan menu modal popup untuk halaman register.
import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroupDirective, FormBuilder, FormGroup, NgForm, Validators } from '@angular/forms';
import { NavController, ModalController, LoadingController, ToastController,Platform } from '@ionic/angular';
import { RegisterPage } from '../register/register.page';
import { ServiceService } from '../services/service.service';
@Component({
selector: 'app-login',
templateUrl: './login.page.html',
styleUrls: ['./login.page.scss'],
})
export class LoginPage implements OnInit {
FormLogin:FormGroup;
showPasswordText:any;
dataLogin:any;
constructor(
private formBuilder: FormBuilder,
private navCtrl: NavController,
public loadingController: LoadingController,
public modalController: ModalController,
private platform: Platform,
public toastController: ToastController,
private serviceService: ServiceService,
) { }
ngOnInit() {
//setting form login
this.FormLogin=this.formBuilder.group({
Username:['',Validators.required],
Password:['',Validators.required]
});
}
//fungsi login
async login(){
//menampilkan loading
const loading = await this.loadingController.create({
message: 'Please wait...'
});
await loading.present();
//memanggil fungsi loginapi yang berada di service
this.serviceService.loginApi(this.FormLogin.value,'login').subscribe(
data => {
this.dataLogin=data;
if(this.dataLogin.status=="error"){
let message='Nama pengguna dan kata sandi yang Anda masukkan tidak cocok. Silahkan periksa dan coba lagi.';
this.presentToast(message);
}
loading.dismiss();
},
error => {
let message='Tidak ada koneksi internet. Silakan periksa koneksi Anda.';
this.presentToast(message);
loading.dismiss();
}
);
}
//menampilkan halaman register
async registerModal() {
const modal = await this.modalController.create({
component: RegisterPage
});
return await modal.present();
}
async presentToast(Message) {
const toast = await this.toastController.create({
message: Message,
duration: 2500,
position: "bottom"
});
toast.present();
}
}
Edit file login.page.html untuk membuat tampilan form login dan button register.
<ion-content class="ion-padding">
<ion-grid>
<ion-row>
<ion-col><br />
<img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_dc4jZZ74IN7WDKGpw4E9hyphenhyphentAsQAffIXV2edLyrgYpCciEouXbvwXIWJUR3Pt4l3ziyUfHClCqknQVEoOr6tFbh56w4hoxD9HutqyWz2GpXHl-BwHHQ9K54EZx1LpodWPubhcia-bOIA/s320/logo+2.png" width="30%" /><br /><br />
</ion-col>
</ion-row>
</ion-grid>
<form [formGroup]="FormLogin" (ngSubmit)="login()">
<ion-item>
<div color="dark" slot="start">
<ion-icon slot="icon-only" name="person" ></ion-icon>
</div>
<ion-input type="text" name="Username" placeholder="Username" formControlName="Username"></ion-input>
</ion-item>
<ion-item>
<div color="dark" slot="start">
<ion-icon slot="icon-only" name="lock-closed"></ion-icon>
</div>
<ion-input type="text" name="password" placeholder="Password" formControlName="Password" *ngIf="showPasswordText"></ion-input>
<ion-input type="password" name="password" placeholder="Password" formControlName="Password" *ngIf="!showPasswordText"></ion-input>
<ion-icon color="dark" slot="icon-only" (click)="showPasswordText = !showPasswordText" *ngIf="showPasswordText" name="eye" slot="end" class="ion-align-self-center"></ion-icon>
<ion-icon color="dark" slot="icon-only" (click)="showPasswordText = !showPasswordText" *ngIf="!showPasswordText" name="eye-off" slot="end" class="ion-align-self-center"></ion-icon>
</ion-item>
<p class="ion-text-right">Forgot Password?</p>
<ion-button type="submit" expand="block" color="primary" [disabled]="!FormLogin.valid">Login</ion-button>
</form>
<p class="ion-text-center">Don't have a account?</p>
<ion-button expand="block" color="danger" (click)="registerModal()" >Register</ion-button>
</ion-content>
Tambahkan css untuk mempercantik tampilan form login di login.page.css.
ion-list{
background-color: transparent;
padding: 0;
}
ion-row {
align-items: center;
text-align: center;
}
ion-item {
border-radius: 10px !important;
font-size: 0.9em;
margin-bottom: 15px;
border: 1px solid rgba(34, 34, 34, 0.096);
border-bottom: 0px !important;
background-color: #ffffff;
-webkit-box-shadow: 4px 8px 6px -6px #222;
-moz-box-shadow: 4px 8px 6px -6px #222;
box-shadow: 4px 4px 6px -6px #222;
}
Tampilan form login seperti gambar dibawah ini.
Buka file home.page.ts, kita akan membuat fungsi logout dan fungsi untuk menampilkan username.
import { Component, OnInit } from '@angular/core';
import { LoadingController } from '@ionic/angular';
import { ServiceService } from '../services/service.service';
@Component({
selector: 'app-home',
templateUrl: 'home.page.html',
styleUrls: ['home.page.scss'],
})
export class HomePage implements OnInit {
Username:any;
constructor(
public loadingController: LoadingController,
private serviceService: ServiceService
) {}
ngOnInit() {
//ambil data dari localstorage
let dataStorage=JSON.parse(localStorage.getItem(this.serviceService.TOKEN_KEY));
this.Username=dataStorage.data.Username;
}
async logout(){
const loading = await this.loadingController.create({
message: 'Please wait...'
});
await loading.present();
localStorage.clear();
this.serviceService.logout();
loading.dismiss();
}
}
Buka file home.page.html untuk mengubah tampilan home dengan menambahkan button logout.
<ion-header translucent>
<ion-toolbar color="primary">
<ion-title>
Maribelajarcoding
</ion-title>
</ion-toolbar>
</ion-header>
<ion-content fullscreen>
<div id="container">
<strong>Selamat datang, {{Username}}</strong>
<p>Start with Ionic <a target="_blank" rel="noopener noreferrer" href="https://ionicframework.com/docs/components">UI Components</a></p>
<ion-button color="danger" (click)="logout()" >Logout</ion-button>
</div>
</ion-content>
Tampilan halaman home seperti gambar dibawah ini.
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { IonicModule } from '@ionic/angular';
import { RegisterPageRoutingModule } from './register-routing.module';
import { RegisterPage } from './register.page';
import { ReactiveFormsModule } from '@angular/forms';
@NgModule({
imports: [
CommonModule,
FormsModule,
IonicModule,
RegisterPageRoutingModule,
ReactiveFormsModule
],
declarations: [RegisterPage]
})
export class RegisterPageModule {}
Selanjutnya kita buka register.page.ts untuk setting form group, validasi form, dan fungsi register seperti dibawah ini.
import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroupDirective, FormBuilder, FormGroup, NgForm, Validators } from '@angular/forms';
import { NavController, LoadingController, ToastController, Platform, ModalController, AlertController } from '@ionic/angular';
import { ServiceService } from '../services/service.service';
@Component({
selector: 'app-register',
templateUrl: './register.page.html',
styleUrls: ['./register.page.scss'],
})
export class RegisterPage implements OnInit {
showPasswordText:any;
showKonfirmPasswordText:any;
validations = {
'Username': [
{ type: 'required', message: 'Username harus diisi.' },
{ type: 'validUsername', message: 'Username sudah terdaftar.' }
],
'Password': [
{ type: 'required', message: 'Password harus diisi.' },
{ type: 'minlength', message: 'Password minimal harus 5 karakter.' },
{ type: 'pattern', message: 'Password harus mengandung huruf (baik huruf besar dan kecil) dan angka.' },
],
'NamaLengkap': [
{ type: 'required', message: 'Nama lengkap harus diisi.' },
],
'Email': [
{ type: 'required', message: 'Email harus diisi.' },
{ type: 'pattern', message: 'Email tidak valid.' },
],
'NoHp': [
{ type: 'required', message: 'No Hp harus diisi.' },
{ type: 'minlength', message: 'No Hp minimal harus 10 karakter.' },
{ type: 'maxlength', message: 'No Hp maksimal harus 15 karakter.' },
],
};
FormRegister: FormGroup;
ResponseRegister:any;
constructor(
private formBuilder: FormBuilder,
private navCtrl: NavController,
public loadingController: LoadingController,
private platform: Platform,
public toastController: ToastController,
public alertController: AlertController,
public modalController: ModalController,
private serviceService: ServiceService
) { }
ngOnInit() {
this.FormRegister = this.formBuilder.group({
Username:new FormControl('', Validators.compose([
Validators.required,
])),
Password:new FormControl('', Validators.compose([
Validators.required,
Validators.minLength(5),
// Validators.pattern('^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])[a-zA-Z0-9]+$')
])),
NamaLengkap:new FormControl('', Validators.compose([Validators.required])),
Email:new FormControl('', Validators.compose([
Validators.required,
Validators.pattern('^[\\w]+(?:\\.[\\w])*@(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,6}$')
])),
NoHp:new FormControl('', Validators.compose([
Validators.required,
Validators.minLength(10),
Validators.maxLength(15)
]))
});
}
async Register(){
//menampilkan loading
const loading = await this.loadingController.create({
message: 'Please wait...'
});
await loading.present();
//panggil fungsi register di service
this.serviceService.RegisterApi(this.FormRegister.value,'register').subscribe(
data => {
this.ResponseRegister=data;
//cek apakah register berhasil atau tidak
if(this.ResponseRegister.status=="error"){
this.AlertRegister("Pendaftaran user tidak berhasil, silahkan coba lagi.");
loading.dismiss();
}else{
loading.dismiss();
this.modalController.dismiss();
}
loading.dismiss();
},
error => {
loading.dismiss();
}
);
}
dismissRegister() {
this.modalController.dismiss();
}
async AlertRegister(Message) {
const alert = await this.alertController.create({
header: 'Peringatan!',
//subHeader: 'Subtitle',
message: Message,
buttons: ['OK']
});
await alert.present();
}
}
Buat tampilan form register di register.page.html,
<ion-header>
<ion-toolbar color="primary">
<ion-title class="ion-text-left">Register</ion-title>
<ion-buttons slot="end">
<ion-button (click)="dismissRegister()">Close</ion-button>
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<form [formGroup]="FormRegister">
<ion-item>
<ion-label color="primary" position="floating">Username</ion-label>
<ion-input type="text" name="Username" formControlName="Username"></ion-input>
</ion-item>
<div class="error-container">
<ng-container *ngFor="let validation of validations.Username">
<div class="error-message" *ngIf="FormRegister.get('Username').hasError(validation.type) && (FormRegister.get('Username').dirty || FormRegister.get('Username').touched)">
<span>{{ validation.message }}</span>
<br><br>
</div>
</ng-container>
</div>
<ion-item>
<ion-label color="primary" position="floating">Password</ion-label>
<ion-input type="text" name="Password" formControlName="Password" *ngIf="showPasswordText" ></ion-input>
<ion-input type="password" name="Password" formControlName="Password" *ngIf="!showPasswordText" ></ion-input>
<ion-icon color="dark" slot="icon-only" (click)="showPasswordText = !showPasswordText" *ngIf="showPasswordText" name="eye" slot="end" class="ion-align-self-center"></ion-icon>
<ion-icon color="dark" slot="icon-only" (click)="showPasswordText = !showPasswordText" *ngIf="!showPasswordText" name="eye-off" slot="end" class="ion-align-self-center"></ion-icon>
</ion-item>
<div class="error-container">
<ng-container *ngFor="let validation of validations.Password">
<div class="error-message" *ngIf="FormRegister.get('Password').hasError(validation.type) && (FormRegister.get('Password').dirty || FormRegister.get('Password').touched)">
<span>{{ validation.message }}</span>
<br><br>
</div>
</ng-container>
</div>
<ion-item>
<ion-label color="primary" position="floating">Nama Lengkap</ion-label>
<ion-input type="text" name="NamaLengkap" formControlName="NamaLengkap"></ion-input>
</ion-item>
<div class="error-container">
<ng-container *ngFor="let validation of validations.NamaLengkap">
<div class="error-message" *ngIf="FormRegister.get('NamaLengkap').hasError(validation.type) && (FormRegister.get('NamaLengkap').dirty || FormRegister.get('NamaLengkap').touched)">
<span>{{ validation.message }}</span>
<br><br>
</div>
</ng-container>
</div>
<ion-item>
<ion-label color="primary" position="floating">Email</ion-label>
<ion-input type="email" name="Email" formControlName="Email"></ion-input>
</ion-item>
<div class="error-container">
<ng-container *ngFor="let validation of validations.Email">
<div class="error-message" *ngIf="FormRegister.get('Email').hasError(validation.type) && (FormRegister.get('Email').dirty || FormRegister.get('Email').touched)">
<span>{{ validation.message }}</span>
<br><br>
</div>
</ng-container>
</div>
<ion-item>
<ion-label color="primary" position="floating">No. Hp</ion-label>
<ion-input type="text" name="NoHp" formControlName="NoHp"></ion-input>
</ion-item>
<div class="error-container">
<ng-container *ngFor="let validation of validations.NoHp">
<div class="error-message" *ngIf="FormRegister.get('NoHp').hasError(validation.type) && (FormRegister.get('NoHp').dirty || FormRegister.get('NoHp').touched)">
<span>{{ validation.message }}</span>
<br><br>
</div>
</ng-container>
</div><br>
<ion-button expand="full" color="danger" (click)="Register()" [disabled]="!FormRegister.valid">Register</ion-button>
</form>
</ion-content>
Tambahkan css di register.page.scss.
ion-item {
border-radius: 10px !important;
//padding-left: 30px !important;
font-size: 0.9em;
margin-bottom: 4px;
border: 1px solid rgba(34, 34, 34, 0.096);
border-bottom: 0px !important;
//box-shadow: none !important;
background-color: #ffffff;
-webkit-box-shadow: 4px 8px 6px -6px #222;
-moz-box-shadow: 4px 8px 6px -6px #222;
box-shadow: 4px 4px 6px -6px #222;
}
.error-container{
color: red;
font-size: 0.8em;
}
Tampilan halaman register.
Uji coba aplikasi.
Source code:
Ionic4_Login_Register.rar