Build Ionic 6 Cordova and Angular CRUD Mobile App

Last updated on: by Digamber
A step by step Ionic Cordova tutorial, In this tutorial, we will learn how to create a CRUD (Create, Read, Update, Delete) Mobile app (Android/iOS) with Ionic and Angular using Express and Node REST API.

In this Ionic Angular Cordova CRUD operations tutorial, you will find out how to create a hybrid mobile app in Ionic Angular using Node Express REST API, You will create a music application which will allow users to add, edit or update a song or view a song list.

Before we begin, we should always remember that we must use the latest Ionic Cordova version to avoid compatibility problems.

Prerequisite

You need to be familiar with the following tools and frameworks to understand this tutorial.

  • Node
  • Express
  • MongoDB
  • Ionic
  • Cordova
  • Angular
  • Angular CLI
  • Text Editor

To set up Node.js on your system, you need to download the latest version of Node from here. You can also follow this tutorial to install Node js on your system.

Environment Setup

Run the following command to install Ionic Cordova globally in your system.

sudo npm install -g cordova ionic

You can verify the Ionic CLI version by running the below command.

ionic -v
# 5.4.6

Use following command to update Ionic and Cordova.

sudo npm update -g cordova ionic

Angular CLI can also be updated by using the below command.

sudo npm install -g @angular/cli

Create Ionic Angular Cordova Project

To create the new blank Ionic CRUD mobile app, we need to execute the following command from the terminal.

ionic start ionic-angular-crud-app blank --type=angular

Get inside the project folder.

cd ionic-angular-crud-app

Next, To start the app in both iOS and Android mode in the browser, use below command to install lab mode as a development dependency.

npm i @ionic/lab --save-dev

Then, run the command in the terminal. to open the Ionic CRUD mobile app in the browser emulator.

ionic serve -l

Add Ionic Angular Routes

In this step you have to add the routes to enable navigation in the Ionic Cordova app, hence first execute below commands to generate Ionic pages.

ng generate page add-song
ng generate page edit-song

We have created the pages to create routes, these pages have also been automatically registered in app/app-routing.module.ts file:

import { NgModule } from '@angular/core';
import { PreloadAllModules, RouterModule, Routes } from '@angular/router';
const routes: Routes = [
  {
    path: 'home',
    loadChildren: () => import('./home/home.module').then( m => m.HomePageModule)
  },
  {
    path: '',
    redirectTo: 'home',
    pathMatch: 'full'
  },
  {
    path: 'add-song',
    loadChildren: () => import('./add-song/add-song.module').then( m => m.AddSongPageModule)
  },
  {
    path: 'edit-song/:id',
    loadChildren: () => import('./edit-song/edit-song.module').then( m => m.EditSongPageModule)
  },
];
@NgModule({
  imports: [
    RouterModule.forRoot(routes, { preloadingStrategy: PreloadAllModules })
  ],
  exports: [RouterModule]
})
export class AppRoutingModule { }

Create Express REST API for Ionic Cordova App

Now, you require to create a backend server to manage the data in Ionic CRUD mobile app. Set up a backend server with the help of Node, Express and MongoDB to create REST APIs for Create, Read, Update and Delete songs data also for storing the data in the MongoDB database.

From the root of your application execute following commands to create a separate folder for creating a locus of REST APIs:

mkdir backend && cd backend

Create the specific package.json for the node/express server.

npm init -y

Install following NPM packages.

npm install body-parser cors express mongoose --save

Install nodemon npm package as a development dependency to avoid re-starting the server every time we make the changes in the server files.

npm install nodemon --save-dev

Create database folder and also create db.js file in the backend folder’s root then paste the following code in it.

module.exports = {
  db: 'mongodb://localhost:27017/node_db'
};

Create model folder and also create Song.js file in the backend folder’s root then add the following code in it.

const mongoose = require('mongoose');
const Schema = mongoose.Schema;
let Song = new Schema({
  song_name: {
    type: String
  },
  artist: {
    type: String
  }
}, {
  collection: 'songs'
})
module.exports = mongoose.model('Song', Song)

Next, create REST APIs for creating, reading, updating and deleting song data for our Ionic CRUD mobile app. We will take help of express js middlewares, and later we will learn how to consume REST APIs in Ionic4/Angular app.

Create routes folder and also create song.route.js file in the backend folder’s root then add the following code in it.

const express = require('express');
const app = express();
const songRoute = express.Router();
let SongModel = require('../model/Song');
// Add Song
songRoute.route('/create-song').post((req, res, next) => {
  SongModel.create(req.body, (error, data) => {
    if (error) {
      return next(error)
    } else {
      res.json(data)
    }
  })
});
// Get all songs
songRoute.route('/').get((req, res) => {
  SongModel.find((error, data) => {
    if (error) {
      return next(error)
    } else {
      res.json(data)
    }
  })
})
// Get single song
songRoute.route('/get-song/:id').get((req, res) => {
  SongModel.findById(req.params.id, (error, data) => {
    if (error) {
      return next(error)
    } else {
      res.json(data)
    }
  })
})

// Update song
songRoute.route('/update-song/:id').put((req, res, next) => {
  SongModel.findByIdAndUpdate(req.params.id, {
    $set: req.body
  }, (error, data) => {
    if (error) {
      return next(error);
      console.log(error)
    } else {
      res.json(data)
      console.log('Song successfully updated!')
    }
  })
})
// Delete song
songRoute.route('/delete-song/:id').delete((req, res, next) => {
  SongModel.findByIdAndRemove(req.params.id, (error, data) => {
    if (error) {
      return next(error);
    } else {
      res.status(200).json({
        msg: data
      })
    }
  })
})
module.exports = songRoute;

In the next step we will configure node/express server, create app.js file in the backend folder’s root and add the given below code inside of it.

let express = require('express'),
  path = require('path'),
  mongoose = require('mongoose'),
  cors = require('cors'),
  bodyParser = require('body-parser'),
  dataBaseConfig = require('./database/db');
// Connecting mongoDB
mongoose.Promise = global.Promise;
mongoose.connect(dataBaseConfig.db, {
  useNewUrlParser: true,
  useUnifiedTopology: true,
  useFindAndModify: false
}).then(() => {
  console.log('Database connected successfully ')
},
  error => {
    console.log('Could not connected to database : ' + error)
  }
)
const songRoute = require('./routes/song.route')
const app = express();
app.use(bodyParser.urlencoded({
    extended: true
  }));
app.use(bodyParser.json());
app.use(cors());
// RESTful API root
app.use('/api', songRoute)
// PORT
const port = process.env.PORT || 3000;
app.listen(port, () => {
  console.log('PORT Connected on: ' + port)
})
// Find 404 and hand over to error handler
app.use((req, res, next) => {
  next(createError(404));
});

// error handler
app.use(function (err, req, res, next) {
  console.error(err.message);
  if (!err.statusCode) err.statusCode = 500;
  res.status(err.statusCode).send(err.message);
});

Go to backend > package.json file and update the main: "index.js" name to main: "app.js" and you are ready to start the backend server. Run the below command while staying in the backend folder.

nodemon server

Before you test the APIs, make sure to start MongoDB community edition. Hence, you have to run and install the MongoDB. If you don’t know how to set up and start the MongoDB community edition, then visit the MongoDB website.

REST API URL – http://localhost:3000/api

MethodAPI url
GET All/api
GET Single/api/get-song/id
POST/api/create-song
PUT/api/update-song/id
DELETE/api/delete-song/id

Create Ionic 6 Angular API Service

We will take the help of Ionic Service to manage REST API in our CRUD mobile app. To make API calls, we need to import HttpClientModule service in the Ionic Angular project.

Head over to src/app/app.module.ts then import and register `HttpClientModule`.

import { HttpClientModule } from '@angular/common/http';
@NgModule({
  declarations: [...],
  entryComponents: [...],
  imports: [
    HttpClientModule
  ],
  providers: [...],
  bootstrap: [...]
})
export class AppModule { }

For type checking, we need to create a Song class. Generate a shared folder then create song.ts file in it. Include the given below code in it to define the song data type.

export class Song {
    _id: number;
    song_name: string;
    artist: string;
}

Run the following command to create Ionic Service for handling REST API calls inside the shared folder:

ng generate service shared/song

Next, place the following code in shared/song.service.ts:

import { Injectable } from '@angular/core';
import { Song } from './song';
import { Observable, of } from 'rxjs';
import { catchError, tap } from 'rxjs/operators';
import { HttpClient, HttpHeaders } from '@angular/common/http';
@Injectable({
  providedIn: 'root'
})
export class SongService {
  httpOptions = {
    headers: new HttpHeaders({ 'Content-Type': 'application/json' })
  };
  constructor(private http: HttpClient) { }
  addSong(song: Song): Observable<any> {
    return this.http.post<Song>('http://localhost:3000/api/create-song', song, this.httpOptions)
      .pipe(
        catchError(this.handleError<Song>('Add Song'))
      );
  }
  getSong(id): Observable<Song[]> {
    return this.http.get<Song[]>('http://localhost:3000/api/get-song/' + id)
      .pipe(
        tap(_ => console.log(`Song fetched: ${id}`)),
        catchError(this.handleError<Song[]>(`Get Song id=${id}`))
      );
  }
  getSongList(): Observable<Song[]> {
    return this.http.get<Song[]>('http://localhost:3000/api')
      .pipe(
        tap(songs => console.log('Songs fetched!')),
        catchError(this.handleError<Song[]>('Get Songs', []))
      );
  }
  updateSong(id, song: Song): Observable<any> {
    return this.http.put('http://localhost:3000/api/update-song/' + id, song, this.httpOptions)
      .pipe(
        tap(_ => console.log(`Song updated: ${id}`)),
        catchError(this.handleError<Song[]>('Update Song'))
      );
  }
  deleteSong(id): Observable<Song[]> {
    return this.http.delete<Song[]>('http://localhost:3000/api/delete-song/' + id, this.httpOptions)
      .pipe(
        tap(_ => console.log(`Song deleted: ${id}`)),
        catchError(this.handleError<Song[]>('Delete Song'))
      );
  }

  private handleError<T>(operation = 'operation', result?: T) {
    return (error: any): Observable<T> => {
      console.error(error);
      console.log(`${operation} failed: ${error.message}`);
      return of(result as T);
    };
  }
}

To make the Http request we imported HttpClientModule in AppModule, now import RxJS operators to send the HTTP request across the server and get the response. Import observable, of, catchError and tap along with HttpClient and HttpHeaders.

Display Data List & Delete

Now, we will display data list in Ionic mobile app. Open home/home.page.ts file and add the following code in it.

import { Component, OnInit } from '@angular/core';
import { SongService } from './../shared/song.service';
@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html',
  styleUrls: ['home.page.scss'],
})
export class HomePage implements OnInit {
  Songs: any = [];
  constructor(
    private songService: SongService
  ) {
  }
  ngOnInit() { }
  ionViewDidEnter() {
    this.songService.getSongList().subscribe((res) => {
      console.log(res)
      this.Songs = res;
    })
  }
  deleteSong(song, i) {
    if (window.confirm('Do you want to delete user?')) {
      this.songService.deleteSong(song._id)
        .subscribe(() => {
          this.Songs.splice(i, 1);
          console.log('Song deleted!')
        }
        )
    }
  }
}

Import the Angular service and inject into the constructor it will allow us to consume the REST APIs and render the data into the Ionic view. We use the ionViewDidEnter() page life cycle hook this hook will update the data in the Ionic view if the data is updated in the database. We also declared the deleteSong() function it helps in removing the song data from the Ionic as well as the mongoDB database.

Next, open the home/home.page.html file and include the following code in it.

<ion-header>
  <ion-toolbar>
    <ion-title>
      Ionic Music App
    </ion-title>
  </ion-toolbar>
</ion-header>
<ion-content>
  <ion-list class="ios list-ios hydrated">
    <ion-list-header class="ios hydrated">
      Song List
    </ion-list-header>
    <ion-item *ngFor="let song of Songs" class="item-label item ios in-list ion-focusable hydrated">
      <ion-label class="sc-ion-label-ios-h sc-ion-label-ios-s ios hydrated">
        <h2>{{song.song_name}}</h2>
        <h3>{{song.artist}}</h3>
      </ion-label>
      <div class="item-note" item-end>
        <button ion-button clear [routerLink]="['/edit-song/', song._id]">
          <ion-icon name="create" style="zoom:2.0"></ion-icon>
        </button>
        <button ion-button clear (click)="deleteSong(song, i)">
          <ion-icon name="trash" style="zoom:2.0"></ion-icon>
        </button>
      </div>
    </ion-item>
  </ion-list>

  <!-- fab placed to the bottom start -->
  <ion-fab vertical="bottom" horizontal="end" slot="fixed" routerLink="/add-song">
    <ion-fab-button>
      <ion-icon name="add"></ion-icon>
    </ion-fab-button>
  </ion-fab>
</ion-content>

Here, we used the Ionic HTML to display the data list in the Ionic view we are showing the data with the help of Angular’s *ngFor directive. Declare the routerLink directive and pass the edit-song route to navigate to the edit page.

To know more about Ionic’s UI components check out here.

Add Data in Ionic

To add the data, we need to import and register FormsModule and ReactiveFormsModule in page’s module. Now, we need to remember that we have to import these Angular 9 form services in every page’s module rather than using it globally in the app.module.ts file.

Open add-song.module.ts file and add the following code.

import { FormsModule, ReactiveFormsModule } from '@angular/forms';
@NgModule({
  imports: [
    FormsModule,
    ReactiveFormsModule
  ]
})

Open add-song.page.ts file and add the following code.

import { Component, OnInit, NgZone } from '@angular/core';
import { SongService } from './../shared/song.service';
import { Router } from '@angular/router';
import { FormGroup, FormBuilder } from "@angular/forms";

@Component({
  selector: 'app-add-song',
  templateUrl: './add-song.page.html',
  styleUrls: ['./add-song.page.scss'],
})
export class AddSongPage implements OnInit {
  songForm: FormGroup;
  constructor(
    private songAPI: SongService,
    private router: Router,
    public fb: FormBuilder,
    private zone: NgZone
  ) {
    this.songForm = this.fb.group({
      song_name: [''],
      artist: ['']
    })
  }
  ngOnInit() { }
  onFormSubmit() {
    if (!this.songForm.valid) {
      return false;
    } else {
      this.songAPI.addSong(this.songForm.value)
        .subscribe((res) => {
          this.zone.run(() => {
            console.log(res)
            this.songForm.reset();
            this.router.navigate(['/home']);
          })
        });
    }
  }
}

Open add-song.page.html file and add the following code.

<ion-header>
  <ion-toolbar class="ios hydrated">
    <ion-buttons slot="start">
      <ion-back-button defaultHref="home"></ion-back-button>
    </ion-buttons>
    <ion-title class="ios title-ios hydrated">Add Song</ion-title>
  </ion-toolbar>
</ion-header>

<ion-content>
  <ion-list lines="full" class="ion-no-margin ion-no-padding ios list-ios list-lines-full list-ios-lines-full hydrated">
    <form [formGroup]="songForm" (ngSubmit)="onFormSubmit()">
      <ion-item>
        <ion-label position="floating">Name</ion-label>
        <ion-input formControlName="song_name" type="text" required></ion-input>
      </ion-item>
      <ion-item>
        <ion-label position="floating">Artist</ion-label>
        <ion-input formControlName="artist" type="text" required>
        </ion-input>
      </ion-item>
      <ion-row>
        <ion-col>
          <ion-button type="submit" color="primary" shape="full" expand="block">Add Song</ion-button>
        </ion-col>
      </ion-row>
    </form>
  </ion-list>
</ion-content>

Ionic Edit Data

To edit the data you need to open the edit-song.module.ts file and add the following code in it.

import { FormsModule, ReactiveFormsModule } from '@angular/forms';
@NgModule({
  imports: [
    FormsModule,
    ReactiveFormsModule
  ]
})

Next, open the edit-song.page.ts file and add the following code inside of it.

import { Component, OnInit } from '@angular/core';
import { SongService } from './../shared/song.service';
import { ActivatedRoute, Router } from "@angular/router";
import { FormGroup, FormBuilder } from "@angular/forms";
@Component({
  selector: 'app-edit-song',
  templateUrl: './edit-song.page.html',
  styleUrls: ['./edit-song.page.scss'],
})
export class EditSongPage implements OnInit {
  updateSongForm: FormGroup;
  id: any;
  constructor(
    private songAPI: SongService,
    private actRoute: ActivatedRoute,
    private router: Router,
    public fb: FormBuilder
  ) {
    this.id = this.actRoute.snapshot.paramMap.get('id');
  }
  ngOnInit() {
    this.getSongData(this.id);
    this.updateSongForm = this.fb.group({
      song_name: [''],
      artist: ['']
    })
  }
  getSongData(id) {
    this.songAPI.getSong(id).subscribe(res => {
      this.updateSongForm.setValue({
        song_name: res['song_name'],
        artist: res['artist']
      });
    });
  }
  updateForm() {
    if (!this.updateSongForm.valid) {
      return false;
    } else {
      this.songAPI.updateSong(this.id, this.updateSongForm.value)
        .subscribe((res) => {
          console.log(res)
          this.updateSongForm.reset();
          this.router.navigate(['/home']);
        })
    }
  }
}

Next, open the edit-song.page.html file and add the following code inside of it.

<ion-header>
  <ion-toolbar class="ios hydrated">
    <ion-buttons slot="start">
      <ion-back-button defaultHref="home"></ion-back-button>
    </ion-buttons>
    <ion-title class="ios title-ios hydrated">Edit Song</ion-title>
  </ion-toolbar>
</ion-header>

<ion-content>
  <ion-list lines="full" class="ion-no-margin ion-no-padding ios list-ios list-lines-full list-ios-lines-full hydrated">
    <form [formGroup]="updateSongForm" (ngSubmit)="updateForm()">
      <ion-item>
        <ion-label position="floating">Song name</ion-label>
        <ion-input formControlName="song_name" type="text" required></ion-input>
      </ion-item>
      <ion-item>
        <ion-label position="floating">Artist</ion-label>
        <ion-input formControlName="artist" type="text" required>
        </ion-input>
      </ion-item>
      <ion-row>
        <ion-col>
          <ion-button type="submit" color="primary" shape="full" expand="block">Update</ion-button>
        </ion-col>
      </ion-row>
    </form>
  </ion-list>
</ion-content>

Conclusion

In this tutorial we learned how to create Ionic Hybrid CRUD mobile app. We also learned how to create REST APIs with the help of Node/Express and save the data from the Ionic frontend to MongoDB database.

To compare your code with this tutorial, you can download the complete code from this Github repo.

Digamber

I am Digamber, a full-stack developer and fitness aficionado. I created this site to bestow my coding experience with newbie programmers. I love to write on JavaScript, ECMAScript, React, Angular, Vue, Laravel.