Getting Started | Building an Application with Ionic
In this part 2 of gestion stock project we will buid an application with Ionic framewotk
We will create an application to stock the product of clients, The user can login and reserve a date then he adds the products and modify or delete his command, product or his account
Ionic apps are created and developed primarily through the Ionic command line utility (the “CLI”), and use Cordova to build/deploy as a native app. This means we need to install a few utilities to get developing.
Installing Ionic
Ionic apps are created and developed primarily through the Ionic command-line utility. The Ionic CLI is the preferred method of installation, as it offers a wide range of dev tools and help options along the way. It is also the main tool through which to run the app and connect it to other services, such as Ionic Appflow.
Before proceeding, make sure your computer has Node.js installed.
Install the Ionic CLI with npm:
$ npm install -g @ionic/cli
Starting an App
Starting a new Ionic app is incredibly simple. From the command line, run the ionic start command and the CLI will handle the rest.
Here, myApp is the name of the project, blank is the starter template, and the project type is angular.
$ ionic start app-ionic
Project structure
Let's go through the files and folders of the app.
/ src
Inside of the /src directory we find our raw, uncompiled code. This is where most of the work for your Ionic app will take place.
/ app
The App folder is the largest folder because it contains all the code of our ionic app. It has all the components, modules, pages, services and styles you will use to build your app.
This is the core of the project. Let’s have a look at the structure of this folder so you get an idea where to find things and where to add your own modules to adapt this project to your particular needs.
Model
Create a folder model in side it add a class Model.ts
export class Command {
comment: string;
createAt: Date;
id: number;
products: Product[];
}
export class Product {
description: string;
id: number;
name: string;
photo: string;
price: number;
quantity: number;
}
export class User {
commands: Command[];
id: number;
username: string;
}
Angular Services
Angular services are singleton objects that get instantiated only once during the lifetime of an application. They contain methods that maintain data throughout the life of an application, i.e. data does not get refreshed and is available all the time. The main objective of a service is to organize and share business logic, models, or data and functions with different components of an Angular application.
An example of when to use services would be to transfer data from one controller to another custom service.
Why Should We Use Services in Angular? The separation of concerns is the main reason why Angular services came into existence. An Angular service is a stateless object and provides some very useful functions. These functions can be invoked from any component of Angular, like Controllers, Directives, etc. This helps in dividing the web application into small, different logical units which can be reused.
Create three services in the service package
$ ionic generate service service/UserService
$ ionic generate service service/CommandService
$ ionic generate service service/ProductService
- UserService.ts
import {Injectable} from '@angular/core';
import {HttpClient} from '@angular/common/http';
import {Observable} from 'rxjs';
import {User} from '../model/Model';
@Injectable({
providedIn: 'root'
})
export class UserService {
private url = 'http://localhost:8080/api';
constructor(private http: HttpClient) { }
saveUser(user: User): Observable<User> {
return this.http.post<User>(`http://localhost:8080/api/saveUser`, user);
}
getUserByUserId(id: number): Observable<User> {
return this.http.get<User>(`${this.url}/getUserByUserId/${id}`);
}
updateUser(user: User, id: number): Observable<User> {
return this.http.put<User>(`http://localhost:8080/api/updateUser/${id}`, user);
}
deleteUser(id: number): Observable<any> {
return this.http.delete<any>(`${this.url}/deleteUser/${id}`);
}
}
- CommandService.ts
import {Injectable} from '@angular/core';
import {HttpClient} from '@angular/common/http';
import {Observable} from 'rxjs';
import {Command} from '../model/Model';
@Injectable({
providedIn: 'root'
})
export class CommandService {
private url = 'http://localhost:8080/api';
constructor(private http: HttpClient) { }
saveCommand(command: Command): Observable<Command> {
return this.http.post<Command>(`http://localhost:8080/api/saveCommand`, command);
}
findCommandById(id: number): Observable<Command> {
return this.http.get<Command>(`${this.url}/findCommandById/${id}`);
}
updateCommand(command: Command, id: number): Observable<Command> {
return this.http.put<Command>(`http://localhost:8080/api/updateCommand/${id}`, command);
}
deleteCommand(id: number): Observable<any> {
return this.http.delete<any>(`${this.url}/deleteCommand/${id}`);
}
findAllCommandForUser(idUser: number): Observable <Command[]> {
return this.http.get<Command[]>(`${this.url}/findAllCommandForUser/${idUser}`);
}
}
- ProductService.ts
import {Injectable} from '@angular/core';
import {HttpClient} from '@angular/common/http';
import {Observable} from 'rxjs';
import {Product} from '../model/Model';
@Injectable({
providedIn: 'root'
})
export class CommandService {
private url = 'http://localhost:8080/api';
constructor(private http: HttpClient) { }
saveProduct(product: Product, id: number): Observable<Product> {
return this.http.post<Product>(`http://localhost:8080/api/saveProduct/${id}`, product);
}
findProductById(id: number): Observable<Product> {
return this.http.get<Product>(`${this.url}/findProductById/${id}`);
}
updateProduct(product: Product, id: number): Observable<Product> {
return this.http.put<Product>(`http://localhost:8080/api/updateProduct/${id}`, product);
}
deleteProduct(id: number): Observable<any> {
return this.http.delete<any>(`${this.url}/deleteProduct/${id}`);
}
}
Adding pages
Now we'll continue with the project by adding pages which are the basic buildings of an Ionic app. So let's get started.
You can create pages either manually or generating them using the Ionic CLI v5 which is the recommended method.
In this guide we'll look first at how to create a page generate it with the Ionic CLI, then how to add it to the project.
Go ahead and open your terminal or command prompt and follow the instructions:
Create these pages
$ ionic generate page add-command
$ ionic generate page add-product
$ ionic generate page display-product
$ ionic generate page edit-command
$ ionic generate page edit-product
$ ionic generate page edit-user
$ ionic generate page login
- login.page.html
<ion-header>
<ion-toolbar color="secondary">
<ion-card>
<ion-item color="secondary">
<ion-icon name="archive" slot="start"></ion-icon>
<ion-label>Login to access gestion stock</ion-label>
</ion-item>
</ion-card>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<ion-card color="light">
<ion-card-header>
<ion-card-title>
<ion-icon name="log-in"></ion-icon> Please full the fields
</ion-card-title>
</ion-card-header>
<ion-card-content>
<div class="spinner" *ngIf="showSpinner">
<ion-spinner name="bubbles" color="danger"></ion-spinner>
</div>
<hr style="border: 1px solid #eee; height: 0">
<ion-list class="ion-list-border">
<ion-item>
<ion-label position="floating">Username</ion-label>
<ion-input type="text" [(ngModel)]="user.username"></ion-input>
</ion-item>
<ion-item>
<ion-row>
<ion-col>
<ion-button expand="block" color="secondary"
(click)="onSubmit()">Login</ion-button>
</ion-col>
</ion-row>
</ion-item>
</ion-list>
</ion-card-content>
</ion-card>
</ion-content>
<ion-footer>
<ion-card>
<ion-card-content>
<ion-img
src="https://www.economie-gestion.com/wp-content/uploads/2018/10/gestion-de-stocks-inventarios-1-728.jpg">
</ion-img>
</ion-card-content>
</ion-card>
<ion-toolbar color="danger" class="ion-text-center">
<ion-label>All copy tright resved by Drmas</ion-label>
</ion-toolbar>
</ion-footer>
- login.page.ts
import { User } from './../model/Model';
import { Component, OnInit } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { ToastController } from '@ionic/angular';
import { UserService } from '../service/UserService';
@Component({
selector: 'app-login',
templateUrl: './login.page.html',
styleUrls: ['./login.page.scss'],
})
export class LoginPage implements OnInit {
showSpinner = false;
user: User = new User();
constructor(private router: Router, private toastController: ToastController,
private route: ActivatedRoute, private userService: UserService) { }
ngOnInit(): void {}
onSubmit() {
this.showSpinner = true;
this.userService.saveUser(this.user).subscribe(user => {
this.user = user;
this.presentToast();
this.router.navigate(['/home/', user.id], {relativeTo: this.route});
this.showSpinner = false;
});
}
async presentToast() {
const toast = await this.toastController.create({
message: 'Login successfully',
duration: 2000
});
toast.present();
}
}
- home.page.html
<ion-content>
<div class="header">
<h2>Gestion de stock </h2>
<div class="space-between">
<div class="followings">
<ion-badge color="warning" mode="ios">{{commandLength}}</ion-badge>
<p>Commands</p>
</div>
<div class="followings">
<ion-badge color="warning" mode="ios"><ion-icon name="heart"></ion-icon></ion-badge>
<p>{{user.username}}</p>
</div>
</div>
</div>
<div class="flex">
<div class="border-blue">
<div class="border-white">
<div class="img-box">
<img src="https://img.over-blog-kiwi.com/0/87/20/59/20190804/ob_f940bb_8bf3477e-e55c-4f56-ab33-59d3380f899e.jpeg" alt="">
</div>
</div>
</div>
</div>
<ion-row>
<ion-col class="ion-text-center"> <ion-button fill="outline" size="small" color="dark" (click)="addCommand(id)">
Create a new command <ion-icon name="create-outline" slot="end"></ion-icon></ion-button> </ion-col>
</ion-row>
<div class="class-section">
<ion-list-header color="light ion-padding">
<ion-text>
<p>Commands details</p>
</ion-text>
</ion-list-header>
<div *ngIf="commandLength==0" class="ion-text-center">
<ion-img class="imgCommand" src="http://www.transagrue.com/public/img/big/entreposage534ba38a55af4.jpg"></ion-img>
<ion-text color="danger"> You don't have command </ion-text>
</div>
<ion-list *ngFor="let CM of commands; let i = index;" class="accordion-list" lines="none" detail="false" no-padding>
<ion-item tappable (click)="toggleSection(i)" [ngClass]="{'section-active': CM.open, 'section': !CM.open}">
<ion-icon slot="start" name="arrow-down-circle-outline" *ngIf="CM.open"> </ion-icon>
<ion-icon slot="start" name="arrow-forward-circle-outline" *ngIf="!CM.open"> </ion-icon>
<ion-label> Stocked = {{CM.createAt | date}} </ion-label>
</ion-item>
<ion-item-divider color="light" *ngIf="CM.open">
<ion-row>
<ion-col size="8">
<ion-text color="dark">{{CM.comment}}</ion-text>
</ion-col>
<ion-col size="4">
<ion-button fill="clear" color="dark" title="Edit command" (click)="updateCommand(CM.id)">
<ion-icon size="meduim" name="create-sharp"></ion-icon>
</ion-button>
<ion-button fill="clear" color="danger" title="Delete command" (click)="deleteCommand(CM.id)">
<ion-icon size="meduim" name="backspace"></ion-icon>
</ion-button>
</ion-col>
</ion-row>
</ion-item-divider>
<div *ngIf="CM.products && CM.open">
<ion-item-divider *ngIf="CM.products.length > 0 && CM.open">
<ion-label>
<ion-icon name="menu"></ion-icon>
Products:
</ion-label>
<ion-button slot="end" size="small" fill="clear" (click)="addProduct(CM.id)">
Add
</ion-button>
</ion-item-divider>
<ion-list *ngFor="let Pro of CM.products; let j = index;" lines="none" class="child-list">
<ion-item tappable (click)="toggleItem(i, j)"
[ngClass]="{'child-active': Pro.open, 'child': !Pro.open}">
<ion-icon name="caret-down-outline" *ngIf="Pro.open"></ion-icon>
<ion-icon name="caret-forward-outline" *ngIf="!Pro.open"></ion-icon>
<ion-label class="product-name">
{{Pro.name}}
</ion-label>
</ion-item>
<ion-list *ngIf="CM.products && Pro.open" class="product-list" lines="none">
<ion-item class="display-product">
<ion-thumbnail slot="start" (click)="displayProduct(Pro.id)" title="More info" tappable>
<img src="{{Pro.photo}}">
</ion-thumbnail>
<ion-label>
<h2>Quantity: {{Pro.quantity}}</h2>
<h3>Price: {{Pro.price}}</h3>
<ion-button size="small" fill="clear" (click)="editProduct(Pro.id)">
<ion-icon name="pencil-outline" slot="start"></ion-icon> Edit
</ion-button>
<ion-button size="small" fill="clear" (click)="deleteProduct(Pro.id)">
<ion-icon name="trash-outline" slot="start"></ion-icon>Delete
</ion-button>
</ion-label>
</ion-item>
</ion-list>
</ion-list>
</div>
<div *ngIf="CM.products.length == 0 && CM.open" class="ion-padding">
<ion-item>
<ion-text color="danger">Sorry, No any product command here</ion-text>
<ion-button size="small" fill="clear" title="Edit product" (click)="addProduct(CM.id)">
<ion-icon name="add-circle-outline"></ion-icon>
</ion-button>
</ion-item>
</div>
</ion-list>
</div>
<br>
</ion-content>
<ion-footer class="ion-text-center ion-no-padding">
<ion-toolbar color="warning">
<ion-row>
<ion-col>
<ion-button color="primary" size="small" (click)="editUSer(user.id)">Update
<ion-icon name="create-outline" slot="end"></ion-icon></ion-button>
</ion-col>
<ion-col>
<ion-button color="danger" size="small" (click)="presentAlertConfirmDeleteUser(user.id)">Delete
<ion-icon name="close-outline" slot="end"></ion-icon></ion-button>
</ion-col>
<ion-col>
<ion-button color="medium" size="small" (click)="logout()">Logout
<ion-icon name="refresh-circle-outline" slot="end"></ion-icon></ion-button>
</ion-col>
</ion-row>
</ion-toolbar>
</ion-footer>
- home.page.scss
.header {
background: #1B9CFC;
height: 180px;
padding-top: 1px ;
}
.header h2 {
color: #ffffff;
font-weight: bold;
text-align: center;
}
.header .space-between {
display: flex;
justify-content: space-between;
padding: 10px 10px 0 10px;
}
.header .followings {
display: flex;
flex-direction: column;
align-items: center;
}
.header .followings p {
color: #ffffff;
margin: 8px 0 0 0;
}
.img-box {
height: 130px;
width: 130px;
border-radius: 50%;
overflow: hidden;
}
.border-white {
border: 4px solid #ffffff;
border-radius: 50%;
width: fit-content;
}
.border-blue {
border: 7px solid #34ace0;
border-radius: 50%;
width: fit-content;
}
.flex {
display: flex;
justify-content: center;
margin-top: -76px;
}
.tweet {
margin-top: -10px;
}
.tweet ion-label {
line-height: 1 !important;
font-size: 15px;
margin: 6px 0 !important;
min-height: 34px;
}
.tweet-img {
width: 95%;
margin-left: 2.5%;
border-radius: 10px;
}
.accordoin-list {
padding-top: 0 !important;
}
.class-section .accordoin-list-class {
--ion-item-background: #ffffff;
.section, .section-active {
--min-height: 58px;
}
}
.class-section .section {
--ion-item-background: #ffffff;
--ion-item-color: #000000;
border-top: 2px solid #dfe4ea;
padding: 6px 0;
}
.class-section .section-active {
--ion-item-background: #ffc400;
--ion-item-color: #ffffff;
font-weight: 600;
ion-icon {
color: #ffffff;
}
}
.class-section .child-list {
padding: 0;
margin: 0;
.child, .child-active {
margin-bottom: 2px;
}
}
.class-section .child {
--ion-item-background: #e8e7e6;
--ion-item-color: #000000;
}
.class-section .child-active {
--ion-item-background: #fad86b;
--ion-item-color: #ffffff;
ion-icon {
color: #ffffff;
}
}
.class-section .padding-list {
padding: 0;
margin: 0;
}
.product-name {
margin-left: 18px;
font-size: 18px;
font-family: initial;
}
.imgCommand {
height: 150px;
}
- home.page.ts
import { DisplayProductPage } from './../display-product/display-product.page';
import { EditProductPage } from './../edit-product/edit-product.page';
import { ProductService } from './../services/product.service';
import { EditCommandPage } from './../edit-command/edit-command.page';
import { async } from '@angular/core/testing';
import { Product } from './../model/rest.d';
import { AddCommandPage } from './../add-command/add-command.page';
import { EditUserPage } from './../edit-user/edit-user.page';
import { Component } from '@angular/core';
import { Command, User } from '../model/Model';
import { ActivatedRoute, Router } from '@angular/router';
import { TokenStorageService } from '../services/TokenStorageService';
import { AlertController, ModalController, ToastController } from '@ionic/angular';
import { AuthService } from '../services/AuthService';
import { CommandService } from '../services/command-service';
import { AddProductPage } from '../add-product/add-product.page';
import { element } from 'protractor';
@Component({
selector: 'app-home',
templateUrl: 'home.page.html',
styleUrls: ['home.page.scss'],
})
export class HomePage {
username: string;
info: User = new User();
showSpinner = false;
command: Command = new Command();
commands: any[] = [];
products: any[];
commandDetails: Command[];
commandLength: number;
productLength: number;
constructor(private route: ActivatedRoute,
private tokenStorageService: TokenStorageService,
private alertController: AlertController,
private router: Router,
private modalCtrl: ModalController,
private authService: AuthService,
private toastController: ToastController,
private commandService: CommandService,
private productService: ProductService) {
this.route.params.subscribe(
params => {
this.username = this.route.snapshot.params.username;
this.authService.getUSerByUsername(this.username).subscribe(info => {
this.info = info;
this.commandService.findAllCommandForUser(info.id).subscribe(commands => {
this.commands = commands;
this.commandLength = this.commands.length;
// this.commands[0].open = true;
})
});
}
);
}
ngOnInit(): void {}
async editUSer(username) {
const modal = await this.modalCtrl.create({
component: EditUserPage,
componentProps: {
username,
},
cssClass: 'cart-modal',
});
modal.present();
}
logout() {
this. tokenStorageService.signOut();
this. router.navigateByUrl('/login');
this. showSpinner = false;
}
async presentAlertConfirmDeleteUser(idUser) {
const alert = await this.alertController.create ({
header: 'Confirm!',
message: 'Are you sure to delete it',
buttons: [
{
text: 'Cancel',
role: 'cancel',
cssClass: 'secondary',
handler: (blah) => {
console.log('Confirm Cancel: blah');
}
}, {
text: 'Okay',
handler: () => {
this.authService.deleteUser(idUser).subscribe(async () => {
const toast = await this.toastController.create({
message: 'Register have been deleted successfully.',
duration: 2000
});
toast.present();
this.logout();
});
}
}
]
});
await alert.present();
}
async addCommand() {
const modal = await this.modalCtrl.create({
component: AddCommandPage,
cssClass: 'add-command-modal',
});
modal.present();
}
toggleSection(index: any) {
this.commands[index].open = !this.commands[index].open;
}
toggleItem(index: any, childIndex: any) {
this.commands[index].products[childIndex].open = !this.commands[index].products[childIndex].open;
}
async deleteCommand(id) {
const alert = await this.alertController.create({
header: 'Confirm!',
message: 'Are you sure to delete it',
buttons: [
{
text: 'Cancel',
role: 'cancel',
cssClass: 'secondary',
handler: (blah) => {
console.log('Confirm Cancel: blah');
}
}, {
text: 'Okay',
handler: () => {
this.commandService.deleteCommand(id).subscribe(async () => {
const toast = await this.toastController.create({
message: 'Command have been deleted successfully.',
duration: 2000
});
toast.present();
window.location.reload();
});
}
}
]
});
await alert.present();
}
async updateCommand(id) {
const modal = await this.modalCtrl.create({
component: EditCommandPage,
componentProps: {
id,
},
cssClass: 'add-command-modal',
});
return await modal.present();
}
async addProduct(id) {
const modal = await this.modalCtrl.create({
component: AddProductPage,
componentProps: {
id,
},
cssClass: 'add-product-modal',
});
return await modal.present();
}
async deleteProduct(id) {
const alert = await this.alertController.create({
header: 'Confirm!',
message: 'Are you sure to delete it',
buttons: [
{
text: 'Cancel',
role: 'cancel',
cssClass: 'secondary',
handler: (blah) => {
console.log('Confirm Cancel: blah');
}
}, {
text: 'Okay',
handler: () => {
this.productService.deleteProduct(id).subscribe(async () => {
const toast = await this.toastController.create({
message: 'Product have been deleted successfully.',
duration: 2000
});
toast.present();
window.location.reload();
});
}
}
]
});
await alert.present();
}
async editProduct(id) {
const modal = await this.modalCtrl.create({
component: EditProductPage,
componentProps: {
id,
},
cssClass: 'add-product-modal',
});
return await modal.present();
}
async displayProduct(id) {
const modal = await this.modalCtrl.create({
component: DisplayProductPage,
componentProps: {
id,
},
cssClass: 'display-product-modal',
});
return await modal.present();
}
}
- add-command.page.html
<ion-header>
<ion-toolbar color="light">
<ion-title>Add command</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<div class="spinner" *ngIf="showSpinner">
<ion-spinner name="bubbles" color="danger"></ion-spinner>
</div>
<ion-list class="ion-padding">
<form [formGroup]="addCommandForm">
<ion-item>
<ion-icon name="time-outline"></ion-icon>
<ion-datetime name="{{newDate | date}}" formControlName="createAt"></ion-datetime>
</ion-item>
<ion-item>
<ion-textarea placeholder="Enter more information here..." formControlName="comment"></ion-textarea>
</ion-item>
<ion-button color="secondary" (click)="addCommand()">
Add command
</ion-button>
<ion-button color="danger" (click)="cancel()">
Cancel
</ion-button>
</form>
</ion-list>
</ion-content>
- add-command.page.ts
import { Command, User } from './../model/Model';
import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup, Validators} from '@angular/forms';
import { CommandService } from '../service/CommandService';
import { ActivatedRoute, Router } from '@angular/router';
@Component({
selector: 'app-add-command',
templateUrl: './add-command.page.html',
styleUrls: ['./add-command.page.scss'],
})
export class AddCommandPage implements OnInit {
command: Command = new Command();
id: number;
newDate = new Date().toString();
showSpinner = false;
constructor(private commandService: CommandService,
private route: ActivatedRoute, private router: Router) {
}
ngOnInit() {
this.id = this.route.snapshot.params.id;
console.log(this.id);
}
addCommand() {
this.showSpinner = true;
const command = this.addCommandForm.value;
this.commandService.saveCommand(command, this.id).subscribe(command => {
this.command = command;
this.router.navigate(['/home/', this.id], {relativeTo: this.route});
this.showSpinner = false;
});
}
cancel() {
this.router.navigate(['/home/', this.id], {relativeTo: this.route});
}
}
- edit-command.page.html
<ion-header>
<ion-toolbar color="light">
<ion-title>Edit command</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<div class="spinner" *ngIf="showSpinner">
<ion-spinner name="bubbles" color="danger"></ion-spinner>
</div>
<ion-list class="ion-padding">
<form [formGroup]="addCommandForm">
<ion-item>
<ion-icon name="time-outline"></ion-icon>
<ion-datetime name="{{command.createAt | date}}" formControlName="createAt"></ion-datetime>
</ion-item>
<ion-item>
<ion-textarea placeholder="{{command.comment}}" formControlName="comment"></ion-textarea>
</ion-item>
<ion-button color="secondary" (click)="addCommand()">
Edit command
</ion-button>
<ion-button color="danger" (click)="cancel()">
Cancel
</ion-button>
</form>
</ion-list>
</ion-content>
- edit-command.page.ts
import { Command, User } from './../model/Model';
import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup, Validators} from '@angular/forms';
import { CommandService } from '../service/CommandService';
import { ActivatedRoute, Router } from '@angular/router';
@Component({
selector: 'app-edit-command',
templateUrl: './edit-command.page.html',
styleUrls: ['./edit-command.page.scss'],
})
export class EditCommandPage implements OnInit {
id: any;
command: Command = new Command();
newDate: any;
addCommandForm = new FormGroup({
createAt: new FormControl('', Validators.required),
comment: new FormControl('')
});
constructor(private commandService: CommandService) {}
ngOnInit() {
this.commandService.findCommandById(this.id).subscribe(command => {
this.command = command;
});
}
addCommand() {
const command = this.addCommandForm.value;
this.commandService.updateCommand(command, this.id).subscribe(command => {
this.command = command;
window.location.reload();
});
}
}
- add-product.page.html
<ion-content>
<ion-header>
<ion-toolbar color="secondary">
<ion-title class="title"Add product</ion-title>
</ion-toolbar>
</ion-header>
<div class="spinner" *ngIf="showSpinner">
<ion-spinner name="bubbles" color="danger"></ion-spinner>
</div>
<hr style="border: 1px solid #eee; height: 0">
<ion-list class="ion-list-border">
<ion-item>
<ion-label position="floating"Name</ion-label>
<ion-input type="text" [(ngModel)]="product.name"></ion-input>
</ion-item>
<ion-item>
<ion-label position="floating">Description</ion-label>
<ion-input type="text" [(ngModel)]="product.description"></ion-input>
</ion-item>
<ion-item>
<ion-label position="floating">Price</ion-label>
<ion-input type="number" [(ngModel)]="product.price"></ion-input>
</ion-item>
<ion-item>
<ion-label position="floating">Quantity</ion-label>
<ion-input type="number" [(ngModel)]="product.quantity"></ion-input>
</ion-item>
<ion-item>
<ion-label position="floating">Photo</ion-label>
<ion-input type="text" [(ngModel)]="product.photo"></ion-input>
</ion-item>
<ion-item>
<ion-row>
<ion-col>
<ion-button expand="block" color="secondary" (click)="onSubmit()">Save</ion-button>
</ion-col>
<ion-col>
<ion-button expand="block" fill="outline" color="danger" (click)="dismissModal()">Cancel</ion-button>
</ion-col>
</ion-row>
</ion-item>
</ion-list>
</ion-content>
- add-product.page.ts
import { CommandService } from './../services/command-service';
import { ModalController } from '@ionic/angular';
import { ProductService } from './../services/product.service';
import { Component, OnInit } from '@angular/core';
import { Product } from '../model/Model';
@Component({
selector: 'app-add-product',
templateUrl: './add-product.page.html',
styleUrls: ['./add-product.page.scss'],
})
import class AddProductPage implements OnInit {
id: any;
product: Product = new Product();
showSpinner = false;
showMessage = false;
constructor(private commandService: CommandService, private modalCtrl: ModalController,
private productService: ProductService) { }
ngOnInit() {}
onSubmit() {
this.commandService.saveProduct(this.product, this.id).subscribe(product => {
this.product = product;
window.location.reload();
})
}
dismissModal() {
this.modalCtrl.dismiss();
}
}
- edit-product.page.html
<ion-content>
<ion-header>
<ion-toolbar color="secondary">
<ion-title class="title"Edit product</ion-title>
</ion-toolbar>
</ion-header>
<div class="spinner" *ngIf="showSpinner">
<ion-spinner name="bubbles" color="danger"></ion-spinner>
</div>
<hr style="border: 1px solid #eee; height: 0">
<ion-list class="ion-list-border">
<ion-item>
<ion-label position="floating"Name</ion-label>
<ion-input type="text" [(ngModel)]="product.name"></ion-input>
</ion-item>
<ion-item>
<ion-label position="floating">Description</ion-label>
<ion-input type="text" [(ngModel)]="product.description"></ion-input>
</ion-item>
<ion-item>
<ion-label position="floating">Price</ion-label>
<ion-input type="number" [(ngModel)]="product.price"></ion-input>
</ion-item>
<ion-item>
<ion-label position="floating">Quantity</ion-label>
<ion-input type="number" [(ngModel)]="product.quantity"></ion-input>
</ion-item>
<ion-item>
<ion-label position="floating">Photo</ion-label>
<ion-input type="text" [(ngModel)]="product.photo"></ion-input>
</ion-item>
<ion-item>
<ion-row>
<ion-col>
<ion-button expand="block" color="secondary" (click)="onSubmit()">Save</ion-button>
</ion-col>
<ion-col>
<ion-button expand="block" fill="outline" color="danger" (click)="dismissModal()">Cancel</ion-button>
</ion-col>
</ion-row>
</ion-item>
</ion-list>
</ion-content>
- edit-product.page.ts
import { CommandService } from './../services/command-service';
import { ModalController } from '@ionic/angular';
import { ProductService } from './../services/product.service';
import { Component, OnInit } from '@angular/core';
import { Product } from '../model/Model';
@Component({
selector: 'app-add-product',
templateUrl: './add-product.page.html',
styleUrls: ['./add-product.page.scss'],
})
import class AddProductPage implements OnInit {
id: any;
product: Product = new Product();
showSpinner = false;
showMessage = false;
constructor(private commandService: CommandService, private modalCtrl: ModalController,
private productService: ProductService) { }
ngOnInit() {
this.productService.findProductById(this.id).subscribe(product => {
this.product = product;
});
}
onSubmit() {
this.commandService.saveProduct(this.product, this.id).subscribe(product => {
this.product = product;
window.location.reload();
})
}
dismissModal() {
this.modalCtrl.dismiss();
}
}
- display-product.page.html
<ion-header>
<ion-toolbar color="tertiary">
<ion-title><ion-icon name="layers"></ion-icon>Product details</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-list>
<ion-item>
<div class="img_box">
<ion-img [src]="product.photo"></ion-img>
</div>
</ion-item>
</ion-list>
<ion-item>
<ion-label><ion-icon name="grid" slot="start" color="medium"></ion-icon>{{product.name}}</ion-label>
</ion-item>
<ion-item>
<ion-label><ion-icon name="checkbox" slot="start" color="medium"></ion-icon> Price: </ion-label>
<ion-button fill="outline" color="danger" slot="end">{{product.price}}</ion-button>
</ion-item>
<ion-item>
<ion-label><ion-icon name="bar-chart" slot="start" color="medium"></ion-icon> Quantity: </ion-label>
<ion-button fill="outline" color="dark" slot="end">{{product.quantity}}</ion-button>
</ion-item>
<ion-item-divider>
<ion-label class="ion-text-wrap">
{{product.description}}
</ion-label>
</ion-item-divider>
</ion-content>
- display-product.scss
.img_box {
width: 224%;
}
.header-row {
font-weight: 500;
border-bottom: 2px solid #000000
}
.data-row {
border-bottom: 1px solid #c4c4c4;
}
ion-note {
font-weight: bold;
}
- display-product.ts
import { ProductService } from './../services/product.service';
import { Component, OnInit } from '@angular/core';
import { Product } from '../model/Model';
@Component({
selector: 'app-display-product',
templateUrl: './display-product.page.html',
styleUrls: ['./display-product.page.scss'],
})
export class DisplayProductPage implements OnInit {
product: Product = new Product();
id: any;
constructor(private productService: ProductService) { }
ngOnInit() {
this.productService.findProductById(this.id).subscribe(product => {
this.product = product;
});
}
}
- edit-user.page.html
<ion-content>
<ion-header>
<ion-toolbar color="secondary">
<ion-title class="title">Update user</ion-title>
</ion-toolbar>
</ion-header>
<div class="spinner" *ngIf="showSpinner">
<ion-spinner name="bubbles" color="danger"></ion-spinner>
</div>
<hr style="border: 1px solid #eee; height: 0">
<ion-list class="ion-list-border">
<ion-item>
<ion-input type="text" [(ngModel)]="user.name"></ion-input>
</ion-item>
<ion-item>
<ion-input type="text" [(ngModel)]="user.username"></ion-input>
</ion-item>
<ion-item>
<ion-input type="email" [(ngModel)]="user.email"></ion-input>
</ion-item>
<ion-item>
<ion-input type="password" [(ngModel)]="user.password"></ion-input>
</ion-item>
<ion-item>
<ion-row>
<ion-col>
<ion-button expand="block" color="secondary" (click)="onSubmit()">Register</ion-button>
</ion-col>
<ion-col>
<ion-button expand="block" fill="outline" color="danger" (click)="dismissModal()">Cancel</ion-button>
</ion-col>
</ion-row>
</ion-item>
</ion-list>
</ion-content>
- edit-user.page.ts
import { User } from './../model/Model';
import { Component, OnInit } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { ToastController } from '@ionic/angular';
import { UserService } from '../service/UserService';
@Component({
selector: 'app-login',
templateUrl: './login.page.html',
styleUrls: ['./login.page.scss'],
})
export class LoginPage implements OnInit {
showSpinner = false;
id = number;
user: User = new User();
constructor(private router: Router, private toastController: ToastController,
private route: ActivatedRoute, private userService: UserService) { }
ngOnInit(): void {
this.userService.findUserById(this.id).subscribe(user => {
this.user = user;
});
}
onSubmit() {
this.showSpinner = true;
this.userService.updateUser(this.user, this.id).subscribe(user => {
this.user = user;
this.presentToast();
this.router.navigate(['/home/', user.id], {relativeTo: this.route});
this.showSpinner = false;
});
}
async presentToast() {
const toast = await this.toastController.create({
message: 'Updated successfully',
duration: 2000
});
toast.present();
}
dismissModal() {
this.modalCtrl.dismiss();
this.showSpinner = false;
}
}
Add the AppRoutingModule
In Angular, the best practice is to load and configure the router in a separate, top-level module that is dedicated to routing and imported by the root AppModule.
By convention, the module class name is AppRoutingModule and it belongs in the app-routing.module.ts in the src/app folder.
- app-routing.module.ts
import { blue NgModule } from '@angular/core';
import { blue PreloadAllModules, RouterModule, Routes } from '@angular/router';
const routes: Routes = [
{ path: '', redirectTo: 'login', pathMatch: 'full' },
{ path: 'home/:id', loadChildren: () => import('./home/home.module').then( m => m.HomePageModule)},
{
path: 'login',
loadChildren: () => import('./login/login.module').then( m => m.LoginPageModule)
},
{
path: 'edit-user',
loadChildren: () => import('./edit-user/edit-user.module').then( m => m.EditUserPageModule)
},
{
path: 'add-command/:id',
loadChildren: () => import('./add-command/add-command.module').then( m => m.AddCommandPageModule)
},
{
path: 'edit-command',
loadChildren: () => import('./edit-command/edit-command.module').then( m => m.EditCommandPageModule)
},
{
path: 'add-product',
loadChildren: () => import('./add-product/add-product.module').then( m => m.AddProductPageModule)
},
{
path: 'edit-product',
loadChildren: () => import('./edit-product/edit-product.module').then( m => m.EditProductPageModule)
},
{
path: 'display-product',
loadChildren: () => import('./display-product/display-product.module').then( m => m.DisplayProductPageModule)
},
];
NgModules and JavaScript modules
The NgModule system is different from and unrelated to the JavaScript (ES2015) module system for managing collections of JavaScript objects. These are complementary module systems that you can use together to write your apps.
In JavaScript each file is a module and all objects defined in the file belong to that module. The module declares some objects to be public by marking them with the export key word. Other JavaScript modules use import statements to access public objects from other modules.
- app-module.ts
import { DisplayProductPageModule } from './display-product/display-product.module';
import { EditUserPageModule } from './edit-user/edit-user.module';
import { EditProductPageModule } from './edit-product/edit-product.module';
import { AddProductPageModule } from './add-product/add-product.module';
import { EditCommandPageModule } from './edit-command/edit-command.module';
import { AddCommandPageModule } from './add-command/add-command.module';
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 { ReactiveFormsModule, FormsModule } from '@angular/forms';
import { NameUniqueDirective } from './productNameUnique/name-unique.directive';
@NgModule({
declarations: [AppComponent, NameUniqueDirective],
entryComponents: [],
imports: [
BrowserModule,
IonicModule.forRoot(),
AppRoutingModule,
HttpClientModule,
FormsModule,
ReactiveFormsModule,
AddCommandPageModule,
EditCommandPageModule,
AddProductPageModule,
EditProductPageModule,
EditUserPageModule,
DisplayProductPageModule
],
providers: [
StatusBar,
SplashScreen,
{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy }
],
exports: [
NameUniqueDirective
],
bootstrap: [ AppComponent]
})
export class AppModule{}
ionic serve
Start a local dev server for app dev/testing
Easily spin up a development server which launches in your browser. It watches for changes in your source files and automatically reloads with the updated build.
$ ionic serve
Conclusion
Now we have an overview of Spring Boot CRUD example when building a CRUD App.
We also take a look at client-server architecture for REST API using Spring Web MVC & Spring Data JPA, as well, we ware creating an Ionic app with Angular 10 project structure for building a front-end app to make HTTP requests and consume responses.