Personal Expense Management Application using Ionic 4
Hello Viewers,
Let's start with this project.
Install node.js in your system
Go to link - https://nodejs.org/en/ and install the latest version of ndoe.js
After installing,
Install Ionic in your system
Open Command Prompt and run below command.
Run below command before going ahead.
You need a SQL script file to create a database and table.
Open your application in file explorer and create inputCredit.sql in assets directory as below.
sqlLite\src\assets\inputCredit.sql
Update sqlLite\src\assets\inputCredit.sql file with below details:
Update the sqlLite\src\app\app.module.ts file with the below entries.
So now you just need to run this code and you will get your required output, please let me know in case of any issue.
Youtube video link for your reference:
In this tutorial, we are going to see Personal Expense Management Application using Ionic 4, where the database will be SQLite.
Let's start with this project.
Install node.js in your system
Go to link - https://nodejs.org/en/ and install the latest version of ndoe.js
After installing,
Check npm is working properly using the Command Prompt -
C:\Users\admin>npm -version 6.10.2 |
Install Ionic in your system
Open Command Prompt and run below command.
npm install -g ionic |
Run below command before going ahead.
ionic start sqlliteApp sidemenu cd sqlliteApp ionic g service services/database ionic g page pages/credit npm install @ionic-native/sqlite @ionic-native/sqlite-porter ionic cordova plugin add cordova-sqlite-storage ionic cordova plugin add uk.co.workingedge.cordova.plugin.sqliteporter |
You need a SQL script file to create a database and table.
Open your application in file explorer and create inputCredit.sql in assets directory as below.
sqlLite\src\assets\inputCredit.sql
Update sqlLite\src\assets\inputCredit.sql file with below details:
CREATE TABLE IF NOT EXISTS Credit_Details(id INTEGER PRIMARY KEY AUTOINCREMENT,card_name TEXT,bank_name TEXT,expensed TEXT, left_money TEXT , card_limit TEXT); CREATE TABLE IF NOT EXISTS Credit_Expense_Transaction(id INTEGER PRIMARY KEY AUTOINCREMENT,transaction_details TEXT,date_of_transaction TEXT,amount TEXT,card_id INTEGER); |
Update the sqlLite\src\app\app.module.ts file with the below entries.
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 { SQLitePorter } from '@ionic-native/sqlite-porter/ngx'; import { SQLite } from '@ionic-native/sqlite/ngx'; import { HttpClientModule } from '@angular/common/http'; @NgModule({ declarations: [AppComponent], entryComponents: [], imports: [ BrowserModule, IonicModule.forRoot(), AppRoutingModule, HttpClientModule ], providers: [ StatusBar, SplashScreen, { provide: RouteReuseStrategy, useClass: IonicRouteStrategy }, SQLite, SQLitePorter ], bootstrap: [AppComponent] }) export class AppModule {} |
Update the sqlLite\src\app\app.component.ts file with the below entries.
import { Component } from '@angular/core'; import { Platform } from '@ionic/angular'; import { SplashScreen } from '@ionic-native/splash-screen/ngx'; import { StatusBar } from '@ionic-native/status-bar/ngx'; @Component({ selector: 'app-root', templateUrl: 'app.component.html', styleUrls: ['app.component.scss'] }) export class AppComponent { public appPages = [ { title: 'Transactions', url: '/credit', icon: 'home' }, { title: 'Reset', url: '/home', icon: 'aperture' } ]; constructor( private platform: Platform, private splashScreen: SplashScreen, private statusBar: StatusBar ) { this.initializeApp(); } initializeApp() { this.platform.ready().then(() => { this.statusBar.styleDefault(); this.splashScreen.hide(); }); } } |
Update the sqlLite\src\app\app-routing.module.ts file with the below entries.
import { NgModule } from '@angular/core'; import { PreloadAllModules, RouterModule, Routes } from '@angular/router'; const routes: Routes = [ { path: '', redirectTo: 'credit', pathMatch: 'full' }, { path: 'home', loadChildren: () => import('./home/home.module').then(m => m.HomePageModule) }, { path: 'credit', loadChildren: () => import ('./pages/credit/credit.module').then(m => m.CreditPageModule) } ]; @NgModule({ imports: [ RouterModule.forRoot(routes, { preloadingStrategy: PreloadAllModules }) ], exports: [RouterModule] }) export class AppRoutingModule {} |
Update the sqlLite\src\app\services\database.service.ts file with the below entries.
import { Platform } from '@ionic/angular'; import { Injectable } from '@angular/core'; import { SQLitePorter } from '@ionic-native/sqlite-porter/ngx'; import { HttpClient } from '@angular/common/http'; import { SQLite, SQLiteObject } from '@ionic-native/sqlite/ngx'; import { BehaviorSubject, Observable } from 'rxjs'; export interface CreditData { id: number; card_name: string; bank_name: string; expensed: string; left_money: string; card_limit: string; } @Injectable({ providedIn: 'root' }) export class DatabaseService { private database: SQLiteObject; private dbReady: BehaviorSubject<boolean> = new BehaviorSubject(false); creditsData = new BehaviorSubject([]); creditTransactions = new BehaviorSubject([]); expensedMoney: any ; leftMoney: any ; actualLimit: any; constructor(private plt: Platform, private sqlitePorter: SQLitePorter, private sqlite: SQLite, private http: HttpClient) { this.plt.ready().then(() => { this.sqlite.create({ name: 'Credit.db', location: 'default' }) .then((db: SQLiteObject) => { this.database = db; this.seedDatabase(); }); }); } seedDatabase() { this.http.get('assets/inputCredit.sql', { responseType: 'text'}) .subscribe(sql => { this.sqlitePorter.importSqlToDb(this.database, sql) .then(_ => { this.loadCreditDetails(); this.loadCreditTransaction(); this.dbReady.next(true); }) .catch(e => console.error(e)); }); } getDatabaseState() { return this.dbReady.asObservable(); } getCreditData(): Observable<CreditData[]> { return this.creditsData.asObservable(); } getCreditTransactions(): Observable<any[]> { return this.creditTransactions.asObservable(); } loadCreditDetails() { return this.database.executeSql('SELECT * FROM Credit_Details', []).then(data => { let creditsData: CreditData[] = []; console.log('loading data for Credit Card'); if (data.rows.length > 0) { for (let i = 0; i < data.rows.length; i++) { creditsData.push({ id: data.rows.item(i).id, card_name: data.rows.item(i).card_name, bank_name: data.rows.item(i).bank_name, expensed: data.rows.item(i).expensed, left_money: data.rows.item(i).left_money, card_limit: data.rows.item(i).card_limit }); } } this.creditsData.next(creditsData); }); } loadCreditTransaction() { let query = 'SELECT ct.id, ct.transaction_details, ct.date_of_transaction, ct.amount, cd.card_name, cd.bank_name from Credit_Expense_Transaction ct JOIN Credit_Details cd ON ct.card_id=cd.id'; return this.database.executeSql(query, []).then(data => { let creditTransactions = []; this.loadCreditDetails(); if (data.rows.length > 0) { for (let i = 0; i < data.rows.length; i++) { creditTransactions.push({ id: data.rows.item(i).id, transaction_details: data.rows.item(i).transaction_details, date_of_transaction: data.rows.item(i).date_of_transaction, amount: data.rows.item(i).amount, card_name: data.rows.item(i).bank_name + ' ' + data.rows.item(i).card_name }); } } this.creditTransactions.next(creditTransactions); }); } addCreditDetail(cardName, bankName, expensed, leftMoney, cardLimit) { let data = [cardName, bankName, expensed, leftMoney, cardLimit]; return this.database.executeSql('INSERT INTO Credit_Details (card_name,bank_name,expensed, left_money, card_limit) VALUES (?, ?, ?, ?, ?)', data).then(data => { this.loadCreditDetails(); }); } addCreditTransactions(transactionDetails, dateOfTransaction, amount, cardId) { let data = [transactionDetails, dateOfTransaction, amount, cardId]; return this.database.executeSql('INSERT INTO Credit_Expense_Transaction (transaction_details, date_of_transaction, amount, card_id) VALUES (?, ?, ?, ?)', data).then(data => { this.updateCreditData(amount, cardId); }); } updateCreditData(amount, cardId) { this.leftMoney = Number.parseInt(this.leftMoney.toString()) - Number.parseInt(amount.toString()); this.expensedMoney = Number.parseInt(this.expensedMoney.toString()) + Number.parseInt(amount.toString()); let data = [this.expensedMoney, this.leftMoney]; return this.database.executeSql(`UPDATE Credit_Details SET expensed = ?, left_money = ? WHERE id = ${cardId}`, data).then(data => { this.loadCreditTransaction(); }); } getExpensedAndLeftMoney(cardId) { return this.database.executeSql('SELECT * FROM Credit_Details WHERE id = ?', [cardId]).then(data => { this.expensedMoney = data.rows.item(0).expensed; this.leftMoney = data.rows.item(0).left_money; }); } // Adding logic for reseting limit getLimitbyId(id) { return this.database.executeSql('SELECT * FROM Credit_Details WHERE id = ?', [id]).then(data => { console.log('Data inside select ' + data); this.actualLimit = data.rows.item(0).card_limit; }); } resetCreditLimit(id) { this.actualLimit = Number.parseInt(this.actualLimit.toString()); let data = [0, this.actualLimit]; return this.database.executeSql(`UPDATE Credit_Details SET expensed = ?, left_money = ? WHERE id = ${id}`, data).then(data => { this.loadCreditTransaction(); }); } } |
Update the sqlLite\src\app\home\home.page.html file with the below entries.
<ion-header> <ion-toolbar color="primary"> <ion-buttons slot="start"> <ion-menu-button></ion-menu-button> </ion-buttons> <ion-title> Reset Card Balance </ion-title> </ion-toolbar> </ion-header> <ion-content class="ion-padding"> <ion-list> <ion-item *ngFor="let cd of creditData | async"> <ion-label class="ion-text-wrap"> <h2>{{ cd.bank_name }} {{ cd.card_name }} with Remaining Limit {{ cd.left_money }}</h2> </ion-label> <ion-button expand="block" (click)="resetLimit(cd.id)">Reset Card</ion-button> </ion-item> </ion-list> </ion-content> |
Update the sqlLite\src\app\home\home.page.ts file with the below entries.
import { DatabaseService} from './../services/database.service'; import { Component, OnInit } from '@angular/core'; import { ToastController} from '@ionic/angular'; import { Observable } from 'rxjs'; @Component({ selector: 'app-home', templateUrl: 'home.page.html', styleUrls: ['home.page.scss'], }) export class HomePage implements OnInit { creditData: Observable<any[]>; transHistory: Observable<any[]>; transferData = {}; constructor(private db: DatabaseService, private toast: ToastController) { } ngOnInit() { this.db.getDatabaseState().subscribe(rdy => { if (rdy) { this.creditData = this.db.getCreditData(); } }); } resetLimit(id: any) { this.db.getLimitbyId(id).then( _ => { this.db.resetCreditLimit(id); }).then(async (res) => { let toast = await this.toast.create({ message: 'Card details reseted successfully.', duration: 3000 }); toast.present(); }); } } |
Update the sqlLite\src\app\pages\credit\credit.page.html file with the below entries.
<ion-header> <ion-toolbar color="primary"> <ion-buttons slot="start"> <ion-menu-button></ion-menu-button> </ion-buttons> <ion-title> Credit Transactions </ion-title> </ion-toolbar> </ion-header> <ion-content class="ion-padding"> <ion-segment [(ngModel)]="selectedView"> <ion-segment-button value="creditDetails"> <ion-label>Dashboard</ion-label> </ion-segment-button> <ion-segment-button value="creditTrans"> <ion-label>Transactions</ion-label> </ion-segment-button> </ion-segment> <div [ngSwitch]="selectedView"> <div *ngSwitchCase="'creditDetails'"> <ion-item> <ion-label position="stacked">Bank Name</ion-label> <ion-input [(ngModel)]="indCreditData.bankName" placeholder="Your Card Bank"></ion-input> </ion-item> <ion-item> <ion-label position="stacked">Card Name</ion-label> <ion-input [(ngModel)]="indCreditData.cardName" placeholder="Your Card Name"></ion-input> </ion-item> <ion-item> <ion-label position="stacked">Card Initial Limit</ion-label> <ion-input [(ngModel)]="indCreditData.cardLimit" placeholder="Card Limit"></ion-input> </ion-item> <ion-button expand="block" (click)="addCardDetails()">Add Card</ion-button> <ion-list> <ion-item button *ngFor="let cd of creditsData" [routerLink]="['/', 'developers', cd.id]"> <ion-label class="ion-text-wrap"> <h2>{{ cd.bank_name }} {{ cd.card_name }} with Remaining Limit {{ cd.left_money }}</h2> <p>Expensed Money :: {{ cd.expensed }}</p> <p>Actual Limit :: {{ cd.card_limit }}</p> </ion-label> </ion-item> </ion-list> </div> <div *ngSwitchCase="'creditTrans'"> <ion-item> <ion-label position="stacked">Transactions Details</ion-label> <ion-input [(ngModel)]="indCreditTrans.transactionDetails" placeholder="Details"></ion-input> </ion-item> <ion-item> <ion-label position="stacked">Transactions Amount</ion-label> <ion-input [(ngModel)]="indCreditTrans.amount" placeholder="Amount"></ion-input> </ion-item> <ion-item> <ion-label position="stacked">Transactions Date</ion-label> <ion-input [(ngModel)]="indCreditTrans.dateOfTransaction" placeholder="Date for Transaction"></ion-input> </ion-item> <ion-item> <ion-label position="stacked">Select Card</ion-label> <ion-select [(ngModel)]="indCreditTrans.cardId"> <ion-select-option *ngFor="let cd of creditsData" [value]="cd.id">{{ cd.bank_name }} {{ cd.card_name }}</ion-select-option> </ion-select> </ion-item> <ion-button expand="block" (click)="addCreditTrans()">Add Transaction</ion-button> <ion-list> <ion-item *ngFor="let ct of creditTrans | async"> <ion-label class="ion-text-wrap"> <h2>Amount : {{ ct.amount }} from {{ ct.card_name }}</h2> <p>Reason : {{ ct.transaction_details }} on {{ ct.date_of_transaction }}</p> </ion-label> </ion-item> </ion-list> </div> </div> </ion-content> |
Update the sqlLite\src\app\pages\credit\credit.page.ts file with the below entries.
import { DatabaseService, CreditData } from './../../services/database.service'; import { Component, OnInit } from '@angular/core'; import { ToastController} from '@ionic/angular'; import { Observable } from 'rxjs'; @Component({ selector: 'app-credit', templateUrl: './credit.page.html', styleUrls: ['./credit.page.scss'], }) export class CreditPage implements OnInit { creditsData: CreditData[] = []; creditTrans: Observable<any[]>; indCreditData = {}; indCreditTrans = {}; selectedView = 'creditDetails'; constructor(private db: DatabaseService, private toast: ToastController) { } ngOnInit() { this.db.getDatabaseState().subscribe(rdy => { if (rdy) { this.db.getCreditData().subscribe(cd => { this.creditsData = cd; }); this.creditTrans = this.db.getCreditTransactions(); } }); } addCardDetails() { this.db.addCreditDetail(this.indCreditData['cardName'], this.indCreditData['bankName'], 0, this.indCreditData['cardLimit'], this.indCreditData['cardLimit']).then(_ => { this.indCreditData = {}; }).then(async (res) => { let toast = await this.toast.create({ message: 'Card added successfully.', duration: 3000 }); toast.present(); }); } addCreditTrans() { this.db.getExpensedAndLeftMoney(this.indCreditTrans['cardId']); this.db.addCreditTransactions(this.indCreditTrans['transactionDetails'], this.indCreditTrans['dateOfTransaction'], this.indCreditTrans['amount'], this.indCreditTrans['cardId']).then(_ => { this.indCreditTrans = {}; }).then(async (res) => { let toast = await this.toast.create({ message: 'Transaction details added successfully.', duration: 3000 }); toast.present(); }); } } |
Run below command to make apk file, and run in your mobile.
ionic cordova platform add android
ionic cordova build android |
So now you just need to run this code and you will get your required output, please let me know in case of any issue.
Youtube video link for your reference:
Part 1:
Part 2:
Part 3:
Wonderful blog. Thank you for sharing such valuable information
ReplyDeleteGroup discussion techniques
How do you start a group discussion