Storing data locally with IndexedDB

IndexedDB is a more robust client-side storage mechanism than local storage in a browser. Likewise, it provides offline capabilities and is based on saving and retrieving data as key-value pairs, but it lets you store significantly bigger amounts of data and allows for high performance searching using database keys. IndexedDB is supported in modern browsers, but is only partially supported in Internet Explorer above Version 10 (refer to http://caniuse.com/#feat=indexeddb for details).

How to do it...

You can find the code for this recipe in the using_indexeddb project. We use the same method from the Posting JSON-formatted data recipe in Chapter 7, Working with Web Servers, but now we only store the data locally in IndexedDB. The following is the relevant code from using_indexeddb.dart:

import 'dart:indexed_db';

void main() {
  //test if browser supports IndexedDB:
  if (!IdbFactory.supported) {
    window.alert("Sorry, this browser does not support IndexedDB");
    return;
    }
  js = new JobStore();
  //creating and opening the database:
  js.openDB();
  querySelector("#store").onClick.listen(storeData);
  }

storeData(Event e) {
  var job = _jobData();
  //writing data to IndexedDB
  js.add(job);
}

It is important that the data access code is isolated from the business logic code in job.dart. This is according to the principle of separation of concerns, which makes it a lot easier to change to another database system; only the data access code needs to be changed. The functionalities required to work with IndexedDB is found in the JobStore class in jobstore_idb.dart:

library store;

import 'dart:html';
import 'dart:async';
import 'dart:indexed_db';
import 'job.dart';

class JobStore {
  static const String JOB_STORE = 'jobStore';
  static const String TYPE_INDEX = 'type_index';
  Database _db;
  final List<Job> jobs = new List();
  
  Future openDB() {
    return window.indexedDB
    .open('JobDB',version: 1,onUpgradeNeeded: _initDb)
    .then(_loadDB)
    .catchError(print);
  }
  
  void _initDb(VersionChangeEvent e) {
    _db = (e.target as Request).result;
    var store = _db.createObjectStore(JOB_STORE, autoIncrement: true);
    store.createIndex(TYPE_INDEX, 'type', unique: false);
  }
  
  Future add(Job job) {
    // create transaction and get objectstore:
    var trans = _db.transaction(JOB_STORE, 'readwrite'),
    var store = trans.objectStore(JOB_STORE);
    store.add(job.toMap())
    // called when add completes
    .then((addedKey) {
  print(addedKey);
  job.dbKey = addedKey;
  });
  return trans.completed.then((_) {
    // called when transaction completes
    jobs.add(job);
    return job;
  });
}

Future _loadDB(Database db) {
  _db = db;
  var trans = db.transaction(JOB_STORE, 'readonly'),
  var store = trans.objectStore(JOB_STORE);
  var cursors = store.openCursor(autoAdvance: true).asBroadcastStream();
  cursors.listen((cursor) {
    var job = new Job.fromJson(cursor.value);
    jobs.add(job);
    });
    
    return cursors.length.then((_) {
    return jobs.length;
  });
}

Future update(Job job) {
  var trans = _db.transaction(JOB_STORE, 'readwrite'),
  var store = trans.objectStore(JOB_STORE);
  return store.put(job.toMap());
}

Future remove(Job job) {
  var trans = _db.transaction(JOB_STORE, 'readwrite'),
  var store = trans.objectStore(JOB_STORE);
  store.delete(job.dbKey);
  return trans.completed
  .then((_) {
    job.dbKey = null;
    jobs.remove(job);
  });
}

Future clear() {
  var trans = _db.transaction(JOB_STORE, 'readwrite'),
  var store = trans.objectStore(JOB_STORE);
  store.clear();
  return trans.completed
  .then((_) {
    jobs.clear();
    });
  }
}

The following is a screenshot along with a view of the IndexedDB database in Chrome Developer Tools:

How to do it...

How it works...

Interacting with IndexedDB is implemented in the dart:indexed_db library, so we have to import it wherever needed. It is always good to test whether the browser can store data in IndexedDB. To determine whether IndexedDB is supported, use the supported getter from the IdbFactory class. All interactions with IndexedDB are asynchronous and return Futures.

As shown in the second comment, openDB() is called. The window.indexedDB.open method is the method used to open or create a new database. If the given database name and version already exist, then that database is opened. For a new database name or higher version number, the upgrade needed event is triggered. In its event handler, this lacks clarity; insert object type for the screen text term too, where the records get an automatically incremented key, is created within database JobDB. We also show how to create an index in jobstore in the type field of class Job with the createIndex method. An IndexedDB database can contain many object stores, and each store can have many indexes. Handling the possible errors when opening a database with .catchError is really important. We also keep a central Database object _db to be used in the database methods.

Note

A common scenario is that all the records from a certain store are read into the app to show them after opening the database. This is done in _loadDB. If we look at the other methods (add, update, remove, and clear), we will see a common pattern, which all database operations must perform within a Transaction object. This retrieves as parameters the object store name JOB_STORE and a certain mode such as readonly or readwrite. Reading a number of records, such as in _loadDB, works through the opening cursor and then listening to it (using listen). Each listen event reads a new record through cursor.value, which is transformed into a Job object and added to the list.

An object to be stored must be given in the map format to the store's add method. When autoincrement is set to true for this store, the generated key number is returned as addedKey. When the transaction is complete (trans.completed.then), we add the newly created job to our list. The same pattern is followed in the update, remove, and clear methods, which call the methods put, delete, and clear, respectively, in the object store.

See also

  • Refer to the Using Browser Local Storage recipe in Chapter 5, Handling Web Applications, if you want to compare local storage with IndexedDB
..................Content has been hidden....................

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