Skip to main content

Angular4 Crud Operation In IndexedDB using JsStore

Introduction

Are you facing difficulties using IndexedDB in angular4 or angular2 ?

Well, you are reading the right article. You will be able to use IndexedDB all functionality in simple way after reading this article.
In this article i am going to explain - how to do crud operation in angular4 using indexedDB wrapper JsStore.

If you are thinking - what is JsStore ?

JsStore is an IndexedDB wrapper. It provides simple sql like api to do db operations and makes IndexedDB simple.

So In this article we will replace IndexedDB with JsStore .

Enough of the words , lets do something.

If you want the source code , you can download it from here - https://github.com/ujjwalguptaofficial/angular4.crud.jsstore

Prerequisites

You should have basic knowledge of 
  • html
  • css
  • javascript
  • angular2 or angular4
  • indexedb (not necessary)

Let's Code

Create Angular project

First we will have to create a angular project. I am showing you how to do with angular cli.
  1. Setup development environment - Install Node.js and npm if they are not installed.
  2. Install angular cli  -  execute the below code in a terminal window.
    npm install -g @angular/cli
      
  3. Create a new project - execute the below command to create an angular project.
    ng new <AppName>
      
    You can choose any application name - i am choosing "my-app".
    Please keep patience , It will take some time to create the project.
  4. Run Project - Goto the project directory and run the project.
    Cd my-app
      ng serve
It will take some time to compile the project. When it will be compiled , you will see a message - "Webpack compiled successfully".


Now open address - http://localhost:4200/ in browser . You will see output like this -


Create Component and Service

Run the below command to create the component - 
ng generate component student
This component will be used to write the business logic.

Now lets create the service to write the data logic. This way our code will be clean and easy to maintain.

Run the below code in terminal -
ng g service service/student
This will create a new student service inside the Service folder.

Now we need to add Service student inside component student . Copy the below code inside the file - student.component.ts

  
  import {
      Component,
      OnInit
  } from '@angular/core';
  import {
      StudentService
  } from '../service/student.service';
  
  @Component({
      selector: 'app-student',
      templateUrl: './student.component.html',
      styleUrls: ['./student.component.css'],
      providers: [StudentService]
  })
  
  export class StudentComponent implements OnInit {
  
      private _service: StudentService;
  
      constructor(service: StudentService) {
          this._service = service;
      }
  
      ngOnInit() {}
  
  }
  
  
In the above code - I have added the StudentService in the providers array, which will be injected as dependency in StudentComponent constructor by angular.

Now to check the new component. We need to add the component selector tag in app.component.html

Replace the code with the below code -
  
  <app-student>
  </app-student>
  
  
Run the command - "ng-serve". You should see the below output.



Download JsStore

Run the below command in terminal to install JsStore - 
npm install jsstore --save
it will download the JsStore and save the dependencies in package.json .

Since database connection will be used by all the services, so it will be good if we keep the connection object in a common service.

So lets create a common service . Execute the below code in terminal -
ng g service service/idb
This will create the IdbService in service folder

now we need to write the code to get the connection. Copy the below code in the idb.service.ts -


  import * as JsStore from 'jsstore';
  import * as workerPath from 'file-loader?name=scripts/[name].[hash].js!jsstore/dist/jsstore.worker.js';
  export class IdbService {
    // this will make sure that we are using one instance or one connection
    // otherwise multiple instance will be created and thus multiple worker and that may create some problems
    static idbCon = new JsStore.Instance(new Worker(workerPath));
  }
  
  

In the above code we are declaring idbCon as static which will make sure that we are maintaining only one jsstore instance. Every service will use this connection to do indexeddb process.

Now we need to use this connection in student service but imagine if we have multiple service they all needs to use the connection and may be there are some common codes which all service needs to use. This is why - we need to create a base service which will help us to remove code redundancy and make the service more cleaner and maintainable.

So lets create a base service - execute below cmd in terminal

  
  ng g service service/base
  
  

This will create the base service. Copy the below code in base service -

  
  import { Injectable } from '@angular/core';
  import { IdbService } from './idb.service';
  
  @Injectable({
    providedIn: 'root'
  })
  export class BaseService {
  
    constructor() {
      // turn on jsstore log status - help you to debug
      // off it in production or when you dont need
      this.connection.setLogStatus(true);
    }
  
    get connection() {
      return IdbService.idbCon;
    }
  }
  
  

Now we need to inherit base service in student service. So copy the below code

  
  import {
      Injectable
  } from '@angular/core';
  import {
      BaseService
  } from './base.service';
  
  @Injectable({
      providedIn: 'root'
  })
  export class StudentService extends BaseService {
      constructor() {
          super();
      }
  }
  
  
Run the below command to check if everything is good or not.
  
ng build
If it shows error - "In ambient enum declarations member initializer must be contant expression" then we need to update the typescript version.

To update the typescript version, run the below command -
npm install typescript@latest
now everything is setup, we just need to write the code.

Create Database

Creating database in JsStore is simple. It uses Sql approach - A database consist of multiple tables and a table consists of multiple columns.

Please take a look at the jsstore doc to create the database -

1. Create the table doc - http://jsstore.net/tutorial/table/

2. Create database doc -  http://jsstore.net/tutorial/database/

now lets write the code to create the database and get the connection. Copy the below code in the base.service.ts -
  
  import {
      IdbService
  } from './idb.service';
  import {
      IDataBase,
      DATA_TYPE,
      ITable
  } from 'jsstore';
  
  export class BaseService {
      dbname = 'Angular_Demo';
      constructor() {
          // turn on jsstore log status - help you to debug
          // turn off it in production or when you dont need
          this.connection.setLogStatus(true);
          this.initJsStore();
      }
  
      get connection() {
          return IdbService.idbCon;
      }
  
      initJsStore() {
          this.connection.isDbExist(this.dbname).then(isExist => {
              if (isExist) {
                  this.connection.openDb(this.dbname);
              } else {
                  const dataBase = this.getDatabase();
                  this.connection.createDb(dataBase);
              }
          }).catch(err => {
              // this will be fired when indexedDB is not supported.
              alert(err.message);
          });
      }
  
      private getDatabase() {
          const tblStudent: ITable = {
              name: 'Students',
              columns: [{
                      name: 'id',
                      primaryKey: true,
                      autoIncrement: true
                  },
                  {
                      name: 'name',
                      notNull: true,
                      dataType: DATA_TYPE.String
                  },
                  {
                      name: 'gender',
                      dataType: DATA_TYPE.String,
                      default: 'male'
                  },
                  {
                      name: 'country',
                      notNull: true,
                      dataType: DATA_TYPE.String
                  },
                  {
                      name: 'city',
                      dataType: DATA_TYPE.String,
                      notNull: true
                  }
              ]
          };
          const dataBase: IDataBase = {
              name: this.dbname,
              tables: [tblStudent]
          };
          return dataBase;
      }
  }
  
  

The above code will create the database when does not exist and open the db connection otherwise just open the connection.

Read data

JsStore provides select api to select the data from IndexedDB.

Please have a look at select api document - http://jsstore.net/tutorial/select 

Lets first create a student model - execute the below command
ng generate class model/student
This will generate a class Student in folder Model.

Now we need to define our student model. So copy the below code in the file student.ts under folder model -

  
  export class IStudent {
      id?: number;
      name: string;
      gender: string;
      country: string;
      city: string;
  }
  
  export class Student implements IStudent {
      id = 0;
      name = '';
      gender = 'm';
      country = '';
      city = '';
  }
  
  
  
In the above code i have also created the interface. It is a good practice to have an interface of class and you will see how we are going to use the interface.

Now lets write the code to select the students from indexedDB . Since it is data related code, so we will write it in Service.

Copy the below code inside the class StudentService -

  
   getStudents() {
      return this.connection.select({
        from: 'Students'
      });
  }
  
  

Looks simple and clean Right ?

Now we need to call getStudents from component student . Copy the below code inside the file - student.component.ts

  
  import {
      Component,
      OnInit
  } from '@angular/core';
  
  import {
      StudentService
  } from '../service/student.service';
  import {
      Student,
      IStudent
  } from '../model/student';
  
  
  
  @Component({
  
      selector: 'app-student',
  
      templateUrl: './student.component.html',
  
      styleUrls: ['./student.component.css'],
  
      providers: [StudentService]
  
  })
  
  
  
  export class StudentComponent implements OnInit {
  
      private service: StudentService;
      students: Array  = [];
  
      constructor(service: StudentService) {
          this.service = service;
      }
  
  
      ngOnInit() {
          this.getStudents();
      }
  
      getStudents() {
          this.service.getStudents().
          then(students => {
              this.students = students;
          }).catch(error => {
              console.error(error);
              alert(error.message);
          });
      }
  
  }
  
  
In the above code -
  1. A method getStudents is created which calls the method - getStudents in the service and store the list of students inside the class member - students.
We have written business and data logic to get the students. Now we need to write the code for view. So lets write it. 

Copy the below code inside the file - student.component.html

  
  <table>
      <tr>
          <th>Student Name</th>
          <th>Gender</th>
          <th>Country</th>
          <th>City</th>
      </tr>
      <tr *ngFor="let student of students" [attr.data-id]="student.id">
          <td>{{student.name}}
          </td>
          <td>{{student.gender=='m'?'Male':'Female'}}
          </td>
          <td>{{student.country}}
          </td>
          <td>{{student.city}}</td>
      </tr>
  </table>
  
  
In the above code -
  1. I have loop through "students" variable.
  2. I have also kept student id as attribute "data_id". We will use this later for identifying the student.
Before runing the code , lets add some styles. Copy the below css code in the student.component.css file -

  
  table {
    border-collapse: collapse;
  }
  
  table tr td,
  th {
    border: 1px solid black;
    text-align: center;
    padding: 10px;
  }
  
  contenteditable {
    border: 2px solid blue;
  }
  
  
Some styles will be used later. I hope you are able to understand these css.

Now everything is done. So lets see the output in browser . Run the command - "ng serve" and open the address localhost:4200 


There is only table heading but no data. This is because we have not inserted any data.

Insert Data

JsStore provides insert api for inserting data into IndexedDB.

Please have a look at insert api doc - http://jsstore.net/tutorial/insert

Now we need to write the code for adding student. So lets write the code in student service.

Copy the below method in  student.service.ts

  
   addStudent(student: IStudent) {
      return this.connection.insert({
        into: 'Students',
        return: true, // as id is autoincrement, so we would like to get the inserted value
        values: [student]
      });
  }
  
  
now we need to call the addStudent from Component. Copy the below code inside the student.component.ts -

  
  import { Component, OnInit } from '@angular/core';
  
  import { StudentService } from '../service/student.service';
  import { Student, IStudent } from '../model/student';
  
  
  
  @Component({
  
    selector: 'app-student',
  
    templateUrl: './student.component.html',
  
    styleUrls: ['./student.component.css'],
  
    providers: [StudentService]
  
  })
  
  
  
  export class StudentComponent implements OnInit {
  
    private service: StudentService;
    students: Array = [];
    newStudent: IStudent = new Student();
   
    constructor(service: StudentService) {
      this.service = service;
    }
  
  
    ngOnInit() {
      this.getStudents();
    }
  
    getStudents() {
      this.service.getStudents().
        then(students => {
          this.students = students;
        }).catch(error => {
          console.error(error);
          alert(error.message);
        });
    }
  
    addStudent() {
      this.service.addStudent(this.newStudent).
        then((addedStudents: IStudent[]) => {
          if (addedStudents.length > 0) {
            this.students.push(addedStudents[0]);
            this.clearNewStudent();
            alert('Successfully added');
          }
        }).catch(error => {
          console.error(error);
          alert(error.message);
        });
    }
  
    clearNewStudent() {
      this.newStudent = new Student();
    }
  
  }
  
  
  
In the above code -
  1. A class member newStudent has been created. This will be used to keep the new student information. Basically it will be used to bind with the view.
  2. A method addStudent is written. It will call the addStudent in the service supplying the new student which will add data in JsStore.
  3. A method clearNewStudent is written, which will clear the student value written in textbox.
now we have successfully written business logic and data logic. Its time for creating view for adding data.

Copy the below code inside the file - student.component.html

  
  <table>
      <tr>
          <th>Student Name</th>
          <th>Gender</th>
          <th>Country</th>
          <th>City</th>
          <th></th>
          <th></th>
      </tr>
      <tr>
          <td><input type="text" [(ngModel)]="newStudent.name" /></td>
          <td>
              <select [(ngModel)]="newStudent.gender">
          <option value="m">Male</option>
          <option value="f">FeMale</option>
        </select>
              <td><input type="text" [(ngModel)]="newStudent.country" /></td>
              <td><input type="text" [(ngModel)]="newStudent.city" /></td>
              <td>
                  <button (click)="addStudent()">Add</button>
              </td>
              <td>
                  <button (click)="clearNewStudent()">Cancel</button>
              </td>
      </tr>
      <tr *ngFor="let student of students" [attr.data-id]="student.id">
          <td>{{student.name}}
          </td>
          <td>{{student.gender=='m'?'Male':'Female'}}
          </td>
          <td>{{student.country}}
          </td>
          <td>{{student.city}}</td>
          <td></td>
          <td></td>
      </tr>
  </table>
  
  
In the above code -
  1. We have added a new table row which is used to add the student. We have binded _newStudent with the textbox.
  2. Method addStudent will be called for add buttton.
  3. method clearNewStudent will be called for cancel button.
Now lets see the output.


lets test the add function - 


Delete data

JsStore provides remove api for deleting data . 

Please read the delete api doc -  http://jsstore.net/tutorial/remove/

Lets write the code for deleting student. We need to delete a particular student, so we will use student id for deleting particular data.

Add the below method in student.service.ts - 

   deleteStudent(studentId: number) {
      return this.connection.remove({
        from: 'Students',
        where: {
          id: studentId
        }
      });
  }
  
lets call this code from component Student. Add the below function inside the component -

  
  deleteStudent(studentId) {
      this.service.deleteStudent(studentId).
        then(rowsDeleted => {
          if (rowsDeleted > 0) {
            const index = this.students.findIndex(student => student.id === studentId);
            this.students.splice(index, 1);
            alert('Successfully deleted');
          }
        }).catch(error => {
          console.error(error);
          alert(error.message);
        });
  }
  
  
Now we need to create the view to delete data. Just add the below code in last cell of second row -

  
  <td><button (click)="deleteStudent(student.id)">Delete</button></td>
  
  


Now everything is done. Lets test the code.



Update data

JsStore provides update api for updating data.

JsStore Update docs - http://jsstore.net/tutorial/update

Lets write the code for updating students in student service. Copy the below code in student.service.ts

  
  updateStudent(studentId: number, updateValue: IStudent) {
      return this.connection.update({
        in: 'Students',
        where: {
          id: studentId
        },
        set: updateValue
      });
    }
  
    getStudent(studentId: number) {
      return this.connection.select({
        from: 'Students',
        where: {
          id: studentId
        }
      });
    }
  
  
In the above code -
  1. updateStudent method is written, which will update the student by student id.
  2. getStudent method is written, which returns student by id. 
Now we need to call these methods from component

  
  import { Component, OnInit } from '@angular/core';
  
  import { StudentService } from '../service/student.service';
  import { Student, IStudent } from '../model/student';
  
  
  
  @Component({
  
    selector: 'app-student',
  
    templateUrl: './student.component.html',
  
    styleUrls: ['./student.component.css'],
  
    providers: [StudentService]
  
  })
  
  
  
  export class StudentComponent implements OnInit {
  
    private service: StudentService;
    students: Array = [];
    newStudent: IStudent = new Student();
    oldStudent: IStudent = new Student();
  
    constructor(service: StudentService) {
      this.service = service;
    }
  
  
    ngOnInit() {
      this.getStudents();
    }
  
    getStudents() {
      this.service.getStudents().
        then(students => {
          this.students = students;
        }).catch(error => {
          console.error(error);
          alert(error.message);
        });
    }
  
    addStudent() {
      this.service.addStudent(this.newStudent).
        then((addedStudents: IStudent[]) => {
          if (addedStudents.length > 0) {
            this.students.push(addedStudents[0]);
            this.clearNewStudent();
            alert('Successfully added');
          }
        }).catch(error => {
          console.error(error);
          alert(error.message);
        });
    }
  
    clearNewStudent() {
      this.newStudent = new Student();
    }
  
    deleteStudent(studentId) {
      this.service.deleteStudent(studentId).
        then(rowsDeleted => {
          if (rowsDeleted > 0) {
            const index = this.students.findIndex(student => student.id === studentId);
            this.students.splice(index, 1);
            alert('Successfully deleted');
          }
        }).catch(error => {
          console.error(error);
          alert(error.message);
        });
    }
  
    clearOldStudent() {
      this.oldStudent = new Student();
    }
  
    getStudent(studentId) {
      this.service.getStudent(studentId).
        then(students => {
          if (students.length > 0) {
            this.oldStudent = students[0];
          }
        }).catch(error => {
          console.error(error);
          alert(error.message);
        });
    }
  
    updateStudent() {
      const updatedValue: IStudent = {
        name: this.oldStudent.name,
        gender: this.oldStudent.gender,
        country: this.oldStudent.country,
        city: this.oldStudent.city
      };
      this.service.updateStudent(this.oldStudent.id, updatedValue).
        then(rowsUpdated => {
          if (rowsUpdated > 0) {
            const index = this.students.findIndex(student => student.id === this.oldStudent.id);
            this.students[index] = this.oldStudent;
            this.clearOldStudent();
            alert('Successfully updated');
          }
        }).catch(error => {
          console.error(error);
          alert(error.message);
        });
    }
  
  }
  
  
  
In the above code -
  1. A class member oldStudent is created. It is used to keep the student info which is in edit mode.
  2. Method getStudent is created, which will be used to set the oldStudent variable by student id.
  3. Method updateStudent is created, which will be used to update the edited student.
Cool, now we are left with the view. Copy the below html code -

  
  <table>
    <tr>
      <th>Student Name</th>
      <th>gender</th>
      <th>country</th>
      <th>city</th>
      <th></th>
      <th></th>
    </tr>
    <tr>
      <td>
        <input type="text" [(ngModel)]="newStudent.name" />
      </td>
      <td>
        <select [(ngModel)]="newStudent.gender">
          <option value="m">Male</option>
          <option value="f">FeMale</option>
        </select>
      </td>
      <td>
        <input type="text" [(ngModel)]="newStudent.country" />
      </td>
      <td>
        <input type="text" [(ngModel)]="newStudent.city" />
      </td>
      <td>
        <button (click)="addStudent()">Add</button>
      </td>
      <td>
        <button (click)="clearNewStudent()">Cancel</button>
      </td>
    </tr>
    <tr *ngFor="let student of students" [attr.data-id]="student.id">
      <td>
        <input type="text" *ngIf="student.editing" [(ngModel)]="oldStudent.name" />
        <span *ngIf="!student.editing">
          {{student.name}}
        </span>
      </td>
      <td>
        <select *ngIf="student.editing" [(ngModel)]="oldStudent.gender">
          <option value="m">Male</option>
          <option value="f">FeMale</option>
        </select>
        <span *ngIf="!student.editing">{{student.gender=='m'?'Male':'Female'}}</span>
      </td>
      <td>
        <input type="text" *ngIf="student.editing" [(ngModel)]="oldStudent.country" />
        <span *ngIf="!student.editing">{{student.country}}</span>
      </td>
      <td>
        <input type="text" *ngIf="student.editing" [(ngModel)]="oldStudent.city" />
        <span *ngIf="!student.editing">{{student.city}}</span>
      </td>
      <td>
        <button *ngIf="student.editing" (click)="updateStudent();student.editing=false">Update</button>
        <button *ngIf="!student.editing" (click)="student.editing=true;getStudent(student.id);">Edit</button>
      </td>
      <td>
        <button *ngIf="student.editing" (click)="student.editing=false">Cancel</button>
        <button *ngIf="!student.editing" (click)="deleteStudent(student.id)">Delete</button>
      </td>
    </tr>
  </table>
  
  
  
In the above code - 
  1. We have kept a temporary variable editing. It will be used to check whether the table cell is edit mode or not.
  2. When edit button is clicked - we set the editing to true and call the getStudent which will set the student info inside the oldStudent.
  3. When Update button is clicked - we set the editing to false and call the updateStudent which will update the student.
Rest are simple html code.

Now lets test the result in browser - 





Finally we are done with Crud Operation.
That was hell of coding. Thanks for reading it guys.

You can download the article implementation here - Angular example

Reference


Comments

  1. hi this is more helpful for me.Can you pls guide me to create multiple databases and using jsstore in indexeddb in angular5

    ReplyDelete
    Replies
    1. Hey - I will surely write it. Give me some time to do it.

      Delete
  2. Is this package compatible with Angular5?

    ReplyDelete
    Replies
    1. yes it is completely supported with angular. Check out the angular example in examples folder - https://github.com/ujjwalguptaofficial/JsStore/tree/master/examples/angular

      There were few problems with jsstore v1 but in v2 everything has been sorted out.

      Delete
  3. I truely excited to be here.You have explained everything very clearly.Iam much more impressed.the person who created this post is a genius and knows how to keep the readers connected.Check out Yiioverflow to know more about Yii framework and its wonderful feautres.

    ReplyDelete
  4. Really awesome tutorial..Thanks a lot...

    ReplyDelete
  5. This comment has been removed by the author.

    ReplyDelete
    Replies
    1. Hi Bhushan - are you getting any problem while using in service worker. Let me know. Although I see you have deleted the comments but I got the mail so thought I should ask.

      Delete

Post a Comment

Popular posts from this blog

Implementing Oauth2.0 authorization for Google in asp.net

Download Source Code
Introduction Now a days, security is a major concern for every service provider like Google, Yahoo, Microsoft etc. and that’s why every service provider which is providing some external service to another app is following the protocol defined by Oauth.

I am going to describe here, how to implement google oauth in asp.net app.
Prerequisites You should have basic knowledge of
C#Asp.net Steps for implementing For implementing Oauth we will have to follow series of steps – Create a project on google console.Get the client id and client secret from the project.Writing code for authorizing the user by google.Writing code for exchanging the authorization code for refresh token.Writing code for exchanging refresh token for access token. Don’t worry about the above steps- i am going to explain these in details with demo.
Let’s Start 1.Create a Project on Google console Goto https://console.developers.google.com and click on project -> new project



after that, a dialog b…

Angular4 Crud Operation In IndexedDB using JsStore V1

Introduction Are you facing difficulties using IndexedDB in angular4 or angular2 ?

Well, you are reading the right article. You will be able to use IndexedDB all functionality in simple way after reading this article. In this article i am going to explain - how to do crud operation in angular4 using indexedDB wrapper JsStore.
If you are thinking - what is JsStore ?

JsStore is an IndexedDB wrapper. It provides simple sql like api to do db operations and makes IndexedDB simple.

So In this article we will replace IndexedDB with JsStore .

Enough of the words , lets do something.

If you want the source code , you can download it from here - https://github.com/ujjwalguptaofficial/angular4.crud.jsstore
Prerequisites You should have basic knowledge of  htmlcssjavascriptangular2 or angular4indexedb (not necessary) Let's Code Create Angular project First we will have to create a angular project. I am showing you how to do with angular cli. Setup development environment - Install Node.js and …