Why should I use the Ionic framework?

An Ionic app is used for developing a hybrid application. Ionic had a timeline as "Mobile for Angular" until its latest version launched as "Ionic for everyone"(Ionic 4).

An Ionic app is used for developing a hybrid application. Ionic had a timeline as "Mobile for Angular" until its latest version launched as "Ionic for everyone"(Ionic 4).

Push Notification using Ionic 4 and Firebase Cloud Messaging

Push Notification using Ionic 4 and Firebase Cloud Messaging

The comprehensive step by step tutorial on receiving a push notification on Mobile App using Ionic 4 and Firebase Cloud Messaging (FCM)

The comprehensive step by step tutorial on receiving a push notification on Mobile App using Ionic 4 and Firebase Cloud Messaging (FCM). We will use Ionic 4 Cordova native FCM plugin for receiving a push notification and using Firebase API for sending push notification from the Postman.

Table of Contents:

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.10.3

1. Setup and Configure Google Firebase Cloud Messaging

Open your browser then go to Google Firebase Console then login using your Google account.

Next, click on the Add Project button then fill the Project Name with Ionic 4 FCM and check the terms then click Create Project button.

After clicking the continue button you will redirect to the Project Dashboard page. Click the Gear Button on the right of Project Overview then click Project Settings. Click the Cloud Messaging tab the write down the Server Key and Sender ID for next usage in the API and Ionic 4 App. Next, back to the General tab then click the Android icon in your Apps to add Android App.

Fill the required fields in the form as above then click Register App button. Next, download the google-services.json that will use in the Ionic 4 app later. Click next after download, you can skip Add Firebase SDK by click again Next button. You can skip step 4 if there’s no App creating on running yet.

2. Create a new Ionic 4 App

To create a new Ionic 4 App, type this command in your terminal.

ionic start ionic4-push blank --type=angular

If you see this question, just type N for because we will installing or adding Cordova later.

Install the free Ionic Appflow SDK and connect your app? (Y/n) N

Next, go to the newly created app folder.

cd ./ionic4-push

As usual, run the Ionic 4 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 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.

3. Add Ionic 4 Cordova Native FCM Plugin

To install Ionic 4 Cordova Native Firebase Message Plugin, type this command.

ionic cordova plugin add cordova-plugin-fcm-with-dependecy-updated
npm install @ionic-native/fcm

Next, open and edit src/app/app.module.ts then add this import.

import { FCM } from '@ionic-native/fcm/ngx';

Add to @NgModule providers.

providers: [
  StatusBar,
  SplashScreen,
  FCM,
  { provide: RouteReuseStrategy, useClass: IonicRouteStrategy }
],

Next, open and edit src/app/app.component.ts then add this import.

import { FCM } from '@ionic-native/fcm/ngx';
import { Router } from '@angular/router';

Inject FCM and Router module to the constructor.

constructor(
  private platform: Platform,
  private splashScreen: SplashScreen,
  private statusBar: StatusBar,
  private fcm: FCM,
  private router: Router
) {
  this.initializeApp();
}

Inside platform ready of initializeApp function, add a function to get FCM token then print out to the browser console.

this.fcm.getToken().then(token => {
  console.log(token);
});

Add this function to refresh the FCM token.

this.fcm.onTokenRefresh().subscribe(token => {
  console.log(token);
});

Add this function to receive push notification from Firebase Cloud Messaging.

this.fcm.onNotification().subscribe(data => {
  console.log(data);
  if (data.wasTapped) {
    console.log('Received in background');
    this.router.navigate([data.landing_page, data.price]);
  } else {
    console.log('Received in foreground');
    this.router.navigate([data.landing_page, data.price]);
  }
});

Above example of receiving a push notification from FCM will redirect to the other page with params of data. For that, next, we have to add a new page by type this command.

ionic g page second

Next, modify src/app/app-routing.module.ts then change the new page route.

const routes: Routes = [
  { path: '', redirectTo: 'home', pathMatch: 'full' },
  { path: 'home', loadChildren: './home/home.module#HomePageModule' },
  { path: 'second/:price', loadChildren: './second/second.module#SecondPageModule' },
];

Next, open and edit src/app/second/second.page.ts then add this import.

import { ActivatedRoute } from '@angular/router';

Inject that module to the constructor.

constructor(private route: ActivatedRoute) { }

Add a variable for hold data from router parameters.

price: any = '';

Add this line to get data from the router parameters.

constructor(private route: ActivatedRoute) {
  this.price = this.route.snapshot.params['price'];
}

Next, open and edit src/app/second/second.page.html then replace all HTML tags with this.

<ion-header>
&nbsp; <ion-toolbar>
&nbsp; &nbsp; <ion-title>Second</ion-title>
&nbsp; </ion-toolbar>
</ion-header>

<ion-content padding>
&nbsp; <ion-card>
&nbsp; &nbsp; <ion-card-header>
&nbsp; &nbsp; &nbsp; <ion-card-title>Congratulation!</ion-card-title>
&nbsp; &nbsp; </ion-card-header>

&nbsp; &nbsp; <ion-card-content>
&nbsp; &nbsp; &nbsp; You get price from our sponsor:
&nbsp; &nbsp; &nbsp; <h2>{{price}}</h2>
&nbsp; &nbsp; </ion-card-content>
&nbsp; </ion-card>
</ion-content>

If you plan to send push notification to the group of topic, add this lines inside the platform ready.

this.fcm.subscribeToTopic('people');

To unsubscribe from topic, add this line.

this.fcm.unsubscribeFromTopic('marketing');

4. Run and Test Sending and Receiving Push Notification

Before running this Ionic 4 app, we have to copy the downloaded google-services.json file to the root of the project. Type this command to add the Android platform.

ionic cordova platform add android

Next, copy the google-services.json to the platform/android/ directory.

cp google-services.json platform/android/

Next, run the Ionic 4 App to the Android device by type this command.

ionic cordova run android

After the app running on the device, check the console from the Google Chrome by type this address chrome://inspect then choose the inspect link. You should take to the browser inspector, just change to the console tab.

As you can see above, you can take and write down the FCM token for use by Postman. Next, open the Postman application from your computer. Change the method to POST and add this address [https://fcm.googleapis.com/fcm/send](https://fcm.googleapis.com/fcm/send "https://fcm.googleapis.com/fcm/send"). On the headers, add this key Content-Type with value application/json and Authorization with value key=YOUR_FIREBASE_KEY....

Next, add this JSON data to the RAW body.

{
&nbsp; "notification":{
&nbsp; &nbsp; "title":"Ionic 4 Notification",
&nbsp; &nbsp; "body":"This notification sent from POSTMAN using Firebase HTTP protocol",
&nbsp; &nbsp; "sound":"default",
&nbsp; &nbsp; "click_action":"FCM_PLUGIN_ACTIVITY",
&nbsp; &nbsp; "icon":"fcm_push_icon"
&nbsp; },
&nbsp; "data":{
&nbsp; &nbsp; "landing_page":"second",
&nbsp; &nbsp; "price":"$3,000.00"
&nbsp; },
&nbsp; &nbsp; "to":"eadego-nig0:APA91bEtKx9hv50lmQmfzl-bSDdsZyTQ4RkelInfzxrPcZjJaSgDmok3-WQKV5FBu9hrMrkRrcCmf3arkGSviGltg5CyC2F9x1J2m0W7U8PxJ3Zlh7-_tL6VcFdb76hbaLIdZ-dOK15r",
&nbsp; &nbsp; "priority":"high",
&nbsp; &nbsp; "restricted_package_name":""
}

If you want to send by topics recipients, change the value of to to topics/people. Next, click the send button and you should see this response.

{
&nbsp; &nbsp; "multicast_id": 7712395953543412819,
&nbsp; &nbsp; "success": 1,
&nbsp; &nbsp; "failure": 0,
&nbsp; &nbsp; "canonical_ids": 0,
&nbsp; &nbsp; "results": [
&nbsp; &nbsp; &nbsp; &nbsp; {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "message_id": "0:1550632139317442%b73443ccb73443cc"
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; ]
}

And you will see the notification in your Android device background screen.

If you tap on it, it will open the App and redirect to the second page with this view.

That it’s, the example of receiving push notification using Ionic 4 and Firebase Cloud Messaging. You can grab the full source code from our GitHub.

Ionic 4: Set default ionic ion-select option

Struggling to set the default option in an&nbsp;&lt;ion-select&gt;&nbsp;control. Using v4 rc2 but this has been an issue since I started with the late v4 betas.

Struggling to set the default option in an <ion-select> control. Using v4 rc2 but this has been an issue since I started with the late v4 betas.

The <ion-select> correctly sets the default selection but I get the small down caret. The selected option is not displayed. When I select the control, the alert is displayed and the correct option is selected. Or am I doing something wrong?

Does the order of setting the measureSetId vs loading the measureSets[] matter?

Thoughts? appreciate any help on this.

<ion-select name="measureSetSelect" [(ngModel)]="selectedMeasureSetId">
  <ion-select-option *ngFor="let measureSet of measureSets" [value]="measureSet.measureSetId">
    {{measureSet.name}}
  </ion-select-option>
</ion-select>

public measureSets: IMeasureSet[];
public selectedMeasureSetId: number;

ngOnInit() {
this.measureSetsApiService.query().pipe(take(1)).subscribe(result => {
this.selectedMeasureSetId = result.find(i => i.isDefault).measureSetId || 1;
this.measureSets = result;
});
}


Ionic 4, Angular 7 and Cordova Crop and Upload Image

Ionic 4, Angular 7 and Cordova Crop and Upload Image

The comprehensive step by step tutorial on crop, and upload Image using Ionic 4, Angular 7 and Cordova

The comprehensive step by step tutorial on crop, and upload Image using Ionic 4, Angular 7 and Cordova. We will use Native Ionic Cordova Crop, File Transfer plugins and it’s dependencies. In this tutorial, we will use our existing uploader API that you can find on our GitHub which it uses Node.js, Express.js, and Multer. Or, you can use your own backend or API that using HTML Form Encoding multipart/form-data. The scenario is in the Ionic 4 App, click the Camera button inside the preview image then it will open image picker. After image picked then it will go to cropping popup that you can crop anyway you like then upload to the API. After the successful upload, the Ionic 4 app will preview the Image by URL that saved to the API server.

Table of Contents:

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.12.0

1. Create a New Ionic 4, Angular 7 and Cordova App

To create a new Ionic 4 App, type this command in your terminal.

ionic start ionic4-crop blank --type=angular

If you see this question, just type N for because we will installing or adding Cordova later.

Install the free Ionic Appflow SDK and connect your app? (Y/n) N

Next, go to the newly created app folder.

cd ./ionic4-crop

As usual, run the Ionic 4 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 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. Install and Configure Image Crop, File Transfer Plugins, and Dependencies

We will install all required plugins for this tutorial. First, we have to install Native Cordova plugins and Ionic 4 Angular 7 Modules by running these commands.

ionic cordova plugin add cordova-plugin-crop
npm install @ionic-native/crop
ionic cordova plugin add cordova-plugin-camera
npm install @ionic-native/camera
ionic cordova plugin add cordova-plugin-file-transfer
npm install @ionic-native/file-transfer
ionic cordova plugin add cordova-plugin-file
npm install @ionic-native/file

Next, open and edit src/app/app.module.ts then add these imports.

import { ImagePicker } from '@ionic-native/image-picker/ngx';

Add that import to @NgModule Providers.

providers: [
&nbsp; StatusBar,
&nbsp; SplashScreen,
&nbsp; { provide: RouteReuseStrategy, useClass: IonicRouteStrategy },
&nbsp; ImagePicker
],

3. Implementing Image Crop and File Upload/Transfer

We will be using the existing Home component or page to implementing Image Preview, Picker, Crop and Upload. For that, open and edit src/app/home/home.page.html then replace all HTML tags with these.

<ion-header>
&nbsp; <ion-toolbar>
&nbsp; &nbsp; <ion-title>
&nbsp; &nbsp; &nbsp; Ionic 4 Crop Upload
&nbsp; &nbsp; </ion-title>
&nbsp; </ion-toolbar>
</ion-header>

<ion-content padding>
&nbsp; <ion-card>
&nbsp; &nbsp; <img *ngIf="!fileUrl" src="assets/no-image.jpeg"/>
&nbsp; &nbsp; <img *ngIf="fileUrl" src="{{fileUrl}}"/>
&nbsp; &nbsp; <ion-card-content>
&nbsp; &nbsp; &nbsp; <ion-button color="medium" size="large" (click)="cropUpload()">
&nbsp; &nbsp; &nbsp; &nbsp; <ion-icon slot="icon-only" name="camera"></ion-icon>
&nbsp; &nbsp; &nbsp; </ion-button>
&nbsp; &nbsp; </ion-card-content>
&nbsp; </ion-card>
</ion-content>

Next, open and edit src/app/home/home.page.ts then add these imports.

import { Crop } from '@ionic-native/crop/ngx';
import { ImagePicker } from '@ionic-native/image-picker/ngx';
import { FileTransfer, FileUploadOptions, FileTransferObject } from '@ionic-native/file-transfer/ngx';

Inject those imports to the constructor.

constructor(private imagePicker: ImagePicker,
&nbsp; private crop: Crop,
&nbsp; private transfer: FileTransfer) { }

Add the variables for hold image URL and response data.

fileUrl: any = null;
respData: any;

Create a function for crop and upload an image file to the API server.

cropUpload() {
&nbsp; this.imagePicker.getPictures({ maximumImagesCount: 1, outputType: 0 }).then((results) => {
&nbsp; &nbsp; for (let i = 0; i < results.length; i++) {
&nbsp; &nbsp; &nbsp; &nbsp; console.log('Image URI: ' + results[i]);
&nbsp; &nbsp; &nbsp; &nbsp; this.crop.crop(results[i], { quality: 100 })
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .then(
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; newImage => {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; console.log('new image path is: ' + newImage);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; const fileTransfer: FileTransferObject = this.transfer.create();
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; const uploadOpts: FileUploadOptions = {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;fileKey: 'file',
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;fileName: newImage.substr(newImage.lastIndexOf('/') + 1)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; };

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fileTransfer.upload(newImage, 'http://192.168.0.7:3000/api/upload', uploadOpts)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;.then((data) => {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;console.log(data);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;this.respData = JSON.parse(data.response);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;console.log(this.respData);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;this.fileUrl = this.respData.fileUrl;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}, (err) => {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;console.log(err);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;});
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; },
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; error => console.error('Error cropping image', error)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; );
&nbsp; &nbsp; }
&nbsp; }, (err) => { console.log(err); });
}

As you can see, we use the IP address to access Express.js API from the device. The uploaded image file accessible from the device through [http://192.168.0.7:3000/images/filename](http://192.168.0.7:3000/images/filename "http://192.168.0.7:3000/images/filename") URL.

4. Run and Test Ionic 4, Angular 7 and Cordova App on iOS/Android Devices

We assume that you have cloned the Node.js, Express.js and Multer image uploader here https://github.com/didinj/node-express-image-uploader.git. Next, open a new Terminal or cmd-tab then go to the cloned Express image uploader.

npm install
nodemon

Next, to run on Android devices type this command while the device connected.

ionic cordova platform add android
ionic cordova run android

To run on iOS simulator or device, we have to build it first.

ionic cordova platform add ios
ionic cordova build ios

Then open and run the iOS app from the XCode. You will this view from your Android device or iOS simulator.

That it’s, the Ionic 4, Angular 7 and Cordova Crop and Upload Image tutorial. You can get the full source code from out GitHub.

Building CRUD Mobile App using Ionic 4, Angular 8

Building CRUD Mobile App using Ionic 4, Angular 8

A comprehensive Ionic 4 Angular 8 tutorial, learn to build CRUD (Create, Read, Update, Delete) Mobile Apps (Android/iOS)

A comprehensive Ionic 4 Angular 8 tutorial, learn to build CRUD (Create, Read, Update, Delete) Mobile Apps (Android/iOS)

The Angular 8 just released a few weeks ago, but Ionic 4 still using Angular 7. As usual, we will start this tutorial using Ionic CLI with the new version 5.

The Ionic 4 Angular 8 application flow will look like this. It just a regular CRUD (Create, Read, Update, Delete) function.

In this tutorial, we will use more Angular 8 than Ionic 4 components itself. So, if you are new to Hybrid Mobile Apps development using Ionic and familiar with Angular then this is your easy way to implement your Angular skill for Mobile App development.

The following tools, frameworks, and modules are required for this tutorial:

Remember always use the latest Ionic 4 and Angular 8 CLI to decrease compatibility issues

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 4 CLI in your terminal or command line. Check the version by type this command.

ionic -v
5.1.0

To update the Angular CLI, type this command.

sudo npm install -g @angular/cli

Now, the Angular version should be like this.

ng version
Angular CLI: 7.3.9
Create Ionic 4 Application and Update to Angular 8

We will be using Ionic CLI to create a new Ionic 4 application. Type this command to create it.

ionic start ionic4-angular8-crud --type=angular

The created Ionic 4 application still using Angular 7.2.2, for that we have to upgrade the Angular 7 to Angular 8. Go to the newly created folder then type this command using Angular CLI.

ng update @angular/cli @angular/core

If you get dependency incompatibility like below.

Package "@ionic/angular" has an incompatible peer dependency to "zone.js" (requires "^0.8.26", would install "0.9.1").
Incompatible peer dependencies found. See above.

Uninstall then install again the required dependency version.

npm uninstall --save zone.js
npm install --save zone.js

Then run again the Angular 8 update command. Next, 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 8 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.

Install Angular 8 Material and CDK

For UI, we will use Angular 8 Material and CDK. To install Angular 8 Material and CDK, simply run this command.

ng add @angular/material

Type enter or yes for every question that showed up.

? Choose a prebuilt theme name, or "custom" for a custom theme: Deep Purple/Amber  [ Preview: h
ttps://material.angular.io?theme=deeppurple-amber ]
? Set up HammerJS for gesture recognition? Yes
? Set up browser animations for Angular Material? Yes

Next, register all required Angular Material CDK components or modules to app.module.ts. Open and edit that file then add this imports.

import { DragDropModule } from '@angular/cdk/drag-drop';
import { ScrollingModule } from '@angular/cdk/scrolling';

For Angular 8 Material, we will not import here but in each Ionic 4 Page Modules. Also, modify FormsModule import to add ReactiveFormsModule.

import { FormsModule, ReactiveFormsModule } from '@angular/forms';

Register the above modules to @NgModule imports.

imports: [
  BrowserModule,
  FormsModule,
  ReactiveFormsModule,
  IonicModule.forRoot(),
  AppRoutingModule,
  BrowserAnimationsModule,
  DragDropModule,
  ScrollingModule
],
Use Dynamic Imports for Angular 8 Route Configuration

Before change the Angular 8 Route configuration, we have to add the required Ionic 4 Page Module first. Type these commands to create them.

ionic g page product-detail
ionic g page product-add
ionic g page product-edit

We just added detail, add, and edit pages because the Product list will display in the Home Page Module. Next, open src/app/app-routing.module.ts then you will see the route modified and includes the page navigation. Next, we will modify this to match the new Angular 8 feature. Replace all route constant with this constant.

const routes: Routes = [
  { path: '', redirectTo: 'home', pathMatch: 'full' },
  { path: 'home', loadChildren: () => import('./home/home.module').then(m => m.HomePageModule)},
  { path: 'product-detail/:id', loadChildren: () => import('./product-detail/product-detail.module').then(m => m.ProductDetailPageModule)},
  { path: 'product-add', loadChildren: () => import('./product-add/product-add.module').then(m => m.ProductAddPageModule)},
  { path: 'product-edit/:id', loadChildren: () => import('./product-edit/product-edit.module').then(m => m.ProductEditPageModule)},
];

Next, modify tsconfig.json to change module and target.

{
  "compilerOptions": {
  …
  "module": "esnext",
  "moduleResolution": "node",
  …
  "target": "es2015",
  …
},
Create Ionic 4 Angular 8 RESTful API Service

To call RESTful API we will use Ionic 4 Angular Service using HttpClientModule. So, all CRUD call handle by Ionic 4 Angular 8 service that emitted the response by Observable and RXJS. Next, open and edit src/app/app.module.ts then add these imports to register HttpClientModule and FormsModule.

import { HttpClientModule } from '@angular/common/http';

Add it to @NgModule imports after BrowserModule.

imports: [
  BrowserModule,
  FormsModule,
  HttpClientModule,
  AppRoutingModule
  ...
],

We will use type specifier to get a typed result object. For that, create a new Typescript file src/app/product.ts then add these lines of Typescript codes.

export class Product {
  _id: number;
  prod_name: string;
  prod_desc: string;
  prod_price: number;
  updated_at: Date;
}

Next, generate an Ionic 4 Angular 8 service by typing this command.

ionic g service api

Next, open and edit src/app/api.service.ts then add these imports.

import { Observable, of, throwError } from 'rxjs';
import { HttpClient, HttpHeaders, HttpErrorResponse } from '@angular/common/http';
import { catchError, tap, map } from 'rxjs/operators';
import { Product } from './product';

Add these constants before the @Injectable.

const httpOptions = {
  headers: new HttpHeaders({'Content-Type': 'application/json'})
};
const apiUrl = 'http://localhost:3000/api/v1/products';

Inject HttpClient module to the constructor.

constructor(private http: HttpClient) { }

Add the error handler function.

private handleError(operation = 'operation', result?: T) {
  return (error: any): Observable => {
    console.error(error); // log to console instead
    return of(result as T);
  };
}

Add all CRUD (create, read, update, delete) functions of products data.

getProducts(): Observable {
  return this.http.get(apiUrl)
    .pipe(
      tap(product => console.log('fetched products')),
      catchError(this.handleError('getProducts', []))
    );
}

getProduct(id: any): Observable {
  const url = `${apiUrl}/${id}`;
  return this.http.get(url).pipe(
    tap(_ => console.log(`fetched product id=${id}`)),
    catchError(this.handleError(`getProduct id=${id}`))
  );
}

addProduct(product: Product): Observable {
  return this.http.post(apiUrl, product, httpOptions).pipe(
    tap((prod: Product) => console.log(`added product w/ id=${prod._id}`)),
    catchError(this.handleError('addProduct'))
  );
}

updateProduct(id: any, product: any): Observable {
  const url = `${apiUrl}/${id}`;
  return this.http.put(url, product, httpOptions).pipe(
    tap(_ => console.log(`updated product id=${id}`)),
    catchError(this.handleError('updateProduct'))
  );
}

deleteProduct(id: any): Observable {
  const url = `${apiUrl}/${id}`;

  return this.http.delete(url, httpOptions).pipe(
    tap(_ => console.log(`deleted product id=${id}`)),
    catchError(this.handleError('deleteProduct'))
  );
}
View List of Data

As we mention in the begining of this article, we will use existing Ionic 4 Home Page Module to display list of data. For that, open and edit src/app/home/home.page.ts then add/replace these imports.

import { Component, OnInit } from '@angular/core';
import { LoadingController } from '@ionic/angular';
import { ActivatedRoute, Router } from '@angular/router';
import { ApiService } from '../api.service';
import { Product } from '../product';
import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';

Next, add Angular 8 OnInit implementation to the HomePage Class name.

export class HomePage implements OnInit

Next, add the constructor then inject those modules to the constructor.

constructor(
  public api: ApiService,
  public loadingController: LoadingController,
  public router: Router,
  public route: ActivatedRoute) { }

Remove all default generated variable, function and constructor body if exists then add this variable before the constructor for hold classroom data that get from the service.

products: Product[] = [];

Add function for getting Product list from API.

async getProducts() {
  const loading = await this.loadingController.create({
    message: 'Loading...'
  });
  await loading.present();
  await this.api.getProducts()
    .subscribe(res => {
      this.products = res;
      console.log(this.products);
      loading.dismiss();
    }, err => {
      console.log(err);
      loading.dismiss();
    });
}

Add Angular 8 init function after the constructor for call above function.

ngOnInit() {
  this.getProducts();
}

Add function for the new Angular 8 CDK Drag&Drop.

drop(event: CdkDragDrop) {
  moveItemInArray(this.products, event.previousIndex, event.currentIndex);
}

Next, because we will use the new Angular 8 CDK features. We should add modules for it to src/app/home/home.module.ts then add these imports.

import { ScrollingModule } from '@angular/cdk/scrolling';
import { DragDropModule } from '@angular/cdk/drag-drop';

Register to @NgModule imports array.

imports: [
  IonicModule,
  CommonModule,
  FormsModule,
  ScrollingModule,
  DragDropModule,
  RouterModule.forChild([{ path: '', component: HomePage }])
],

Next, open and edit src/app/home/home.page.html then replace all HTML tags with this.


  
    Home
  



  
    
      
      {{p.prod_name}}
      
        {{p.prod_price | currency}}
      
    
  

Finally, give this page a style by open and edit src/app/home/home.page.scss then replace all SCSS codes with these.

.example-viewport {
  height: 100%;
  width: 100%;
  border: none;
}

.example-item {
  min-height: 50px;
}
View Data Details and Add Delete Function

Every time you click the list item in the List of data, you will be redirected to Details tab including the ID of the selected data. Open and edit src/app/product-detail/product-detail.page.ts then add/replace this imports.

import { Component, OnInit } from '@angular/core';
import { AlertController } from '@ionic/angular';
import { ApiService } from '../api.service';
import { ActivatedRoute, Router } from '@angular/router';
import { Product } from '../product';

Inject above modules to the constructor.

constructor(
  public api: ApiService,
  public alertController: AlertController,
  public route: ActivatedRoute,
  public router: Router) {}

Add the variables before the constructor for hold Product data and Angular 8 Loading Spinner.

product: Product = { _id: null, prod_name: '', prod_desc: '', prod_price: null, updated_at: null };
isLoadingResults = false;

Add an asynchronous function to getting Product detail from API.

async getProduct() {
  if (this.route.snapshot.paramMap.get('id') === 'null') {
    this.presentAlertConfirm('You are not choosing an item from the list');
  } else {
    this.isLoadingResults = true;
    await this.api.getProduct(this.route.snapshot.paramMap.get('id'))
      .subscribe(res => {
        console.log(res);
        this.product = res;
        this.isLoadingResults = false;
      }, err => {
        console.log(err);
        this.isLoadingResults = false;
      });
  }
}

Add an asynchronous function for display an alert.

async presentAlertConfirm(msg: string) {
  const alert = await this.alertController.create({
    header: 'Warning!',
    message: msg,
    buttons: [
      {
        text: 'Okay',
        handler: () => {
          this.router.navigate(['']);
        }
      }
    ]
  });

  await alert.present();
}

Call get product function from Angular 8 init function.

ngOnInit() {
  this.getProduct();
}

Add the functions to delete the data.

async deleteProduct(id: any) {
  this.isLoadingResults = true;
  await this.api.deleteProduct(id)
    .subscribe(res => {
      this.isLoadingResults = false;
      this.router.navigate([ '/home' ]);
    }, err => {
      console.log(err);
      this.isLoadingResults = false;
    });
}

Add a function to navigate to the Edit Product page.

editProduct(id: any) {
  this.router.navigate([ '/product-edit', id ]);
}

Next, open and edit src/app/details/details.page.html then replace all HTML tags with this.


  
    
      
    
    Product Details
  



  
    <div class="example-loading-shade"
          *ngIf="isLoadingResults">
      
    
    
      
        ## {{product.prod_name}}

        {{product.prod_desc}}
      
      
        
          Product Price:
          {{product.prod_price}}
          Updated At:
          {{product.updated_at | date}}
        
      
      
        edit
        delete
      
    
  

Finally, give this page a style by open and edit src/app/product-detail/product-detail.page.scss then replace all SCSS codes with these.

.example-container {
  position: relative;
  padding: 5px;
  height: 100%;
  background-color: aqua;
}

.example-loading-shade {
  position: absolute;
  top: 0;
  left: 0;
  bottom: 56px;
  right: 0;
  background: rgba(0, 0, 0, 0.15);
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
}

.mat-flat-button {
  margin: 5px;
}
Create a Form to Add Data using Angular 8 Material

To create a form for adding a Product Data using Angular 8 Material, open and edit src/app/product-add/product-add.page.ts then add these imports.

import { Router } from '@angular/router';
import { ApiService } from '../api.service';
import { FormControl, FormGroupDirective, FormBuilder, FormGroup, NgForm, Validators } from '@angular/forms';

Inject above modules to the constructor.

constructor(
  private router: Router,
  private api: ApiService,
  private formBuilder: FormBuilder) { }

Declare variables for the Form Group and all of the required fields inside the form before the constructor.

productForm: FormGroup;
prod_name = '';
prod_desc = '';
prod_price: number = null;
isLoadingResults = false;

Add initial validation for each field in the ngOnInit function.

this.productForm = this.formBuilder.group({
  'prod_name' : [null, Validators.required],
  'prod_desc' : [null, Validators.required],
  'prod_price' : [null, Validators.required]
});

Create a function for submitting or POST product form.

onFormSubmit() {
  this.isLoadingResults = true;
  this.api.addProduct(this.productForm.value)
    .subscribe((res: any) => {
        const id = res._id;
        this.isLoadingResults = false;
        this.router.navigate(['/product-details', id]);
      }, (err: any) => {
        console.log(err);
        this.isLoadingResults = false;
      });
}

Next, add this import for implementing ErrorStateMatcher.

import { ErrorStateMatcher } from '@angular/material/core';

Create a new class before the main class @Components.

/** Error when invalid control is dirty, touched, or submitted. */
export class MyErrorStateMatcher implements ErrorStateMatcher {
  isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
    const isSubmitted = form && form.submitted;
    return !!(control && control.invalid && (control.dirty || control.touched || isSubmitted));
  }
}

Instantiate that MyErrorStateMatcher as a variable in the main class.

matcher = new MyErrorStateMatcher();

Before modifying the HTML file, we have to register all Angular 8 Material files by open and edit src/app/product-add/product-add.module.ts then add these imports.

import {
  MatInputModule,
  MatPaginatorModule,
  MatProgressSpinnerModule,
  MatSortModule,
  MatTableModule,
  MatIconModule,
  MatButtonModule,
  MatCardModule,
  MatFormFieldModule } from '@angular/material';

Declare that imported modules to then @NgModule imports array.

imports: [
  ...
  MatInputModule,
  MatPaginatorModule,
  MatProgressSpinnerModule,
  MatSortModule,
  MatTableModule,
  MatIconModule,
  MatButtonModule,
  MatCardModule,
  MatFormFieldModule
],

Next, open and edit src/app/product-add/product-add.component.html then replace all HTML tags with this.


  
    
      
    
    Product Add
  



  
    <div class="example-loading-shade"
         *ngIf="isLoadingResults">
      
    
    
      
        
          <input matInput placeholder="Product Name" formControlName="prod_name"
                 [errorStateMatcher]="matcher">
          
            Please enter Product Name
          
        
        
          <input matInput placeholder="Product Desc" formControlName="prod_desc"
                 [errorStateMatcher]="matcher">
          
            Please enter Product Description
          
        
        
          <input matInput placeholder="Product Price" formControlName="prod_price"
                 [errorStateMatcher]="matcher">
          
            Please enter Product Price
          
        
        
          save
        
      
    
  

Finally, open and edit src/app/product-add/product-add.component.scss then add this SCSS codes.

.example-container {
  position: relative;
  padding: 5px;
  height: 100%;
  background-color: aqua;
}

.example-form {
  min-width: 150px;
  max-width: 500px;
  width: 100%;
}

.example-full-width {
  width: 100%;
}

.example-full-width:nth-last-child(0) {
  margin-bottom: 10px;
}

.button-row {
  margin: 10px 0;
}

.mat-flat-button {
  margin: 5px;
}

.example-card {
  margin: 5px;
}
Create a Form to Edit Data using Angular 8 Material

To create a Form of Edit Product Data using Angular 8 Material, open and edit src/app/product-edit/product-edit.page.ts then add these lines of imports.

import { Router, ActivatedRoute } from '@angular/router';
import { ApiService } from '../api.service';
import { FormControl, FormGroupDirective, FormBuilder, FormGroup, NgForm, Validators } from '@angular/forms';
import { ErrorStateMatcher } from '@angular/material/core';

Add a new Class before the @Component that handles the error message in the HTML form.

/** Error when invalid control is dirty, touched, or submitted. */
export class MyErrorStateMatcher implements ErrorStateMatcher {
  isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
    const isSubmitted = form && form.submitted;
    return !!(control && control.invalid && (control.dirty || control.touched || isSubmitted));
  }
}

Next, add these lines of variables to the main Class before the constructor.

productForm: FormGroup;
_id = '';
prod_name = '';
prod_desc = '';
prod_price: number = null;
isLoadingResults = false;
matcher = new MyErrorStateMatcher();

Inject the constructor params with these modules.

constructor(
  private router: Router,
  private route: ActivatedRoute,
  private api: ApiService,
  private formBuilder: FormBuilder) { }

Initialize the form group with the form controls of the product form and call the product detail data in the Angular 8 ngOnInit function.

ngOnInit() {
  this.getProduct(this.route.snapshot.params['id']);
  this.productForm = this.formBuilder.group({
    'prod_name' : [null, Validators.required],
    'prod_desc' : [null, Validators.required],
    'prod_price' : [null, Validators.required]
  });
}

Create a new Angular 8 function to call the product data by ID.

getProduct(id: any) {
  this.api.getProduct(id).subscribe((data: any) => {
    this._id = data._id;
    this.productForm.setValue({
      prod_name: data.prod_name,
      prod_desc: data.prod_desc,
      prod_price: data.prod_price
    });
  });
}

Create a new Angular 8 function that handles the form submission to save data to the REST API.

onFormSubmit() {
  this.isLoadingResults = true;
  this.api.updateProduct(this._id, this.productForm.value)
    .subscribe((res: any) => {
        const id = res._id;
        this.isLoadingResults = false;
        this.router.navigate(['/product-details', id]);
      }, (err: any) => {
        console.log(err);
        this.isLoadingResults = false;
      }
    );
}

Add a function to navigate to the Product Detail page.

productDetails() {
  this.router.navigate(['/product-details', this._id]);
}

Before modifying the HTML file, we have to register all Angular 8 Material files by open and edit src/app/product-edit/product-edit.module.ts then add these imports.

import {
  MatInputModule,
  MatPaginatorModule,
  MatProgressSpinnerModule,
  MatSortModule,
  MatTableModule,
  MatIconModule,
  MatButtonModule,
  MatCardModule,
  MatFormFieldModule } from '@angular/material';

Declare that imported modules to then @NgModule imports array.

imports: [
  ...
  MatInputModule,
  MatPaginatorModule,
  MatProgressSpinnerModule,
  MatSortModule,
  MatTableModule,
  MatIconModule,
  MatButtonModule,
  MatCardModule,
  MatFormFieldModule
],

Next, open and edit src/app/product-edit/product-edit.page.html then replace all HTML tags with these.


  
    
      
    
    Product Edit
  



  
    <div class="example-loading-shade"
         *ngIf="isLoadingResults">
      
    
    
      
        
          <input matInput placeholder="Product Name" formControlName="prod_name"
                 [errorStateMatcher]="matcher">
          
            Please enter Product Name
          
        
        
          <input matInput placeholder="Product Desc" formControlName="prod_desc"
                 [errorStateMatcher]="matcher">
          
            Please enter Product Description
          
        
        
          <input matInput placeholder="Product Price" formControlName="prod_price"
                 [errorStateMatcher]="matcher">
          
            Please enter Product Price
          
        
        
          save
        
      
    
  

Finally, add some styles for this page by open and edit src/app/product-edit/product-edit.page.scss then replace all SCSS codes with these.

.example-container {
  position: relative;
  padding: 5px;
  height: 100%;
  background-color: aqua;
}

.example-form {
  min-width: 150px;
  max-width: 500px;
  width: 100%;
}

.example-full-width {
  width: 100%;
}

.example-full-width:nth-last-child(0) {
  margin-bottom: 10px;
}

.button-row {
  margin: 10px 0;
}

.mat-flat-button {
  margin: 5px;
}

.example-card {
  margin: 5px;
}
Run and Test the Whole Ionic 4 Angular 8 Mobile Apps

Before running Ionic 4 Angular 8 Mobile Apps we have to start the MongoDB server and Node/Express.js REST API server. Type these commands in the separate Terminal/CMD tabs.

mongod
nodemon

Now, we have to run the Ionic 4 Angular 8 Mobile Apps in the browser using this command.

ionic serve -l

And here we go, the full Ionic 4 Angular 8 Mobile Apps CRUD functions.

That it's, the comprehensive step by step tutorial of Ionic 4 Angular 8 CRUD Mobile Apps. You can find the full source code from our GitHub.