Avez-vous déjà souhaité que votre application Web conserve ses données même après la fermeture du navigateur ? C’est là qu’intervient l’API Javascript IndexedDB qui permet de stocker des données de manière persistante sur le client (sur son navigateur).
Contrairement à l’API LocalStorage traditionnel, IndexedDB offre une structure plus robuste et flexible, comparable à une base de données relationnelle. Imaginez pouvoir stocker des objets complexes, effectuer des requêtes efficaces et garantir l’intégrité des données grâce aux transactions.
Dans ce tutoriel, nous allons explorer les concepts fondamentaux d’IndexedDB et vous guider à travers des exemples concrets pour vous familiariser avec son utilisation. Et enfin, nous coderons un système de gestion de tâches en utilisant IndexedDB.
Les concepts clés d’IndexedDB
Avant de nous plonger dans le code, découvrons quelques notions importantes :
- Base de données : Un conteneur pour stocker vos données. Imaginez un classeur dans lequel vous regroupez des feuilles (les objets de stockage).
- Objet de stockage : Un espace dédié à un type de données spécifique. C’est comme une feuille dans votre classeur, où chaque ligne représente une entrée de données.
- Index : Un moyen d’organiser et d’accéder rapidement aux données. Imaginez des onglets dans votre classeur qui vous permettent de retrouver facilement les informations.
- Transaction : Un ensemble d’opérations groupées garantissant l’intégrité des données. Comme si vous mettiez à jour plusieurs feuilles simultanément dans votre classeur, sans risquer de perdre des informations.
- Clé : Un identifiant unique pour chaque entrée de données. C’est comme le numéro de téléphone de chaque contact dans votre carnet d’adresses.
Manipulation de la base de données
Commençons par établir une connexion à notre base de données IndexedDB :
const request = indexedDB.open('maBaseDeDonnees', 1); // Nom de la base de données et version
request.onsuccess = function(event) {
db = event.target.result;
console.log('Connexion à la base de données réussie !');
};
request.onerror = function(event) {
console.error('Erreur lors de l\'ouverture de la base de données :', event.target.error);
};
Nous lançons la procédure d’ouverture d’une base de données grâce à la fonction open()
. Nous indiquons le nom de la base de données à ouvrir ou à créer puis le numéro de version de cette base de données. Elle est incrémentée lors de modifications significatives de la structure de stockage.
Un événement est déclenché lorsque l’ouverture de la base de données réussit onsuccess
ou en cas d’erreur lors de l’ouverture onerror
. Il est crucial de gérer les erreurs potentielles lors de l’ouverture de la base de données pour garantir la fiabilité de votre application.
Création un objet de stockage
Maintenant que nous sommes connectés, il nous faut créer un objet de stockage pour stocker, par exemple, des produits :
const request = indexedDB.open('maBaseDeDonnees', 1); // Nom de la base de données et version
request.onupgradeneeded = function(event) {
// Obtention de la base de données
db = event.target.result;
// Créé un objet de stockage (table)
const objectStore = db.createObjectStore('produits', { keyPath: 'id', autoIncrement: true });
// Index unique sur la propriété nom
objectStore.createIndex('nom', 'nom', { unique: true });
console.log('Object store créée et index ajouté');
};
request.onsuccess = function(event) {
db = event.target.result;
console.log('Connexion à la base de données réussie !');
};
request.onerror = function(event) {
console.error('Erreur lors de l\'ouverture de la base de données :', event.target.error);
};
Ce code met en place une fonction qui sera exécutée lorsque la base de données IndexedDB sera créée ou mise à jour vers une nouvelle version.
La fonction onupgradeneeded
est déclenchée lors de la création initiale de la base de données ou lorsqu’une version plus récente de la base de données est ouverte.
db.createObjectStore(‘produits’, { keyPath: ‘id’, autoIncrement: true }); crée un objet de stockage (table) nommé produits
avec une clé primaire id
qui s’incrémente automatiquement.
Ensuite, on crée un index unique sur la propriété nom
de l’objet de stockage, garantissant que chaque valeur de nom
est unique.
Ajouter des données
Ajoutons quelques produits à notre objet de stockage :
// Nom de l'objet de stockage et mode d'accès
const transaction = db.transaction(['produits'], 'readwrite');
// Création de l'objet de stockage
const objectStore = transaction.objectStore('produits');
objectStore.add({id: 1, nom: 'T-shirt', prix: 15.99});
objectStore.add({id: 2, nom: 'Casquette', prix: 12.50});
On utilise la méthode add()
de l’objet de stockage objectStore
pour ajouter un premier produit. Ce produit est défini sous forme d’objet avec des propriétés id
, nom
et prix
.
const request = indexedDB.open('maBaseDeDonnees', 1); // Nom de la base de données et version
request.onupgradeneeded = function(event) {
const db = event.target.result;
if (!db.objectStoreNames.contains('produits')) {
const objectStore = db.createObjectStore('produits', { keyPath: 'id', autoIncrement: true });
objectStore.createIndex('nom', 'nom', { unique: true });
console.log('Object store créée et index ajouté');
}
};
request.onsuccess = function(event) {
db = event.target.result;
console.log('Connexion à la base de données réussie !');
// Nom de l'objet de stockage et mode d'accès
const transaction = db.transaction(['produits'], 'readwrite');
// Création de l'objet de stockage
const objectStore = transaction.objectStore('produits');
// Ajout de produit
objectStore.add({id: 1, nom: 'T-shirt', prix: 15.99});
objectStore.add({id: 2, nom: 'Casquette', prix: 12.50});
};
request.onerror = function(event) {
console.error('Erreur lors de l\'ouverture de la base de données :', event.target.error);
};
- La méthode
add()
permet d’ajouter de nouveaux objets de données dans un objet de stockage IndexedDB. - Chaque produit est représenté par un objet JavaScript avec des propriétés décrivant ses caractéristiques.
- L’événement
oncomplete
de la transaction garantit que le message de confirmation n’est affiché qu’une fois les ajouts terminés avec succès.
La méthode onupgradeneeded
a été modifiée. Avec cette modification, lors de la recharge de la page, la base de données vérifiera d’abord si l’objet de stockage produits
existe déjà avant de tenter de le recréer. Cela permettra de garantir que les données sont persistantes et que vous pourrez toujours récupérer le produit avec l’ID 1 même après le rechargement de la page.
Récupérer des données depuis IndexedDB
Récupérons un produit spécifique par son ID :
objectStore.get(1).onsuccess = function(event) {
const produit = event.target.result;
if (produit) {
console.log('Produit récupéré : ', produit);
} else {
console.log('Produit introuvable avec l\'ID 1');
}
};
Ce code tente de récupérer un objet avec l’ID 1 depuis l’objet de stockage nommé objectStore
. Si la requête réussit, la fonction onsuccess
est exécutée. À l’intérieur de cette fonction, l’objet récupéré est stocké dans la variable produit
.
Si un produit avec cet ID
est trouvé, ses détails sont affichés dans la console ; sinon, un message s’affiche indiquant que le produit est introuvable.
const request = indexedDB.open('maBaseDeDonnees', 1); // Nom de la base de données et version
request.onupgradeneeded = function(event) {
const db = event.target.result;
if (!db.objectStoreNames.contains('produits')) {
const objectStore = db.createObjectStore('produits', { keyPath: 'id', autoIncrement: true });
objectStore.createIndex('nom', 'nom', { unique: true });
console.log('Object store créée et index ajouté');
}
};
request.onsuccess = function(event) {
db = event.target.result;
console.log('Connexion à la base de données réussie !');
// Nom de l'objet de stockage et mode d'accès
const transaction = db.transaction(['produits'], 'readwrite');
// Création de l'objet de stockage
const objectStore = transaction.objectStore('produits');
// Les produits ont déjà été ajoutés précédemment
// objectStore.add({id: 1, nom: 'T-shirt', prix: 15.99});
// objectStore.add({id: 2, nom: 'Casquette', prix: 12.50});
// Récupération d'un produit
objectStore.get(1).onsuccess = function(event) {
const produit = event.target.result;
if (produit) {
console.log('Produit récupéré : ', produit);
} else {
console.log('Produit introuvable avec l\'ID 1');
}
};
};
request.onerror = function(event) {
console.error('Erreur lors de l\'ouverture de la base de données :', event.target.error);
};
Modifier des données depuis IndexedDB
Modifions le prix du produit avec l’ID 1 :
// Mettre à jour le prix du produit avec l'ID 2
objectStore.openCursor().onsuccess = function(event) {
const cursor = event.target.result;
if (cursor) {
if (cursor.value.id === 1) {
cursor.value.prix = 14.99; // Mettre à jour le prix
cursor.update(cursor.value); // Enregistrer la modification
console.log('Prix du produit mis à jour !');
return;
}
cursor.continue();
} else {
console.log('Produit introuvable avec l\'ID 2.');
}
};
Ce code ouvre un curseur sur l’objet de stockage pour parcourir tous les éléments. Pour chaque élément, il vérifie si l’ID est égal à 1. Si c’est le cas, il met à jour le prix de cet élément à 14.99
et enregistre la modification dans la base de données avec la méthode update()
. Ensuite, il affiche un message confirmant que le prix du produit a été mis à jour. Si aucun produit avec l’ID 1 n’est trouvé, il affiche un message indiquant que le produit est introuvable.
const request = indexedDB.open('maBaseDeDonnees', 1); // Nom de la base de données et version
request.onupgradeneeded = function(event) {
const db = event.target.result;
if (!db.objectStoreNames.contains('produits')) {
const objectStore = db.createObjectStore('produits', { keyPath: 'id', autoIncrement: true });
objectStore.createIndex('nom', 'nom', { unique: true });
console.log('Object store créée et index ajouté');
}
};
request.onsuccess = function(event) {
db = event.target.result;
console.log('Connexion à la base de données réussie !');
// Nom de l'objet de stockage et mode d'accès
const transaction = db.transaction(['produits'], 'readwrite');
// Création de l'objet de stockage
const objectStore = transaction.objectStore('produits');
// Mettre à jour le prix du produit avec l'ID 2
objectStore.openCursor().onsuccess = function(event) {
const cursor = event.target.result;
if (cursor) {
if (cursor.value.id === 1) {
cursor.value.prix = 14.99; // Mettre à jour le prix
cursor.update(cursor.value); // Enregistrer la modification
console.log('Prix du produit mis à jour !');
return;
}
cursor.continue();
} else {
console.log('Produit introuvable avec l\'ID 2.');
}
};
// Récupération d'un produit
objectStore.get(1).onsuccess = function(event) {
const produit = event.target.result;
if (produit) {
console.log('Produit récupéré : ', produit);
} else {
console.log('Produit introuvable avec l\'ID 1');
}
};
};
request.onerror = function(event) {
console.error('Erreur lors de l\'ouverture de la base de données :', event.target.error);
};
Supprimer des données
Supprimons le produit avec l’ID 1 :
objectStore.delete(1).onsuccess = function(event) {
if (event.target.result) {
console.log('Produit avec l\'ID 1 supprimé !');
} else {
console.log('Produit introuvable avec l\'ID 1');
}
};
Ce code supprime un élément avec l’ID 1 de l’objet de stockage. S’il réussit à supprimer l’élément avec succès, il affiche un message indiquant que le produit avec l’ID 1 a été supprimé. Si aucun produit avec l’ID 1 n’est trouvé dans l’objet de stockage, il affiche un message indiquant que le produit est introuvable.
const request = indexedDB.open('maBaseDeDonnees7', 1); // Nom de la base de données et version
request.onupgradeneeded = function(event) {
const db = event.target.result;
if (!db.objectStoreNames.contains('produits')) {
const objectStore = db.createObjectStore('produits', { keyPath: 'id', autoIncrement: true });
objectStore.createIndex('nom', 'nom', { unique: true });
console.log('Object store créée et index ajouté');
}
};
request.onsuccess = function(event) {
db = event.target.result;
console.log('Connexion à la base de données réussie !');
// Nom de l'objet de stockage et mode d'accès
const transaction = db.transaction(['produits'], 'readwrite');
// Création de l'objet de stockage
const objectStore = transaction.objectStore('produits');
objectStore.delete(1).onsuccess = function(event) {
if (event.target.result) {
console.log('Produit avec l\'ID 1 supprimé !');
} else {
console.log('Produit introuvable avec l\'ID 1');
}
};
// Récupérer tous les produits et les afficher dans la console
objectStore.getAll().onsuccess = function(event) {
console.log('Produits récupérés : ', event.target.result);
};
};
request.onerror = function(event) {
console.error('Erreur lors de l\'ouverture de la base de données :', event.target.error);
};
Fermer la connexion à la base de données
N’oubliez pas de fermer la connexion à la base de données lorsque vous avez terminé :
transaction.oncomplete = function() {
db.close();
console.log('Connexion à la base de données fermée.');
};
IndexedDB offre un moyen puissant et flexible de stocker des données de manière persistante dans vos applications Web. En maîtrisant ses concepts fondamentaux et en explorant ses fonctionnalités avancées, vous pouvez créer des expériences utilisateur plus riches et plus robustes.
Dans le prochain chapitre, nous allons coder un gestionnaire de tâche avec IndexedDB
Fondateur de l’agence Créa-troyes.
Intervenant en Freelance.
Contactez-moi