IndexedDB in Angular with NgxIndexedDB

IndexedDB in Angular with NgxIndexedDB

In this tutorial you will learn about how to add and retrieve data including files to indexedDB using NgxIndexedDB in Angular 11

Building a web app sometimes you want to add offline capabilities to your application. You can do this using a client-side database like indexedDB. IndexedDB is a low-level API for client-side storage of significant amounts of structured data, including files/blobs. This API uses indexes to enable high-performance searches of this data. You can read more about IndexedDB here

In this tutorial we will use ngx-indexed-db, a service that wraps IndexedDB database in an Angular service combined with the power of observables, so let's dive in.

First we install angular, open your command line in the folder you want to start your application in and enter the command below.

npm install -g @angular/cli

Create a new app

ng new indexeddb-app
cd indexeddb-app
ng serve

If everything go as planned we should be provided with a screen like this.

Screenshot 2021-03-03 155546.jpg

Now let's add ngx-indexed-db by running the command below.

npm install ngx-indexed-db

Import the NgxIndexedDBModule and FormsModule and initiate them by adding the following line to your app.module.ts file:

import { DBConfig, NgxIndexedDBModule } from 'ngx-indexed-db';
import { FormsModule } from '@angular/forms';

const dbConfig: DBConfig = {
  name: 'coolDB',
  version: 1,
  objectStoresMeta: [{
    store: 'coolTable',
    storeConfig: {keyPath: 'id', autoIncrement: true},
    storeSchema: [
      {name: 'first_name', keypath: 'first_name', options: { unique: false}},
      {name: 'last_name', keypath: 'last_name', options: { unique: false}},
      {name: 'profile_photo', keypath: 'profile_photo', options: { unique: false}},
      {name: 'email', keypath: 'email', options: { unique: true}}
    ]
  }]
};

@NgModule({
    ...
    imports: [
        ...
        NgxIndexedDBModule.forRoot(dbConfig),
        FormsModule
    ],
    ...
})

Now open app.component.html and replace content with this code

<style>
  .container{
    margin: 30px 40px;
    padding: 20px;
    border-radius: 4px;
    box-shadow: 1px 2px 4px rgba(0, 0, 0, 0.3);
  }
  .control{
    margin: 20px
  }
</style>
<div class="container">
  <form #dataForm = "ngForm" (ngSubmit)="submitData(dataForm)" enctype="multipart/form-data" novalidate>
    <div class="control">
      <!-- error or success message will be shown here -->
      <p *ngIf="submitted">{{returnMsg}}</p>
    </div>
    <div class="control">
      <label>First name: </label>
      <input ngModel name="first_name" type="text">
    </div>


    <div class="control">
      <label>Last name: </label>
      <input ngModel name="last_name" type="text">
    </div>

    <div class="control">
      <label>Email: </label>
      <input ngModel name="email" type="email">
    </div>

    <div class="control">
      <label>Profile Picture: </label>
      <input ngModel name="photo" type="file" (change)="fileEvent($event, dataForm)">
    </div>

    <div class="control">
      <button type="submit">Submit</button>
    </div>
  </form>
</div>

We are using ngModel to pass data to our form. We define a fileEvent function which will get the uploaded file and pass the data as a File type which we will save in our DB.

Next we need to create our fileEvent and submitData functions so go ahead and open the app.component.ts file and paste the following code.

import { Component } from '@angular/core';
import { NgxIndexedDBService, IndexDetails } from 'ngx-indexed-db';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'indexeddb-app';
  returnMsg: string;
  submitted: boolean = false;
  fileData: any;

  //import the ngxindexed service
  constructor(private dbService: NgxIndexedDBService){

  }

  submitData(form){
    //get value of form
    const data = form.value;

    this.submitted = true;

    //check if form is valid
    if(data.first_name && data.last_name && data.email && data.photo){
      //add data to indexedDB
      this.dbService.add('coolTable', {
        first_name: data.first_name,
        last_name: data.last_name,
        profile_photo: data.photo,
        email: data.email
      })
      .subscribe((key) => {
        this.returnMsg = "Data added successfully";
        console.log('key: ', key);
      });
    }
    else{
      //return an error
      this.returnMsg = 'Enter all form values';
    }

  }

  //function to get attached file
  fileEvent(e, form){
    //get attached file
    this.fileData = e.target.files[0];

    //set the value of photo to attached file
    let data = form.value;
    data.photo = this.fileData;

  }
}

What is happening here? First we import the NgxIndexedDBService which will give us access to methods we can use to perform operations on our indexedDB. Then define our submitData function which we pass our form as a parameter and get the value of our form as a constant which we are calling data. Since we are submitting the form, we set submitted to true and then do a check to see if our form is valid by checking if all input fields contain a value.

If there are values in all of our input fields we go ahead with our indexedDB transaction or we return an error message. We use the add method provided by the NgxIndexedDBService which we imported as dbService. So we add to coolTable the values first_name, last_name, email and profile_photo.

The fileEvent function is use to get the attached value of the file input field and then we set the value of photo field our form to the file value which is of type File.

Screenshot 2021-03-09 112636.jpg

After submitting our form we can find our data saved successfully in our IndexedDB as shown in the above image. You can view by going to developer tools in your browser and clicking on Application. (In Google Chrome, just right click and select inspect or press Ctrl + Shift + I)

We can define a function to retrieve all data by index.

//function to retrive all by index
  getFromIndexDb(email){
    let index_detail:IndexDetails = {
      indexName: 'email',
      order: 'asc'
    }

    //get all data with the email field equal to the email passed
    this.dbService.getAllByIndex('coolTable', 'email', IDBKeyRange.only(email))
    .subscribe((kpis) => {
      console.log(kpis);
    })
  }

That's it folks, hoped this helped you can find more methods by visiting the package page. A github repo to this tutorial can be found here https://github.com/nanaVekta/indexedDB-tutorial

Photo credit: Unsplash.co