Table des matières

IndexedDB

IndexedDB permet de stocker des données localement au niveau du client. Ce stockage peut être utile par exemple en mode itinérance, là où l'accès à un réseau n'est pas toujours possible.

Caractéristiques

Capacité

Dans la spécification du W3C, il n'y a pas de précision sur la capacité. En règle générale, on peut s'attendre à une limite de 50 mo qui peut être augmentée avec la permission de l'utilisateur. Chrome, par exemple, ne demande pas de permission.

Support des navigateurs

IndexedDB est supporté, au moment d'écrire ceci, par :

Il est moins bien supporté par :

Le mieux, pour ne pas avoir des informations désuètes, car cela évolue toujours, c'est de se référer à Can I Use IndexedDB.

Quelques outils qui peuvent aider à supporter IndexedDB sur plusieurs plateformes :

Opérations

Cycle d'événements

Quand on fait une requête d'ouverture à IndexedDB, on spécifie le nom de la base de données et sa version. Si la version spécifiée est plus élevée que la version actuelle d'IndexedDB, l'événement onupgradeneeded sera lancée. Sinon, ça ira directement au onsuccess. En cas d'erreur à tout moment, on peut aller à onerror.

Ouvrir une base de données

var openRequest = window.indexedDB.open('InitDB', 1);
 
openRequest.onupgradeneeded = function (e) {
  var newVersion  = e.target.result;
 
  if (!newVersion.objectStoreNames.contains('courses')) {
    newVersion.createObjectStore('courses',
      {
        autoIncrement: true
      }
    );
  }
};
 
openRequest.onerror = openRequest.onblocked = console.log;
 
openRequest.onsuccess = function (e) {
  initDB = e.target.result;
};

Supprimer une base de données

if (typeof initDB !== 'undefined') {
  console.log('Closing the database...');
 
  initDB.close();
 
  console.log('Attempting to delete the database...');
  var deleteRequest = indexedDB.deleteDatabase('InitDB');
  deleteRequest.onerror = deleteRequest.onblocked = console.log;
  deleteRequest.onsuccess = function () {
    console.log('Database deleted');
  };
} else {
  console.log('You must first create a database before attemping a delete.');
}

Opérations CRUD

Voici un exemple de modèle de base de données :

var db = {
  name: 'CrudDB',
  version: 1,
  instance: {},
  storeNames: {
    courses: 'courses'
  },
  defaultErrorHandler: function (e) {
    console.log(e);
  },
  setDefaultErrorHandler: function (request) {
 
    if ('onerror' in request) {
      request.onerror = db.defaultErrorHandler;
    }
 
    if ('onblocked' in request) {
      request.onblocked = db.defaultErrorHandler;
    }
 
  }
};

Insertion

L'insertion d'un objet dans la base de données utilise la méthode add().

var dt = new Date();
 
var course = {
    title: 'HTML5 Advanced Topics',
    author: {
        first: 'Craig',
        last: 'Shoemaker'
    },
    courseID: 'html5-advanced',
    insertDate: dt,
    modifiedDate: dt
};
 
var transaction = db.instance.transaction([db.storeNames.courses], 'readwrite');
 
var store = transaction.objectStore(db.storeNames.courses);
var addRequest = store.add(course);
 
db.setDefaultErrorHandler(addRequest);
 
addRequest.onsuccess = function (e) {
    console.log('Course added');
    console.log('key: ' + e.target.result);
};

Lecture

La lecture d'un objet dans la base de données utilise la méthode get().

Pour des raisons de performance, lorsqu'on lit des données, s'assurer d'être en mode readonly lorsqu'on instancie la transaction.

var transaction = db.instance.transaction([db.storeNames.courses], 'readonly');
 
var store = transaction.objectStore(db.storeNames.courses);
var key = 1;
var getRequest = store.get(key);
 
db.setDefaultErrorHandler(getRequest);
 
getRequest.onsuccess = function (e) {
    var course = e.target.result;
 
    if (course !== undefined) {
        console.log(course);
    } else {
        console.log('A course with the key of ' + key + ' does not exist');
    }
};

Mise à jour

La mise à jour est en fait une combinaison d'un getRequest (méthode get()) et d'un putRequest (méthode put()) du même objet modifié entre les deux opérations.

var transaction = db.instance.transaction([db.storeNames.courses], 'readwrite');
 
var store = transaction.objectStore(db.storeNames.courses);
var key = 1;
var getRequest = store.get(key);
 
db.setDefaultErrorHandler(getRequest);
 
getRequest.onsuccess = function (e) {
    var course = e.target.result;
 
    if (course !== undefined) {
        course.modifiedDate = new Date();
 
        var putRequest = store.put(course, key);
 
        db.setDefaultErrorHandler(putRequest);
 
        putRequest.onsuccess = function (e) {
            console.log('Course Updated');
        };
 
    } else {
        console.log('A course with the key of ' + key + ' does not exist');
    }
};

Suppression

La suppression d'un objet dans la base de données utilise la méthode delete().

var transaction = db.instance.transaction([db.storeNames.courses], 'readwrite');
 
var store = transaction.objectStore(db.storeNames.courses);
var key = 1;
var deleteRequest = store.delete(key);
 
db.setDefaultErrorHandler(deleteRequest);
 
deleteRequest.onsuccess = function (e) {
    console.log('Course deleted');
};

Curseurs, Index et Ranges

Créer un curseur

Un curseur permet de récupérer un ensemble de données. Pour créer un curseur, on utilise createIndex().

var store = newVersion.createObjectStore('people', { autoIncrement: true });
store.createIndex('lastName', 'lastName', { unique: false });
store.createIndex('age', 'age', { unique: false });

Range

Les fonctions sont disponibles sur l'interface IDBKeyRange.

On crée un range avec IDBKeyRange, par exemple:

var range = IDBKeyRange.upperBound(lastName);

Ensuite, on ouvre le curseur avec openCursor(range[, direction]).

request = index.openCursor(range, 'prev');

Les directions peuvent être prev, next, prevunique, nextunique.

Clés

Il y a plusieurs façons de déterminer la clé (key) des objets:

AutoIncrement

Avec cette façon de faire, la clé est une séquence qui est augmentée de 1 à chaque nouvel objet inséré. La clé autoIncrement ne fait pas partie des données. Donc, si on récupère un objet, celui-ci n'aura pas son identifiant associé avec lui.

keyPath

Une solution pour régler le problème de l'identifiant qui ne suit pas les données, c'est d'avoir un autoIncrement avec un keyPath id.

newVersion.createObjectStore(
  'AutoKeyWithPath', {
    keyPath: 'id',
    autoIncrement: true
  }
);

Le keyPath peut être différent et il n'est pas nécessaire d'avoir un autoIncrement avec celui-ci. Par exemple, pour une liste d'utilisateurs, le keyPath peut être le courriel.

newVersion.createObjectStore(
  'Users', {
    keyPath: 'email'
  }
);

UUID

Librairies JavaScript pour IndexedDB

Plusieurs librairies existent pour palier aux problèmes d'IndexedDB, voici quelques exemples :

À lire

Sources