© Carlos Rojas 2020
C. RojasBuilding Progressive Web Applications with Vue.js https://doi.org/10.1007/978-1-4842-5334-2_6

6. IndexedDB

Carlos Rojas1 
(1)
Medellin, Colombia
 

At some point in our development, we want to save transactional information when our app is offline. We could save it in cache storage, but this can be complex, such as in scenarios like managing the information in a CRUD (Create, Read, Update, Remove). To help in these instances, we have technology that has existed in web browsers for a very long time: IndexedDB.

What Is IndexedDB?

IndexedDB is a transactional database system that, unlike other options that we have in browsers, is perfect for storing significant amounts of data, such as catalogs or other types that require a quick information search. Some terms with which to familiarize yourself about IndexedDB are as follows:
  • Object store: Object stores are similar to tables or relations in traditional relational databases.

  • Database: Databases are where all the object stores are kept. You can create as many databases as you want, but usually one per app is sufficient.

  • Transaction: Every action in IndexedDB works through a transaction. To effect any action, you first need to create a transaction, then listen for events on completion. Transactions are useful for maintaining integrity. If one of the operations fails, the whole action is canceled.

One feature that makes IndexedDB perfect to be used with PWAs is that it is asynchronous. Previously, it was both synchronous and asynchronous. The synchronous API was intended for use only with web workers, but was removed from the spec because it was unclear whether it was needed. For more information on the asynchronous API, see https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API/Using_IndexedDB.

Using IndexedDB

In general terms, the process of using IndexedDB with our app is shown in Figure 6-1.
../images/483082_1_En_6_Chapter/483082_1_En_6_Fig1_HTML.png
Figure 6-1

IndexedDB workflow

Opening a DB

The process to open IndexedDB using JS is as follows:
var connection;
connection = window.indexedDB.open("notes", 1);
connection.onsuccess = (event) => {
// data
let dummyData = [];
// Opening successful process.
db = event.target.result;
writingObjectStore(dummyData);
readingObjectStore();
};
connection.onerror = (event) => {
// We handle the opening DB error.
console.error('error:', event.target.errorCode);
};

Initiating Read/Write in the Object Store

The proccess to write/read from IndexedDB using JS is as follows:
function writingObjectStore(dummyData) {
        // It can be read-only or readwrite.
        var transaction = db.transaction(['notes'], 'readwrite');
        // Adding the data in objectStore.
        let objectStore = transaction.objectStore("notes");
        dummyData.forEach(
            (note) => {
            let request = objectStore.add(note);
            request.onerror = (e) => {
              // Handle the error.
            };
            request.onsuccess = (e) => {
                console.log('Item added to indexedDB');
            };
            }
        );
}
function readingObjectStore() {
        let transaction = db.transaction(["notes"], "readonly");
        // Adding the data in objectStore.
        let objectStore = transaction.objectStore('notes') ;
        let request = objectStore.getAll();
        request.onsuccess = () => {
            request.result.forEach((item) => console.log('Items by name:', item));
            };
}

Deleting from the Object Store

The process to delete from IndexedDB using JS is as follows:
function deletingFromTheObjectStore() {
       // Deleting a registry.
       let request = db.transaction(["notes"], "readwrite")
                       .objectStore("notes")
                       .delete("title 1");
        request.onsuccess = (event) => {
              // It was deleted successfully.
              console.log('registry deleted');
        };
}
All the code together looks like this:
<script>
    (function() {
    var db;
    var connection;
    connection = window.indexedDB.open("notes", 1);
    connection.onupgradeneeded = (upgradeDb) => {
        var db = upgradeDb.target.result;
        if (!db.objectStoreNames.contains('notes')) {
            db.createObjectStore('notes', {keyPath: 'title'});
        }
    };
    connection.onsuccess = (event) => {
    // data
    let dummyData = [
    {
    "title": "title 1",
    "content": "PWA"
    },
    {
    "title": "title 2",
    "content": "Loren Ipsum"
    }
    ];
    // Opening successful process.
    db = event.target.result;
    writingObjectStore(dummyData);
    readingObjectStore();
    };
    connection.onerror = (event) => {
    // We handle the opening DB error.
    console.error('error:', event.target.errorCode);
    };
    function writingObjectStore(dummyData) {
        // It can be read-only or readwrite.
        var transaction = db.transaction(['notes'], 'readwrite');
        // Adding the data in objectStore.
        let objectStore = transaction.objectStore("notes");
        dummyData.forEach(
            (note) => {
            let request = objectStore.add(note);
            request.onerror = (e) => {
            };
            request.onsuccess = (e) => {
                console.log('Item added to indexedDB');
            };
            }
        );
    }
    function readingObjectStore() {
        let transaction = db.transaction(["notes"], "readonly");
        // Adding the data in objectStore.
        let objectStore = transaction.objectStore('notes') ;
        let request = objectStore.getAll();
        request.onsuccess = () => {
            request.result.forEach((item) => console.log('Items by name:', item));
            };
    }
    function deletingFromTheObjectStore() {
        // Deleting a registry.
        let request = db.transaction(["notes"], "readwrite")
                        .objectStore("notes")
                        .delete("title 1");
        request.onsuccess = (event) => {
            // It was deleted successfully.
            console.log('registry deleted');
        };
    }
    })();
    </script>
You can go there from the repo (https://github.com/carlosrojaso/appress-book-pwa) with
$git checkout indexedDB-plain
$serve -S indexedDB
Run the server, In your browser open Chrome DevTools and now you can see the same like in Figure 6-2.
../images/483082_1_En_6_Chapter/483082_1_En_6_Fig2_HTML.jpg
Figure 6-2

Using IndexedDB with plain JS

Using IndexedDB in VueNoteApp

As you can see, using IndexedDB is relatively easy, but at some point it can get messy with regard to design and implementation. This is the reason why there are libraries and wrappers that help us handle transactions more easily. I recommend localForage (similar to localStorage), but my favorite is Dexie.js. I like it because it allows me to use promises easily and is well documented. You can find the documentation at https://dexie.org/docs/.

To work with Dexie.js, we have to move to a new branch of IndexedDB in git:
$git checkout indexedDB
And we need to install Dexie.js in our project:
$npm install dexie --save
Then we create a configuration file for IndexedDB named indexedDB.js:
import Dexie from 'dexie';
let iDB = new Dexie('Notes');
const version = 1;
iDB.version(version).stores({
  notes: '++id, title'
});
export let indDB = iDB;
Later, we update Dashboard.vue to get the notes, and save and delete from IndexedDB:
<template>
  <div class="dashboard">
    <v-content>
      <Notes :pages="pages" @new-note="newNote" @delete-note="deleteNote"/>
    </v-content>
    <v-dialog v-model="dialog">
        <v-card>
        <v-card-title>
            <span class="headline">New Note</span>
        </v-card-title>
        <v-card-text>
            <v-container grid-list-md>
            <v-layout wrap>
                <v-flex xs12 sm12 md12>
                <v-text-field v-model="newTitle" value="" label="Title*" required></v-text-field>
                </v-flex>
                <v-flex xs12 sm12 md12>
                <v-textarea v-model="newContent" value="" label="Content"></v-textarea>
                </v-flex>
            </v-layout>
            </v-container>
            <small>*indicates required field</small>
        </v-card-text>
        <v-card-actions>
            <v-spacer></v-spacer>
            <v-btn color="blue darken-1" flat @click="closeModal()">Close</v-btn>
            <v-btn color="blue darken-1" flat @click="saveNote()">Save</v-btn>
        </v-card-actions>
        </v-card>
    </v-dialog>
  </div>
</template>
<script>
import {indDB} from '../indexedDB.js'
import Notes from './Notes.vue'
export default {
  name: 'Dashboard',
  components: {
    Notes
  },
  data: () => ({
    pages:[],
    newTitle: ",
    newContent: ",
    index: 0,
    dialog: false
  }),
  mounted() {
    indDB.open().catch(function (e) {
      console.log("Something happened opening indexed DB: " + e.stack);
    });
    this.getAllRows();
  },
  methods:  {
    newNote () {
      this.dialog = true;
    },
    saveNote () {
      const newItem = {
        title: this.newTitle,
        content: this.newContent
      };
      this.pages.push(newItem);
      this.index = this.pages.length - 1;
      indDB.transaction('rw',indDB.notes,() =>
      {
        indDB.notes.add(newItem);
      });
      this.resetForm();
      this.closeModal();
    },
    closeModal () {
      this.dialog = false;
    },
    getAllRows () {
      indDB.notes.each(note => {this.pages.push(note);});
    },
    deleteNote (item) {
      this.deleteRow(this.pages[item]);
      this.pages.splice( item, 1);
      this.index = Math.max(this.index - 1, 0);
    },
    deleteRow (item){
      const criteria = {'title': item.title};
      // Get primaryKey.
      indDB.notes.where(criteria).first(
        (result) => {
          indDB.notes.delete(result.id);
        }
      );
    },
    resetForm () {
      this.newTitle = ";
      this.newContent = ";
    }
  }
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>
You can check the IndexedDB database in Chrome DevTools in the Application tab (Figure 6-3).
../images/483082_1_En_6_Chapter/483082_1_En_6_Fig3_HTML.jpg
Figure 6-3

Chrome DevTools Application tab

From the Application tab, select Storage ➤ IndexedDB. You should see something like what is presented in Figure 6-4.
../images/483082_1_En_6_Chapter/483082_1_En_6_Fig4_HTML.jpg
Figure 6-4

Chrome DevTools IndexedDB storage view

Summary

IndexedDB is a transactional database system that is perfect for storing significant amounts of data, which makes it a good choice to use for our app.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.9.164