| [ Web Proxy ] |
| Viewing: https://developer.mozilla.org/fr/docs/Web/API/IndexedDB_API/Using_IndexedDB | [Back] [Original] |
Get to know MDN better
Cette page a t traduite partir de l'anglais par la communaut. Vous pouvez contribuer en rejoignant la communaut francophone sur MDN Web Docs.
IndexedDB est un moyen de stocker des donnes de manire persistante dans un navigateur. Cela vous laisse crer des applications web avec de riches possibilits de requtes indpendamment de la disponibilit du rseau puisque vos applications peuvent fonctionner en ligne ou hors-ligne.
Ce tutoriel vous guide travers l'utilisation de l'API asynchrone de IndexedDB. Si vous n'tes pas familier avec le principe de IndexedDB, vous devriez d'abord lire les concepts basiques d'IndexedDB.
Pour la documentation de rfrence sur l'API d'IndexedDB, voyez l'article IndexedDB et ses sous-parties, qui dtaille les types d'objets utiliss par IndexedDB, ainsi que les mthodes sur l'API asynchrone (l'API synchrone a t retire de la spcification).
Le modle de base qu'IndexedDB utilise est le suivant :
Maintenant que nous avons ces grands concepts en poche, nous pouvons voir des choses plus concrtes.
tant donn que la spcification volue encore, les implmentations actuelles de IndexedDB se cachent sous les prfixes du navigateur. Les fournisseurs de navigateurs peuvent avoir des implmentations diffrentes de l'API IndexedDB standard jusqu' ce que la spcification se soit solidifie. Mais une fois qu'un consensus est atteint sur la norme, les fournisseurs l'implmentent sans les balises de prfixe. Actuellement, certaines implmentations ont supprim le prfixe : Internet Explorer 10, Firefox 16, Chrome 24. Lorsqu'ils utilisent un prfixe, les navigateurs bass sur Gecko utilisent le prfixe moz, tandis que les navigateurs WebKit utilisent le prfixe webkit.
Au cas o vous souhaiteriez tester votre code dans des navigateurs qui utilisent toujours un prfixe, vous pouvez utiliser le code suivant :
// Sur la ligne suivante, vous devez inclure les prfixes des implmentations que vous souhaitez tester.
window.indexedDB =
window.indexedDB ||
window.mozIndexedDB ||
window.webkitIndexedDB ||
window.msIndexedDB;
// N'UTILISEZ PAS "var indexedDB = ..." si vous n'tes pas dans une fonction.
// De plus, vous pourriez avoir besoin de rferences des objets window.IDB*:
window.IDBTransaction =
window.IDBTransaction ||
window.webkitIDBTransaction ||
window.msIDBTransaction;
window.IDBKeyRange =
window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange;
// (Mozilla n'a jamais prfix ces objets, donc nous n'avons pas besoin de window.mozIDB*)
Faites attention aux implmentations qui utilisent un prfixe ; elles peuvent tre bogues, incompltes, voire suivre une ancienne version de la spcification. Il n'est donc pas recommand d'utiliser en production. Il serait prfrable de ne pas supporter ces navigateurs :
if (!window.indexedDB) {
window.alert(
"Votre navigateur ne supporte pas une version stable d'IndexedDB. Quelques fonctionnalits ne seront pas disponibles.",
);
}
On commence l'ensemble du processus comme ceci :
// Ouvrons notre premire base
var request = window.indexedDB.open("MyTestDatabase", 3);
Vous avez vu ? Ouvrir une base de donnes est comme n'importe quelle autre opration vous avez juste le "demander".
La requte "open" n'ouvre pas la base de donnes ni ne dmarre une transaction aussitt. L'appel de la fonction open() retourne un objet IDBOpenDBRequest avec un rsultat (success) ou une valeur d'erreur qui permet de la grer comme un vnement. La plupart des autres fonctions asynchrones dans IndexedDB fonctionnent de la mme faon ; Elles retournent un objet IDBRequest avec le rsultat ou une erreur. Le rsultat de la fonction "open" est une instance de IDBDatabase.
Le second paramtre de la mthode open est la version de la base de donnes. La version de la base dtermine le schma de celle-ci Les objets stocks dans la base de donnes et leur structure. Si la base de donnes n'existe pas dj, elle est cre via l'opration open(), puis, un vnement onupgradeneeded est dclench et vous crez le schma de la base dans le gestionnaire pour cet vnement. Si la base de donnes existe, mais que vous spcifiez un numro de version plus lev, un vnement onupgradeneeded est dclench immdiatement, vous permettant de mettre jour le schma dans son gestionnaire plus d'informations dans Updating the version of the database plus bas et la page rfrence IDBFactory.open.
Attention :
Le numro de version est un nombre "unsigned long long" ce qui signifie qu'il peut s'agir d'un entier trs grand. Cela veut galement dire que vous ne pouvez pas utiliser de rl, sinon, il sera converti au nombre entier le plus proche (infrieur) et la transaction peut ne pas dmarrer ou ne pas dclencher l'vnement upgradeneeded. Par exemple, n'utilisez pas 2.4 comme un numro de version :
var request = indexedDB.open("MyTestDatabase", 2.4); // Ne faites pas a, mme si la version sera arrondie 2
La premire chose que vous ferez avec la plupart des requtes que vous gnrerez sera d'ajouter des gestionnaires de succs ou d'erreurs :
request.onerror = function (event) {
// Faire quelque chose avec request.errorCode !
};
request.onsuccess = function (event) {
// Faire quelque chose avec request.result !
};
Laquelle de ces deux fonctions, onsuccess() or onerror(), sera appele ? Si tout se passe bien, un vnement success (qui est un vnement DOM dont la proprit type est "success") est dclench avec request comme cible. Une fois dclench, la fonction onsuccess() de request est lance avec l'vnement success comme argument. S'il y avait un quelconque problme, un vnement erreur (qui est un vnement DOM dont la proprit type est dfinie "error") est lance dans request. Cela dclenche la fonction onerror() avec l'vnement d'erreur comme argument.
L'API IndexedDB est conue pour minimiser le recours la gestion des erreurs, donc vous ne serez pas amen voir beaucoup d'vnements erreurs (du moins, pas tant que vous utilisez l'API !). Cependant, dans le cas d'une ouverture de base de donnes, il y a quelques conditions qui gnrent des vnements d'erreurs. Le problme le plus courant est que l'utilisateur a dcid d'interdire l'accs la cration de base de donnes. Un des principaux objectifs d'IndexedDB est de permettre un stockage important de donnes pour l'utilisation hors-ligne. (Pour en savoir plus sur la capacit de stockage de chaque navigateur, voyez Limites de stockage).
videmment, les navigateurs ne peuvent permettre qu'une publicit en ligne ou un site malicieux pollue votre ordinateur, donc ils informent l'utilisateur la premire fois qu'une application web tente d'ouvrir un espace de stockage IndexedDB. L'utilisateur peut choisir de permettre ou refuser l'accs. En ce qui concerne l'utilisation d'IndexedDB en mode priv, les donnes restent en mmoire jusqu' ce que la session prive soit close (Navigation prive pour Firefox et mode Incognito pour Chrome, mais dans Firefox, cela n'est pas encore implment depuis novembre 2015, aussi vous ne pouvez pas utiliser IndexedDB dans le mode priv de Firefo du tout).
Maintenant, en admettant qu'un utilisateur ait accept la cration d'une base, et que vous receviez un vnement "success" qui dclenche le callback (rappel) "success" ; que se passe-il aprs ? La requte a gnr un appel indexedDB.open(), donc request.result est une instance de IDBDatabase, et vous voulez garder en mmoire cela pour plus tard. Votre code devrait ressembler ceci :
var db;
var request = indexedDB.open("MyTestDatabase");
request.onerror = function (event) {
alert("Pourquoi ne permettez-vous pas ma web app d'utiliser IndexedDB?!");
};
request.onsuccess = function (event) {
db = event.target.result;
};
Comme mentionn ci-dessus, les vnements d'erreur gnrent des info-bulles. Ils sont rattachs la requte qui a gnr l'erreur, puis la bulle de l'vnement est transmis la transaction, et enfin l'objet de la base de donnes. Si vous souhaitez viter d'ajouter un gestionnaire d'erreurs chaque requte, vous pouvez en ajouter un unique l'objet de la base de donne, de cette manire :
db.onerror = function (event) {
// Gestionnaire d'erreur gnrique pour toutes les erreurs de requtes de cette base
alert("Database error: " + event.target.errorCode);
};
Une des erreurs courantes possibles lorsqu'on ouvre une base de donnes, c'est VER_ERR. Celle-ci indique que la version de la base de donnes stocke sur le disque est suprieure la version que vous tes en train d'essayer d'ouvrir. C'est un cas qui doit toujours tre pris en considration par le gestionnaire d'erreurs.
Lorsque vous crez une nouvelle base de donnes, ou que vous augmentez le numro de version d'une base existante (en spcifiant un numro de version suprieur celui que vous aviez auparavant, lors de Ouvrir une base de donnes), l'vnement onupgradeneeded sera dclench et un objet IDBVersionChangeEvent sera pass un vnement onversionchange dans request.result (la variable db dans l'exemple). Dans le gestionnaire d'vnement upgradeneeded, vous devez crer les objets de stockage requis pour cette version de base :
// Cet vnement est seulement implment dans des navigateurs rcents
request.onupgradeneeded = function (event) {
var db = event.target.result;
// Cre un objet de stockage pour cette base de donnes
var objectStore = db.createObjectStore("name", { keyPath: "myKey" });
};
Dans ce cas, la base de donnes disposera aussitt des objets de stockage de la version prcdente de la base, donc vous n'aurez pas crer de nouveau ces objets de stockage. Vous aurez seulement besoin de crer de nouveaux objets de stockage, ou d'en supprimer de la version prcdente si vous n'en avez plus besoin. Si vous avez besoin de changer un objet de stockage existant (par exemple, pour changer la keyPath), alors vous devez supprimer l'ancien objet de stockage et le crer nouveau avec les nouveaux paramtres. Notez que ceci supprimera les informations dans l'objet de stockage ! Si vous avez besoin de sauvegarder ces informations, vous devez les lire et les sauvegarder quelque part avant de mettre jour la base de donnes.
Essayer de crer un objet de stockage avec un nom dj existant (ou essayer de supprimer un objet de stockage avec un nom qui n'existe pas encore) renverra une erreur.
Si l'vnement onupgradeneeded quitte avec succs, le gestionnaire onsuccess de la requte d'ouverture de la base de donnes sera dclench.
Maintenant, structurons la base de donnes. IndexedDB utilise des objets de stockage plutt que des tableaux, et une seule base de donnes peut contenir un nombre quelconque d'objets de stockage. Chaque fois qu'une valeur est stocke dans un objet de stockage, elle est associe une cl. Il y a diffrentes manires pour une cl d'tre dfinie, selon que l'objet de stockage utilise un chemin de cl ou un gnrateur de cl.
Le tableau suivant montre les diffrentes manires d'attribuer des cls.
Key Path chemin de cl (keyPath) |
Key Generator gnrateur de cl (autoIncrement) |
Description |
|---|---|---|
| Non | Non | L'objet de stockage peut contenir n'importe quel type de valeur, mme des valeurs primitives comme des nombres ou des chanes de caractres. Vous devez fournir un argument cl spar chaque fois que vous souhaitez ajouter une nouvelle valeur. |
| Oui | Non | L'objet de stockage peut contenir des objets JavaScript. Les objets doivent avoir une proprit qui a le mme nom que le key path. |
| Non | Oui | L'objet de stockage peut contenir n'importe quel type de valeur. La cl est gnre pour vous automatiquement, ou vous pouvez fournir un argument cl spar si vous voulez utiliser une cl spcifique. |
| Oui | Oui | L'objet de stockage peut contenir des objets JavaScript. Normalement, une cl est gnre, et sa valeur est stocke dans l'objet dans une proprit avec le mme nom que le key path. Cependant, si une telle proprit existe, sa valeur est utilise en tant que cl, plutt que la gnration d'une nouvelle cl. |
Vous pouvez aussi crer des index sur un objet de stockage, condition que l'objet de stockage contienne des objets, et non des primitives. Un index vous permet de consulter les valeurs stockes dans un objet de stockage en utilisant la valeur d'une proprit de l'objet stock, plutt que la cl de l'objet.
En outre, les index ont la capacit d'appliquer des contraintes simples sur les donnes stockes. En paramtrant l'option unique lorsque l'on cre un index, ce dernier fait que deux objets ne peuvent tre enregistrs en ayant la mme valeur pour le chemin de cl de l'index. Par exemple, si vous avez un objet de stockage qui contient un ensemble de personnes, et que vous voulez vous assurer que deux personnes n'aient pas la mme adresse de courriel, vous pouvez utiliser un index avec le paramtre unique true.
Cela peut sembler confus, mais ce simple exemple devrait illustrer ces concepts. D'abord, nous dfinissons quelques donnes client utiliser dans notre exemple :
// Voici quoi ressemblent nos donnes client.
const customerData = [
{ ssn: "444-44-4444", name: "Bill", age: 35, email: "bill@company.com" },
{ ssn: "555-55-5555", name: "Donna", age: 32, email: "donna@home.org" },
];
Bien sr, vous n'utiliseriez pas le numro de scurit sociale comme cl primaire dans une table clients parce que tout le monde n'a pas de numro de scurit sociale, et vous pourriez stocker leur date de naissance au lieu de leur ge, mais laissons ces choix non pertinents pour des raisons de commodit et continuons.
Maintenant, voyons la cration d'une base de donnes pour stocker ces donnes :
const dbName = "the_name";
var request = indexedDB.open(dbName, 2);
request.onerror = function (event) {
// Gestion des erreurs.
};
request.onupgradeneeded = function (event) {
var db = event.target.result;
// Crer un objet de stockage qui contient les informations de nos clients.
// Nous allons utiliser "ssn" en tant que cl parce qu'il est garanti d'tre
// unique - du moins, c'est ce qu'on en disait au lancement.
var objectStore = db.createObjectStore("customers", { keyPath: "ssn" });
// Crer un index pour rechercher les clients par leur nom. Nous pourrions
// avoir des doublons (homonymes), alors on n'utilise pas d'index unique.
objectStore.createIndex("name", "name", { unique: false });
// Crer un index pour rechercher les clients par leur adresse courriel. Nous voulons nous
// assurer que deux clients n'auront pas la mme, donc nous utilisons un index unique.
objectStore.createIndex("email", "email", { unique: true });
// Utiliser la transaction "oncomplete" pour tre sr que la cration de l'objet de stockage
// est termine avant d'ajouter des donnes dedans.
objectStore.transaction.oncomplete = function (event) {
// Stocker les valeurs dans le nouvel objet de stockage.
var customerObjectStore = db
.transaction("customers", "readwrite")
.objectStore("customers");
for (var i in customerData) {
customerObjectStore.add(customerData[i]);
}
};
};
Comme indiqu prcdemment, onupgradeneeded est le seul endroit o vous pouvez modifier la structure de la base de donnes. Dans cette mthode, vous pouvez crer et supprimer des objets de stockage, construire et supprimer des index.
Les objets de stockage sont crs avec un simple appel createObjectStore(). La mthode prend le nom du stockage et un paramtre de type objet. Mme si les paramtres sont optionnels, ils vous laissent dfinir d'importantes proprits et redfinir le type d'un objet de stockage que vous voulez crer. Dans notre cas, nous avons demand un objet de stockage nomm "customers" et dfini un keyPath, qui est la proprit rendant unique un objet individuel dans le stockage. Cette proprit dans l'exemple est "ssn" puisqu'un numro de scurit sociale est garanti unique. "ssn" doit tre prsent sur chaque objet stock dans objectStore.
Nous avons aussi demand un index nomm name qui examine la proprit name dans les objets stocks. Comme avec createObjectStore(), createIndex() prend un paramtre de type objet facultatif (options) qui dfinit le type d'index crer. Ajouter des objets qui n'auront pas de proprit name fonctionnera, mais ces objets n'apparatront pas dans l'index name .
Nous pouvons rcuprer les objets client stocks, en utilisant directement leur ssn dans l'objet de stockage, ou en utilisant leur nom via l'index name. Pour en savoir plus sur ce fonctionnement, se rfrer la section Utiliser un index.
Paramtrer un marqueur autoIncrement lorsque l'on cre un objet de stockage activera le gnrateur de cls pour cet objet de stockage. Par dfault, ce marqueur n'est pas dfini.
Avec la gnrateur de cls, une cl sera gnre automatiquement lorsque vous ajoutez une valeur dans un objet de stockage. Le compteur initial pour la gnration de cls est toujours dfini 1 lorsque l'objet de stockage est cr pour la premire fois. Fondamentalement, une nouvelle cl auto-gnre sera incrmente de 1 par rapport la prcdente. Le nombre courant d'un gnrateur de cl ne dcroit jamais, moins qu'un rsultat d'opration sur la base soit annul, par exemple, l'abandon d'une transaction sur la base. En consquence, supprimer un enregistrement, voire l'ensemble des enregistrements d'un objet de stockage n'affecte jamais le gnrateur de cls d'un objet de stockage.
Nous pouvons crer un autre objet de stockage avec un gnrateur de cls comme ci-dessous :
// Ouverture d'indexedDB.
var request = indexedDB.open(dbName, 3);
request.onupgradeneeded = function (event) {
var db = event.target.result;
// Cration d'un autre objet appel "names" avec l'option autoIncrement dfinie true.
var objStore = db.createObjectStore("names", { autoIncrement: true });
// Puisque l'objet "names" a un gnrateur de cls, la cl pour la valeur name est gnre automatiquement.
// Les enregistrements ajouts ressembleront ceci :
// key : 1 => value : "Bill"
// key : 2 => value : "Donna"
for (var i in customerData) {
objStore.add(customerData[i].name);
}
};
Pour plus de dtails sur le gnrateur de cls, voyez "W3C Key Generators".
Avant de faire quoi que ce soit avec votre nouvelle base de donnes, vous aurez besoin de dmarrer une transaction. Les transactions viennent de l'objet base de donnes, et vous devez spcifier sur quel objet vous souhaitez faire pointer la transaction. Une fois dans la transaction, vous pouvez accder l'objet de stockage qui contient vos donnes et faire vos requtes. Puis, vous devez dcider si vous allez appliquer des changements la base de donnes, ou si vous avez juste besoin de la lire. Les transactions disposent de trois modes disponibles: readonly (lecture seule), readwrite (lecture/criture), et versionchange (changement de version).
Pour changer le "schma" ou la structure de la base de donnes qui implique de crer ou supprimer des objets de stockage ou des index la transaction doit tre en mode versionchange. Cette transaction est ouverte en appelant la mthode IDBFactory.open avec une version spcifie. (Dans les navigateurs WebKit, qui n'ont pas implment la dernire spcification, la mthode IDBFactory.open prend seulement un paramtre, le nom de la base de donnes ; Vous devez donc appeler IDBVersionChangeRequest.setVersion pour tablir la transaction versionchange.)
Pour lire les enregistrements d'un objet de stockage existant, la transaction peut tre en mode readonly ou readwrite. Pour appliquer des changements un objet de stockage existant, la transaction doit tre en mode readwrite. Vous dmarrez ces transactions avec IDBDatabase.transaction. La mthode accepte deux paramtres : les storeNames (la porte, dfinie comme un tableau des objets de stockage auxquels vous souhaitez accder) et le mode (readonly ou readwrite) pour la transaction. La mthode retourne un objet de transaction contenant la mthode IDBIndex.objectStore, que vous utilisez pour accder votre objet de stockage. Par dfaut, lorsqu'aucun mode n'est spcifi, les transactions dmarrent en mode readonly.
Note :
partir de Firefox 40, les transactions IndexedDB ont des garanties de durabilit relaches afin d'augmenter les performances (voir bug Firefox 1112702.) Auparavant, lors d'une transaction readwrite IDBTransaction.oncomplete tait dclench seulement lorsque les donnes taient garanties pour une criture sur le disque. Dans Firefox 40+ l'vnement complete est dclench une fois que l'OS a autoris l'criture de donnes, mais potentiellement avant que les donnes soient rellement crites sur le disque. L'vnement complete peut ainsi tre livr plus vite qu'avant, cependant, il existe un petit risque que l'ensemble de la transaction soit perdu si l'OS s'effondre ou si un problme lectrique survient avant que les donnes soient crites. Comme de tels vnements catastrophiques sont rares, la plupart des utilisateurs n'ont pas s'en soucier. Si vous devez vous assurer de la durabilit pour quelconque raison que ce soit (par exemple, vous stockez des donnes critiques qui ne peuvent tre recalcules plus tard) vous pouvez forcer une transaction crire sur le disque avant que l'vnement complete ne soit dlivr en crant une transaction utilisant un mode exprimental (non-standard) readwriteflush (se rfrer IDBDatabase.transaction.
Vous pouvez acclrer l'accs vos donnes en utilisant le bon mode et la bonne porte dans la transaction. Voici deux astuces :
readwrite pour une transaction seulement lorsque c'est ncessaire. Vous pouvez excuter simulatnement plusieurs transactions readonly avec chevauchements, mais vous ne pouvez avoir qu'une seule transaction readwrite dans un objet de stockage. Pour en savoir plus, regardez la dfinition des transactions dans l'article des concepts de base.Si vous venez juste de crer une base de donnes, alors vous souhaitez probablement crire dedans. Voici comment a se passe :
var transaction = db.transaction(["customers"], "readwrite");
// Note: Les anciennes implmentations utilisent la constante dprcie IDBTransaction.READ_WRITE au lieu de "readwrite".
// Au cas o vous souhaitiez mettre en oeuvre ces implmentations, vous pouvez crire :
// var transaction = db.transaction(["customers"], IDBTransaction.READ_WRITE);
La fonction transaction() prend deux arguments (bien que l'un d'eux soit facultatif) et retourne un objet transaction. Le premier argument est une liste d'objets de stockage que la transaction va traiter. Vous pouvez passer un tableau vide si vous voulez que la transaction traite l'ensemble des objets de stockage, mais ne le faites pas, parce que la spcification indique qu'un tableau vide devrait gnrer une InvalidAccessError. Si vous ne spcifiez rien pour le deuxime argument, vous dmarrerez une transaction "read-only" (lecture seule) . Si vous souhaitez aussi crire, vous devrez passer l'option "readwrite" (lecture/criture).
Maintenant que vous avez une transaction, vous devez comprendre sa dure de vie. Les transactions sont troitement lies la boucle de l'vnement. Si vous tablissez une transaction et si vous sortez de la boucle d'vnements sans l'utiliser, alors la transaction deviendra inactive. La seule manire de garder la transaction active est d'y insrer une requte. Lorsque la requte est termine, vous obtenez un vnement DOM, et en supposant que la requte ait russi, vous avez une autre opportunit d'tendre la transaction durant ce "callback" (rappel). Si vous sortez de la boucle d'vnements sans tendre la transaction, alors elle devient inactive, etc Tant qu'il reste des demandes en attente, la transaction reste active. La dure de vie des transactions est vraiment trs simple, mais cela peut prendre un peu de temps de la matriser. Quelques exemples supplmentaires aideront. Si vous commencez voir des codes d'erreur TRANSACTION_INACTIVE_ERR, alors vous avez rat quelque chose.
Les transactions peuvent recevoir des vnements DOM de trois types : error (erreur), abort (abandonne) et complete (termine). Nous avons dj parl du fait que les error crent des bulles, ainsi une transaction peut recevoir des vnements d'erreur venant de n'importe quelle requte l'ayant gnr. Un point plus subtil ici, c'est que le comportement par dfaut d'une erreur est d'abandonner la transaction l o elle a eu lieu. A moins que vous griez l'erreur en appelant d'abord stopPropagation() sur l'vnement erreur, puis que vous fassiez quelque chose d'autre, la transaction complte sera annule. Cette conception vous oblige rflchir et grer les erreurs, mais vous pouvez toujours ajouter un gestionnaire d'erreurs "fourre-tout" la base de donnes si la gestion d'erreurs fines est trop lourde. Si vous ne grez pas un vnement d'erreur, ou si vous appelez abort() sur la transaction, alors la transaction est annule et un vnement abort est lanc sur la transaction. Sinon, une fois que toutes les demandes en instance sont termines, vous recevez un vnement complete. Si vous faites beaucoup d'oprations sur les bases de donnes, alors suivre la transaction plutt que les requtes individuelles, peut certainement vous aider.
Maintenant que vous avons une transaction, nous avons besoin de rcuprer l'objet de stockage de celle-ci. Les transactions vous permettent seulement d'avoir l'objet de stockage que vous avez spcifi lors de la cration de la transaction. Puis, vous pouvez ajouter toutes les donnes dont vous avez besoin.
// Faire quelque chose lorsque toutes les donnes sont ajoutes la base de donnes.
transaction.oncomplete = function (event) {
alert("All done!");
};
transaction.onerror = function (event) {
// N'oubliez pas de grer les erreurs !
};
var objectStore = transaction.objectStore("customers");
for (var i in customerData) {
var request = objectStore.add(customerData[i]);
request.onsuccess = function (event) {
// event.target.result == customerData[i].ssn;
};
}
La mthode result d'une requte venant d'un appel add() est la cl de la valeur qui vient d'tre ajoute. Dans ce cas, ce devrait tre quivalent la proprit ssn de l'objet qui vient d'tre ajout, puisque l'objet de stockage utilise la proprit ssn pour le key path. Notez que la fonction add() requiert qu'aucun objet dj prsent dans la base ait la mme cl. Si vous essayez de modifier une entre existante, ou si vous ne vous en occupez pas, vous pouvez utiliser la fonction put(), comme montr plus loin dans la section Mettre jour une entre dans la base de donnes.
Supprimer des donnes est trs similaire :
var request = db
.transaction(["customers"], "readwrite")
.objectStore("customers")
.delete("444-44-4444");
request.onsuccess = function (event) {
// c'est supprim !
};
Maintenant que la base de donnes dispose de quelques informations, vous pouvez les rcuprer de plusieurs faons. D'abord, la plus simple get(). Vous devez fournir une cl pour rcuprer la valeur, comme ceci :
var transaction = db.transaction(["customers"]);
var objectStore = transaction.objectStore("customers");
var request = objectStore.get("444-44-4444");
request.onerror = function (event) {
// gestion des erreurs!
};
request.onsuccess = function (event) {
// Faire quelque chose avec request.result !
alert("Name for SSN 444-44-4444 is " + request.result.name);
};
a fait beaucoup de code pour une "simple" rcupration. Voici comment le raccourcir un peu, en supposant que vous griez les erreurs au niveau de la base de donnes :
db
.transaction("customers")
.objectStore("customers")
.get("444-44-4444").onsuccess = function (event) {
alert("Name for SSN 444-44-4444 is " + event.target.result.name);
};
Vous voyez comment a fonctionne ? Comme il n'y a qu'un seul objet de stockage, vous pouvez viter de passer une liste d'objets dont vous avez besoin dans votre transaction, et juste passer le nom comme une chane de caractres. Aussi, nous faisons seulement une lecture de la base, donc nous n'avons pas besoin d'une transaction "readwrite". Appeler une transaction() sans spcifier de mode nous donne une transaction "readonly". Une autre subtilit ici est que nous n'enregistrons pas l'objet de notre requte dans une variable. Comme l'vnement DOM a la requte comme cible, vous pouvez utiliser l'vnement pour rcuprer la proprit result.
Vous pouvez acclrer l'accs vos donnes en limitant la porte et le mode de la transaction. Voici deux astuces :
Maintenant que nous avons rcuprr quelques donnes, les mettre jour et en insrer est assez simple. Mettons jour l'exemple prcdent :
var objectStore = db
.transaction(["customers"], "readwrite")
.objectStore("customers");
var request = objectStore.get("444-44-4444");
request.onerror = function (event) {
// Gestion des erreurs!
};
request.onsuccess = function (event) {
// On rcupre l'ancienne valeur que nous souhaitons mettre jour
var data = request.result;
// On met jour ce(s) valeur(s) dans l'objet
data.age = 42;
// Et on remet cet objet jour dans la base
var requestUpdate = objectStore.put(data);
requestUpdate.onerror = function (event) {
// Faire quelque chose avec lerreur
};
requestUpdate.onsuccess = function (event) {
// Succs - la donne est mise jour !
};
};
Ici, nous avons cr une variable objectStore et nous avons recherch un enregistrement d'un client, identifi par la valeur ssn (444-44-4444). Nous avons ensuite mis le rsultat dans une variable (data), mis jour la proprit age de cet objet, puis cr une deuxime requte (requestUpdate) pour mettre l'enregistrement du client dans l'objectStore, en crasant la valeur prcdente.
Note :
Dans ce cas, nous avons eu spcifier une transaction readwrite puisque nous voulions crire dans la base, et pas seulement la lire.
Utiliser get() ncessite de connatre la cl que vous souhaitez rcuprer. Si vous voulez parcourir toutes les valeurs de l'objet de stockage, alors vous devez utiliser un curseur. Voici comment a marche :
var objectStore = db.transaction("customers").objectStore("customers");
objectStore.openCursor().onsuccess = function (event) {
var cursor = event.target.result;
if (cursor) {
alert("Name for SSN " + cursor.key + " is " + cursor.value.name);
cursor.continue();
} else {
alert("No more entries!");
}
};
La fonction openCursor() prend en compte plusieurs arguments. En premier, vous pouvez spcifier une plage de rsultats rcuprer en utilisant un objet "key range" que nous allons voir dans une minute. En deuxime, vous pouvez spcifier la direction vers laquelle vous souhaitez itrer. Dans l'exemple ci-dessus, nous avons itr tous les objets dans l'ordre ascendant. Le "callback" (rappel) de russite pour les curseurs est un peu spcial. L'objet cursor lui-mme est le result (rsutat) de la requte (au-dessus, nous utilisons le raccourci event.target.result). Puis la cl et valeur courante peuvent tre trouves dans les proprits key(cl) et value (valeur) de l'objet cursor. Si vous souhaitez continuer, vous devez appeler continue() sur le curseur. Lorsque vous avez atteint la fin des donnes (ou s'il n'y a plus d'entres qui correspondent votre requte openCursor() ) , vous aurez toujours votre callback success, mais la proprit result sera undefined.
Une utilisation classique avec les curseurs est de rcuprer tous les objets dans un objet de stockage et de les mettre dans un tableau, comme ceci :
var customers = [];
objectStore.openCursor().onsuccess = function (event) {
var cursor = event.target.result;
if (cursor) {
customers.push(cursor.value);
cursor.continue();
} else {
alert("Got all customers: " + customers);
}
};
Note :
Mozilla a aussi implment getAll() pour grer ce cas (et getAllKeys(), qui est actuellement cach derrire la prfrence dom.indexedDB.experimental dans about:config) . ceux-ci ne font pas partie d' IndexedDB standard, et peuvent disparatre dans le futur. Nous les avons inclus partceque nous pensons qu'ils sont utiles. Le code suivant fait exactement la mme chose que ci-dessus :
objectStore.getAll().onsuccess = function (event) {
alert("Got all customers: " + event.target.result);
};
Il y a un cot de performance associ avec la recherche de la proprit value du curseur, parce que l'objet est cr paresseusement. Quand vous utilisez getAll() par exemple, Gecko doit crer tous les objets la fois. Si vous tes seulement intress par la lecture de chaque cl, pour l'instance, il est beaucoup plus efficace d'utiliser un curseur que getAll(). Si vous essayez d'obtenir un tableau de tous les objets d'un objet de stockage, utilisez getAll().
Le stockage des donnes des clients utilisant le SSN comme cl est logique puisque le SSN identifie un individu unique. (Que ce soit une bonne ide pour la vie prive est une question diffrente, et en dehors du champ de cet article). Si vous devez rechercher un client par son nom, vous devrez toutefois faire itrer sur toutes les cls SSN dans la base de donnes jusqu' ce que vous trouviez la bonne. La recherche de cette manire serait trs lente, alors, vous pouvez utiliser un index.
// D'abord, assurez-vous de crer un index dans request.onupgradeneeded:
// objectStore.createIndex("name", "name");
// Autrement, vous obtiendrez une DOMException.
var index = objectStore.index("name");
index.get("Donna").onsuccess = function (event) {
alert("Donna's SSN is " + event.target.result.ssn);
};
Le "name" du curseur n'est pas unique, donc il pourrait y avoir plus d'une entre avec le name attribu "Donna". Dans ce cas, vous obtenez toujours celui qui a la valeur cl la plus basse .
Si vous avez besoin d'accder toutes les entres avec un name donn, vous pouvez utiliser un curseur. Vous pouvez ouvrir deux types diffrents de curseurs sur les index. Un curseur normal situe la proprit index de l'objet dans l'objet de stockage. Un curseur de cls situe la proprit index des cls utilises pour stocker l'objet dans l'objet de stockage. Les diffrences sont illustres ici :
// Utilisation d'un curseur normal pour saisir tous les enregistrements des objets client
index.openCursor().onsuccess = function (event) {
var cursor = event.target.result;
if (cursor) {
// cursor.key est un nom, comme "Bill", et cursor.value est l'objet entier.
alert(
"Name: " +
cursor.key +
", SSN: " +
cursor.value.ssn +
", email: " +
cursor.value.email,
);
cursor.continue();
}
};
// Utilisation d'un curseur de cls pour saisir les cls des enregistrements des objets client
index.openKeyCursor().onsuccess = function (event) {
var cursor = event.target.result;
if (cursor) {
// cursor.key est un nom, comme "Bill", et cursor.value est le SSN.
// Pas moyen d'obtenir directement le reste de l'objet stock .
alert("Name: " + cursor.key + ", SSN: " + cursor.value);
cursor.continue();
}
};
Si vous souhaitez limiter l'intervalle de valeurs que vous voyez dans un curseur, vous pouvez utiliser un objet IDBKeyRange et le donner comme premier argument openCursor() ou openKeyCursor() . Vous pouvez crer un intervalle de cls qui n'autorise qu'une seule cl, ou qui a des limites infrieure et suprieure, ou qui a des bornes infrieure et suprieure. La limite peut tre "closed" (ferme) (c'est--dire que l'intervalle de cls comprend les valeurs donnes) ou "open" (ouverte) (c'est--dire que la plage de cls n'inclut pas les valeurs donnes. Voici comment cela fonctionne :
// Correspond seulement "Donna"
var singleKeyRange = IDBKeyRange.only("Donna");
// Correspond n'importe quoi contenant "Bill", y compris "Bill"
var lowerBoundKeyRange = IDBKeyRange.lowerBound("Bill");
// Correspond n'importe quoi contenant "Bill", mais pas "Bill"
var lowerBoundOpenKeyRange = IDBKeyRange.lowerBound("Bill", true);
// Correspond n'importe quoi, mais "Donna" exclus.
var upperBoundOpenKeyRange = IDBKeyRange.upperBound("Donna", true);
// Correspond n'importe quoi compris entre "Bill" et "Donna", mais "Donna" exclus.
var boundKeyRange = IDBKeyRange.bound("Bill", "Donna", false, true);
// Pour utiliser un des intervalles de cls, placez le en premier argument de openCursor()/openKeyCursor()
index.openCursor(boundKeyRange).onsuccess = function (event) {
var cursor = event.target.result;
if (cursor) {
// Faire quelque chose avec la slection.
cursor.continue();
}
};
Parfois, vous voudrez peut-tre itrer dans l'ordre dcroissant plutt que dans l'ordre croissant (la direction par dfaut pour tous les curseurs). Le changement de direction est ralis en passant prev la fonction openCursor() antrieure comme second argument :
objectStore.openCursor(boundKeyRange, "prev").onsuccess = function (event) {
var cursor = event.target.result;
if (cursor) {
// Faire quelque chose avec les entres.
cursor.continue();
}
};
Si vous souhaitez simplement spcifier un changement de direction, mais ne pas limiter les rsultats, vous pouvez simplement passer "null" comme premier argument :
objectStore.openCursor(null, "prev").onsuccess = function (event) {
var cursor = event.target.result;
if (cursor) {
// Faire quelque chose avec les entres.
cursor.continue();
}
};
tant donn que l'index "name" n'est pas unique, il peut y avoir plusieurs entres o le name est le mme. Notez qu'une telle situation ne peut pas se produire avec les objets stocks car la cl doit toujours tre unique. Si vous souhaitez filtrer les doublons pendant l'itration du curseur sur les index, vous pouvez passer nextunique (ou prevunique si vous revenez en arrire) comme paramtre de direction. Lorsque nextunique ou prevunique sont utiliss, l'entre avec la cl la plus basse est toujours celle retourne.
index.openKeyCursor(null, "nextunique").onsuccess = function (event) {
var cursor = event.target.result;
if (cursor) {
// Faire quelque chose avec les entres.
cursor.continue();
}
};
Voyez "IDBCursor Constants" pour les arguments de direction valides.
Lorsque votre application Web change de telle sorte qu'une modification de version est ncessaire pour votre base de donnes, vous devez considrer ce qui se passe si l'utilisateur a l'ancienne version de votre application ouverte dans un onglet, puis charge la nouvelle version de votre application dans une autre . Lorsque vous appelez open() avec une version plus grande que la version actuelle de la base de donnes, toutes les autres bases de donnes ouvertes doivent reconnatre explicitement la demande avant de commencer modifier la base de donnes (un vnement onblocked (bloqu) est dclench jusqu' ce qu'elles soient fermes ou recharges). Voici comment cela fonctionne :
var openReq = mozIndexedDB.open("MyTestDatabase", 2);
openReq.onblocked = function (event) {
// Si un autre onglet est charg avec la base de donnes, il doit tre ferm
// avant que nous puissions continuer.
alert("Veuillez fermer tous les ongles ouverts sur ce site!");
};
openReq.onupgradeneeded = function (event) {
// Toutes les autres bases de donnes ont t fermes. Tout rgler.
db.createObjectStore(/* ... */);
useDatabase(db);
};
openReq.onsuccess = function (event) {
var db = event.target.result;
useDatabase(db);
return;
};
function useDatabase(db) {
// Assurez-vous d'ajouter un gestionnaire pour tre averti si une autre page demande
// un changement de version. Nous devons fermer la base de donnes.
// Cela permet l'autre page de mettre niveau la base de donnes.
// Si vous ne le faites pas, la mise niveau ne se produira que lorsque l'utilisateur fermera l'onglet .
db.onversionchange = function (event) {
db.close();
alert("A new version of this page is ready. Please reload!");
};
// Faire quelque chose avec la base de donnes .
}
Vous devriez galement couter les erreurs VersionError pour grer le cas o les applications dj ouvertes dclencheraient un code conduisant une nouvelle tentative d'ouverture de la base de donnes, mais en utilisant une version dsute.
IndexedDB utilise le principe " same-origin " (mme origine), ce qui signifie qu'il relie le stockage l'origine du site qui le cre (gnralement, c'est le domaine ou le sous-domaine du site), de sorte qu'il ne peut tre consult par aucune autre origine.
Le contenu de la fentre de tiers (par exemple le contenu de <iframe>) peut accder IndexedDB pour l'origine dans laquelle il est intgr, moins que le navigateur ne soit configur pour ne jamais accepter de cookies tiers (voir le bug Firefox 1147821).
Lorsque le navigateur s'arrte (parce que l'utilisateur a choisi l'option Quit ou Exit), le disque contenant la base de donnes est supprim de manire inattendue ou les permissions sont perdues dans le magasin de base de donnes, les choses suivantes se produisent :
AbortError. L'effet est le mme que si IDBTransaction.abort() est appel sur chaque transaction.IDBDatabase reprsentant la connexion la base de donnes reoit un vnement close. Vous pouvez utiliser un gestionnaire d'vnements IDBDatabase.onclose pour couter ces vnements, afin de savoir quand une base de donnes est ferme de faon inattendue .Le comportement dcrit ci-dessus est nouveau et n'est disponible que pour les versions de navigateur suivantes : Firefox 50, Google Chrome 31 (approximativement).
Avant ces versions de navigateurs, les transactions taient interrompues silencieusement et aucun vnement close n'tait dclench, donc il n'y avait aucun moyen de dtecter une fermeture de base de donnes inattendue.
tant donn que l'utilisateur peut quitter le navigateur tout moment, cela signifie que vous ne pouvez pas compter sur une transaction particulire complter, et sur les navigateurs plus anciens, vous n'tes mme pas inform quand elles ne sont pas termines. Il y a plusieurs consquences ce comportement.
Tout d'abord, vous devez vous occuper de toujours laisser votre base de donnes dans un tat cohrent la fin de chaque transaction. Par exemple, supposons que vous utilisiez IndexedDB pour stocker une liste d'lments que l'utilisateur est autoris diter. Vous enregistrez la liste aprs l'dition en effaant l'objet de stockage puis en crivant la nouvelle liste. Si vous effacez l'objet de stockage dans une transaction et que vous crivez la nouvelle liste dans une autre transaction, il existe un danger : si le navigateur se ferme aprs l'effacement mais avant l'criture, votre base de donnes est vide. Pour viter cela, vous devez combiner l'effacement et l'criture en une seule transaction.
Ensuite, vous ne devez jamais lier les transactions de base de donnes pour les vnements unload (dchargement). Si l'vnement unload est dclench par la fermeture du navigateur, toutes les transactions cres dans le gestionnaire d'vnements unload ne seront jamais termines. Une approche intuitive, pour le maintien de certaines informations dans les sessions du navigateur, est de le lire partir de la base de donnes, lorsque le navigateur (ou une page particulire) est ouvert, le mettre jour mesure que l'utilisateur interagit avec le navigateur, puis l'enregistrer dans la base de donnes lorsque le navigateur ( ou page) se ferme. Cependant, cela ne fonctionne pas. Les transactions de la base de donnes sont cres dans le gestionnaire d'vnements unload, mais comme elles sont asynchrones, elles sont interrompues avant qu'elles puissent s'excuter.
En fait, il n'y a aucun moyen de garantir que les transactions IndexedDB seront termines, mme avec un arrt normal du navigateur. Voir le bug Firefox 870645. Comme solution de rechange pour cette notification d'arrt normal, vous pouvez suivre vos transactions et ajouter un vnement beforeunload pour avertir l'utilisateur si des transactions ne sont pas encore termines au moment du dchargement.
Au-moins, avec l'ajout des notifications d'annulation et IDBDatabase.onclose, vous pouvez savoir quand cela s'est produit.
Mozilla a implment la capacit d'effectuer un tri des donnes IndexedDB localises sur Firefox 43+. Par dfaut, IndexedDB n'a pas pris en charge l'internationalisation des chanes de tri, et tait tri comme s'il s'agissait d'un texte anglais. Par exemple, "b", "", "z", "a" devaient tre tris comme suit :
ce qui n'est videmment pas la faon dont les utilisateurs souhaitent que leurs donnes soient tries - Aaron et aron, par exemple, doivent aller l'un ct de l'autre dans une liste de contacts. L'obtention d'un tri international appropri exige donc que l'ensemble des donnes soit appel dans la mmoire et que le tri soit excut par le JavaScript ct client, ce qui n'est pas trs efficace.
Cette nouvelle fonctionnalit permet aux dveloppeurs de spcifier une "locale" (langue) lors de la cration d'un index en utilisant IDBObjectStore.createIndex() (vrifiez ses paramtres). Dans ce cas, lorsqu'un curseur est utilis pour itrer sur l'ensemble de donnes , et si vous souhaitez spcifier un tri local, vous pouvez utiliser un IDBLocaleAwareKeyRange.
IDBIndex a galement eu de nouvelles proprits qui lui ont t ajoutes pour spcifier la langue : locale (retourne la langue si elle est spcifie, ou null sinon) et isAutoLocale (retourne true (vrai) si l'index a t cr avec une "locale auto", ce qui signifie que la langue par dfaut de la plate-forme est utilise, sinon false).
Note :
Cette fonctionnalit est couramment cache derrire une marque (flag) pour l'activer et l'exprimenter, aller about:config et activez dom.indexedDB.experimental.
<script
type="text/javascript"
src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<h1>IndexedDB Demo: storing blobs, e-publication example</h1>
<div class="note">
<p>Works and tested with:</p>
<div id="compat"></div>
</div>
<div id="msg"></div>
<form id="register-form">
<table>
<tbody>
<tr>
<td>
<label for="pub-title" class="required"> Title: </label>
</td>
<td>
<input type="text" id="pub-title" name="pub-title" />
</td>
</tr>
<tr>
<td>
<label for="pub-biblioid" class="required">
Bibliographic ID:<br />
<span class="note">(ISBN, ISSN, etc.)</span>
</label>
</td>
<td>
<input type="text" id="pub-biblioid" name="pub-biblioid" />
</td>
</tr>
<tr>
<td>
<label for="pub-year"> Year: </label>
</td>
<td>
<input type="number" id="pub-year" name="pub-year" />
</td>
</tr>
</tbody>
<tbody>
<tr>
<td>
<label for="pub-file"> File image: </label>
</td>
<td>
<input type="file" id="pub-file" />
</td>
</tr>
<tr>
<td>
<label for="pub-file-url">
Online-file image URL:<br />
<span class="note">(same origin URL)</span>
</label>
</td>
<td>
<input type="text" id="pub-file-url" name="pub-file-url" />
</td>
</tr>
</tbody>
</table>
<div class="button-pane">
<input type="button" id="add-button" value="Add Publication" />
<input type="reset" id="register-form-reset" />
</div>
</form>
<form id="delete-form">
<table>
<tbody>
<tr>
<td>
<label for="pub-biblioid-to-delete">
Bibliographic ID:<br />
<span class="note">(ISBN, ISSN, etc.)</span>
</label>
</td>
<td>
<input
type="text"
id="pub-biblioid-to-delete"
name="pub-biblioid-to-delete" />
</td>
</tr>
<tr>
<td>
<label for="key-to-delete">
Key:<br />
<span class="note">(for example 1, 2, 3, etc.)</span>
</label>
</td>
<td>
<input type="text" id="key-to-delete" name="key-to-delete" />
</td>
</tr>
</tbody>
</table>
<div class="button-pane">
<input type="button" id="delete-button" value="Delete Publication" />
<input
type="button"
id="clear-store-button"
value="Clear the whole store"
class="destructive" />
</div>
</form>
<form id="search-form">
<div class="button-pane">
<input
type="button"
id="search-list-button"
value="List database content" />
</div>
</form>
<div>
<div id="pub-msg"></div>
<div id="pub-viewer"></div>
<ul id="pub-list"></ul>
</div>
body {
font-size: 0.8em;
font-family: Sans-Serif;
}
form {
background-color: #cccccc;
border-radius: 0.3em;
display: inline-block;
margin-bottom: 0.5em;
padding: 1em;
}
table {
border-collapse: collapse;
}
input {
padding: 0.3em;
border-color: #cccccc;
border-radius: 0.3em;
}
.required:after {
content: "*";
color: red;
}
.button-pane {
margin-top: 1em;
}
#pub-viewer {
float: right;
width: 48%;
height: 20em;
border: solid #d092ff 0.1em;
}
#pub-viewer iframe {
width: 100%;
height: 100%;
}
#pub-list {
width: 46%;
background-color: #eeeeee;
border-radius: 0.3em;
}
#pub-list li {
padding-top: 0.5em;
padding-bottom: 0.5em;
padding-right: 0.5em;
}
#msg {
margin-bottom: 1em;
}
.action-success {
padding: 0.5em;
color: #00d21e;
background-color: #eeeeee;
border-radius: 0.2em;
}
.action-failure {
padding: 0.5em;
color: #ff1408;
background-color: #eeeeee;
border-radius: 0.2em;
}
.note {
font-size: smaller;
}
.destructive {
background-color: orange;
}
.destructive:hover {
background-color: #ff8000;
}
.destructive:active {
background-color: red;
}
(function () {
var COMPAT_ENVS = [
["Firefox", ">= 16.0"],
[
"Google Chrome",
">= 24.0 (you may need to get Google Chrome Canary), NO Blob storage support",
],
];
var compat = $("#compat");
compat.empty();
compat.append('<ul id="compat-list"></ul>');
COMPAT_ENVS.forEach(function (val, idx, array) {
$("#compat-list").append("<li>" + val[0] + ": " + val[1] + "</li>");
});
const DB_NAME = "mdn-demo-indexeddb-epublications";
const DB_VERSION = 1; // Utilisez un "long long" pour cette valeur (ne pas utiliser un flottant (float))
const DB_STORE_NAME = "publications";
var db;
// Utilis pour garder une trace de la vue affiche pour viter de la recharger inutilement
var current_view_pub_key;
function openDb() {
console.log("openDb ...");
var req = indexedDB.open(DB_NAME, DB_VERSION);
req.onsuccess = function (evt) {
// Le mieux utiliser "this" que "req" pour obtenir le rsultat et viter
// les problmes avec "garbage collection".
// db = req.result;
db = this.result;
console.log("openDb DONE");
};
req.onerror = function (evt) {
console.error("openDb:", evt.target.errorCode);
};
req.onupgradeneeded = function (evt) {
console.log("openDb.onupgradeneeded");
var store = evt.currentTarget.result.createObjectStore(DB_STORE_NAME, {
keyPath: "id",
autoIncrement: true,
});
store.createIndex("biblioid", "biblioid", { unique: true });
store.createIndex("title", "title", { unique: false });
store.createIndex("year", "year", { unique: false });
};
}
/**
* @paramtre {string}(chane de caractres) store_name
* @paramtre {string}(chane de caractres) mode either "readonly" ou "readwrite"
*/
function getObjectStore(store_name, mode) {
var tx = db.transaction(store_name, mode);
return tx.objectStore(store_name);
}
function clearObjectStore(store_name) {
var store = getObjectStore(DB_STORE_NAME, "readwrite");
var req = store.clear();
req.onsuccess = function (evt) {
displayActionSuccess("Store cleared");
displayPubList(store);
};
req.onerror = function (evt) {
console.error("clearObjectStore:", evt.target.errorCode);
displayActionFailure(this.error);
};
}
function getBlob(key, store, success_callback) {
var req = store.get(key);
req.onsuccess = function (evt) {
var value = evt.target.result;
if (value) success_callback(value.blob);
};
}
/**
* @paramtre objet de stockage {IDBObjectStore=}
*/
function displayPubList(store) {
console.log("displayPubList");
if (typeof store == "undefined")
store = getObjectStore(DB_STORE_NAME, "readonly");
var pub_msg = $("#pub-msg");
pub_msg.empty();
var pub_list = $("#pub-list");
pub_list.empty();
// Rinitialisation de l'iframe afin qu'il n'indique pas le contenu prcdent
newViewerFrame();
var req;
req = store.count();
// Les requtes sont excutes dans l'ordre o elles ont t faites en-dehors de la
// transaction, et leurs rsultats sont retourns dans le mme ordre.
// Ainsi, le texte du compteur ci-dessous sera affich avant la liste de pub actuelle
// (ce n'est pas algorithmiquement important dans ce cas) .
req.onsuccess = function (evt) {
pub_msg.append(
"<p>There are <strong>" +
evt.target.result +
"</strong> record(s) in the object store.</p>",
);
};
req.onerror = function (evt) {
console.error("add error", this.error);
displayActionFailure(this.error);
};
var i = 0;
req = store.openCursor();
req.onsuccess = function (evt) {
var cursor = evt.target.result;
// Si le curseur pointe vers quelque chose, demandez les donnes
if (cursor) {
console.log("displayPubList cursor:", cursor);
req = store.get(cursor.key);
req.onsuccess = function (evt) {
var value = evt.target.result;
var list_item = $(
"<li>" +
"[" +
cursor.key +
"] " +
"(biblioid: " +
value.biblioid +
") " +
value.title +
"</li>",
);
if (value.year != null) list_item.append(" - " + value.year);
if (
value.hasOwnProperty("blob") &&
typeof value.blob != "undefined"
) {
var link = $('<a href="' + cursor.key + '">File</a>');
link.on("click", function () {
return false;
});
link.on("mouseenter", function (evt) {
setInViewer(evt.target.getAttribute("href"));
});
list_item.append(" / ");
list_item.append(link);
} else {
list_item.append(" / No attached file");
}
pub_list.append(list_item);
};
// Passer l'objet de stockage suivant
cursor.continue();
// Ce compteur sert seulement crer des identifiants distincts
i++;
} else {
console.log("No more entries");
}
};
}
function newViewerFrame() {
var viewer = $("#pub-viewer");
viewer.empty();
var iframe = $("<iframe />");
viewer.append(iframe);
return iframe;
}
function setInViewer(key) {
console.log("setInViewer:", arguments);
key = Number(key);
if (key == current_view_pub_key) return;
current_view_pub_key = key;
var store = getObjectStore(DB_STORE_NAME, "readonly");
getBlob(key, store, function (blob) {
console.log("setInViewer blob:", blob);
var iframe = newViewerFrame();
// Il n'est pas possible de dfinir un lien direct vers
// le blob pour fournir un moyen de le tlcharger directement.
if (blob.type == "text/html") {
var reader = new FileReader();
reader.onload = function (evt) {
var html = evt.target.result;
iframe.load(function () {
$(this).contents().find("html").html(html);
});
};
reader.readAsText(blob);
} else if (blob.type.indexOf("image/") == 0) {
iframe.load(function () {
var img_id = "image-" + key;
var img = $('<img id="' + img_id + '"/>');
$(this).contents().find("body").html(img);
var obj_url = window.URL.createObjectURL(blob);
$(this)
.contents()
.find("#" + img_id)
.attr("src", obj_url);
window.URL.revokeObjectURL(obj_url);
});
} else if (blob.type == "application/pdf") {
$("*").css("cursor", "wait");
var obj_url = window.URL.createObjectURL(blob);
iframe.load(function () {
$("*").css("cursor", "auto");
});
iframe.attr("src", obj_url);
window.URL.revokeObjectURL(obj_url);
} else {
iframe.load(function () {
$(this).contents().find("body").html("No view available");
});
}
});
}
/**
* @paramtre {string} (chane de caractres) biblioid (identifiant bibliothque)
* @paramtre {string} (chane de caractres) title (titre)
* @paramtre {number} (nombre) year (anne)
* @paramtre {string} (chane de caractres) url : l'URL de l'image tlcharger et stocker sur le pc
* IndexedDB database. La ressource derrire cette URL assujettie
* "Same origin policy", donc pour que cette mthode fonctionne, l'URL doit venir de
* la mme origine que le site web/l'application sur lequel le code est dploy.
*/
function addPublicationFromUrl(biblioid, title, year, url) {
console.log("addPublicationFromUrl:", arguments);
var xhr = new XMLHttpRequest();
xhr.open("GET", url, true);
// Dfinir le type de rponse recherch "blob"
// http://www.w3.org/TR/XMLHttpRequest2/#the-response-attribute
xhr.responseType = "blob";
xhr.onload = function (evt) {
if (xhr.status == 200) {
console.log("Blob retrieved");
var blob = xhr.response;
console.log("Blob:", blob);
addPublication(biblioid, title, year, blob);
} else {
console.error(
"addPublicationFromUrl error:",
xhr.responseText,
xhr.status,
);
}
};
xhr.send();
// Nous ne pouvons pas utiliser jQuery ici car, partir de jQuery 1.8.3,
// le nouveau "blob" responseType n'est pas gr.
// http://bugs.jquery.com/ticket/11461
// http://bugs.jquery.com/ticket/7248
// $.ajax({
// url: url,
// type: 'GET',
// xhrFields: { responseType: 'blob' },
// success: function(data, textStatus, jqXHR) {
// console.log("Blob retrieved");
// console.log("Blob:", data);
// // addPublication(biblioid, title, year, data);
// },
// error: function(jqXHR, textStatus, errorThrown) {
// console.error(errorThrown);
// displayActionFailure("Error during blob retrieval");
// }
// });
}
/**
* @paramtre {string} (chane de caractres) biblioid (identifiant bibliothque)
* @paramtre {string} (chane de caractres) title (titre)
* @paramtre {number} (nombre) year (anne)
* @paramtre {Blob=} blob
*/
function addPublication(biblioid, title, year, blob) {
console.log("addPublication arguments:", arguments);
var obj = { biblioid: biblioid, title: title, year: year };
if (typeof blob != "undefined") obj.blob = blob;
var store = getObjectStore(DB_STORE_NAME, "readwrite");
var req;
try {
req = store.add(obj);
} catch (e) {
if (e.name == "DataCloneError")
displayActionFailure(
"This engine doesn't know how to clone a Blob, " + "use Firefox",
);
throw e;
}
req.onsuccess = function (evt) {
console.log("Insertion in DB successful");
displayActionSuccess();
displayPubList(store);
};
req.onerror = function () {
console.error("addPublication error", this.error);
displayActionFailure(this.error);
};
}
/**
* @paramtre {string} (chane de caractres) biblioid (identifiant bibliothque)
*/
function deletePublicationFromBib(biblioid) {
console.log("deletePublication:", arguments);
var store = getObjectStore(DB_STORE_NAME, "readwrite");
var req = store.index("biblioid");
req.get(biblioid).onsuccess = function (evt) {
if (typeof evt.target.result == "undefined") {
displayActionFailure("No matching record found");
return;
}
deletePublication(evt.target.result.id, store);
};
req.onerror = function (evt) {
console.error("deletePublicationFromBib:", evt.target.errorCode);
};
}
/**
* @paramtre {number} (nombre) key (cl)
* @paramtre {IDBObjectStore=} store (objet de stockage)
*/
function deletePublication(key, store) {
console.log("deletePublication:", arguments);
if (typeof store == "undefined")
store = getObjectStore(DB_STORE_NAME, "readwrite");
// Selon les spcifications http://www.w3.org/TR/IndexedDB/#object-store-deletion-operation
// le rsultat de l'objet de stockage, l'algorithme de l'opration de suppression est
// "undefined" (indfini), donc il n'est pas possible de savoir si certains enregistrements
// ont t effectivement supprims en lisant le rsultat de la requte.
var req = store.get(key);
req.onsuccess = function (evt) {
var record = evt.target.result;
console.log("record:", record);
if (typeof record == "undefined") {
displayActionFailure("No matching record found");
return;
}
// Attention: La mme cl utilise pour la cration doit tre transmise pour
// la suppression. Si la cl tait un nombre pour la cration, elle doit
// tre un nombre pour la suppression.
req = store.delete(key);
req.onsuccess = function (evt) {
console.log("evt:", evt);
console.log("evt.target:", evt.target);
console.log("evt.target.result:", evt.target.result);
console.log("delete successful");
displayActionSuccess("Deletion successful");
displayPubList(store);
};
req.onerror = function (evt) {
console.error("deletePublication:", evt.target.errorCode);
};
};
req.onerror = function (evt) {
console.error("deletePublication:", evt.target.errorCode);
};
}
function displayActionSuccess(msg) {
msg = typeof msg != "undefined" ? "Success: " + msg : "Success";
$("#msg").html('<span class="action-success">' + msg + "</span>");
}
function displayActionFailure(msg) {
msg = typeof msg != "undefined" ? "Failure: " + msg : "Failure";
$("#msg").html('<span class="action-failure">' + msg + "</span>");
}
function resetActionStatus() {
console.log("resetActionStatus ...");
$("#msg").empty();
console.log("resetActionStatus DONE");
}
function addEventListeners() {
console.log("addEventListeners");
$("#register-form-reset").click(function (evt) {
resetActionStatus();
});
$("#add-button").click(function (evt) {
console.log("add ...");
var title = $("#pub-title").val();
var biblioid = $("#pub-biblioid").val();
if (!title || !biblioid) {
displayActionFailure("Required field(s) missing");
return;
}
var year = $("#pub-year").val();
if (year != "") {
// Le mieux est d'utiliser Number.isInteger si le moteur a EcmaScript 6
if (isNaN(year)) {
displayActionFailure("Invalid year");
return;
}
year = Number(year);
} else {
year = null;
}
var file_input = $("#pub-file");
var selected_file = file_input.get(0).files[0];
console.log("selected_file:", selected_file);
// Garder une rfrence sur la faon de rinitialiser l'entre du fichier dans l'interface
// utilisateur une fois que nous avons sa valeur, mais au lieu de faire cela nous utiliserons
// plutt un type "reset" entr dans le formulaire HTML .
// file_input.val(null);
var file_url = $("#pub-file-url").val();
if (selected_file) {
addPublication(biblioid, title, year, selected_file);
} else if (file_url) {
addPublicationFromUrl(biblioid, title, year, file_url);
} else {
addPublication(biblioid, title, year);
}
});
$("#delete-button").click(function (evt) {
console.log("delete ...");
var biblioid = $("#pub-biblioid-to-delete").val();
var key = $("#key-to-delete").val();
if (biblioid != "") {
deletePublicationFromBib(biblioid);
} else if (key != "") {
// Le mieux est d'utiliser Number.isInteger si le moteur a EcmaScript 6
if (key == "" || isNaN(key)) {
displayActionFailure("Invalid key");
return;
}
key = Number(key);
deletePublication(key);
}
});
$("#clear-store-button").click(function (evt) {
clearObjectStore();
});
var search_button = $("#search-list-button");
search_button.click(function (evt) {
displayPubList();
});
}
openDb();
addEventListeners();
})(); // Immediately-Invoked Function Expression (IIFE)
Rfrence :
Tutoriels :
Bibliothques :
Cette page a t modifie le 17 dc. 2024 par les contributeurices du MDN.
Certaines parties de ce contenu sont protges par le droit d'auteur 19982026 des contributeurs individuels de mozilla.org. Contenu disponible sous une licence Creative Commons.
| Web Proxy Viewer | New URL | Original Page |