Showing posts with label IE9. Show all posts
Showing posts with label IE9. Show all posts

Tuesday, July 24, 2012

Linq2IndexedDB: Debugging IndexedDB

In my latest version of the Linq2IndexedDB 1.0.7, I added the viewer object. This object allows you to inspect your IndexedDB database and gives you an overview of all the object stores and indexes that are present. It even gives you details about how they are configured and what data they contain. Also using this, you can take a look at your database in various debugging tools like:
  • Google Chrome Developer tools
  • Firefox Developer tools
  • IE Developer tools
  • Visual Studio
The viewer object contains 4 properties:
  1. configuration: In here you will find the way the Linq2IndexedDB framework is configured. (The same properties as you can define in the configuration object are present +  a property indicating if framework is running in auto create mode.)
  2. name: The name of the IndexedDB database
  3. objectStores: A collection of all the object stores that are present in the database
  4. version: The current version of the database
image
Inside an object store object, we can see the following:
  1. autoIncrement: indicates if the keys for the object store are generated by the IndexedDB API
  2. data: A collection of all the data available in the object store. These data objects are shown as key/value pair objects.
  3. indexes: A collection of all the indexes available on the object store.
  4. keyPath: The property inside the object that keeps the key
  5. name: The name of the object store
image
The last level we can take a look at, are the indexes. The following information will be provided:
  1. data: A collection of all the data available in the object store. These data objects are shown as key/value pair objects.
  2. keyPath: The property inside the object that keeps the key
  3. multiEntry: Determines if a key is only once defined in the index (which means the value keeps an array of all the values in the object store who match) or the key can be present multiple times (once for every value that matches in the object store.)
  4. name: The name of the index
image

conclusion

I hope this debugging information can help you speed up your software development and finding bugs while working with the IndexedDB API. The latest version of the framework can be found on codeplex, nuget and MyGet.

Update 28/07/2012

I added a new version of the framework, because there were some little bugs with the viewer. The following things change:
  • The viewer no longer blocks the deletion or upgrade of the database
  • The viewer now gets updated when the structure of the database changes
  • The viewer is default disabled for performance issues. If you want to enable it, add true as 3th argument on the function to create a linq2indexeddb function
   1: var db = $.linq2indexedDB("test", null, true);

Friday, November 25, 2011

Indexed DB: Reading data

Finally we get to the part where we can see the results of our Indexed DB tutorial. For the last few moths we have been structuring our database, adding data and now I’ll show how you can read data from an Indexed DB. For this we have 2 approaches. First one is retrieving a single record and the other way is retrieving a collection of records by a cursor.

In this post I’ll handle the retrieving of a single record. A post about retrieving data with a cursor will appear in the near future.

Retrieving data from an object store

In the IObjectStore interface we have an get method to retrieve data. This method must be provided with a key we want to search on. The key parameter can be a valid key or a key range. In case you don’t provide a valid key, a DATA_ERR exception will be thrown.

In the first situation a record of the object store will be retrieved where the given key is the same as the key of the key/value pair. In the other situation, the first record will be retrieved that matches one of the keys defined in the key range. If no record gets found, the result will be undefined. If you are using the Indexed DB prototype on IE 9, you’ll get an “Object with specified key not found.” error in stead of an result.

Time for some code:

When we want to read data, we need to create a transaction. This we do on the same way as for adding data. The only difference is we can use the IDBTransaction.READ_ONLY mode. This is the type that will be used as default when we create a transaction.

   1: var txn = dbconnection.transaction([“ObjectStoreName”]);
   2: var objectStore = txn.objectStore(“ObjectStoreName”);

The result of the get method is a IDBrequest object. When this is successful, the record is added to the result.



   1: var getReq = store.get(key);
   2: getReq.onsuccess = function(event) { 
   3:     var result;
   4:     // IE 9 implementation
   5:     if(event.result){
   6:         result = event.result;
   7:     }
   8:     // IE 10, Chrome and Firefox implementation
   9:     else if (getReq.result){
  10:         result = getReq.result;
  11:     }
  12:     // Code to present the result on your screen
  13: };

Retrieving data from an Index


In a previous post I have been talking about Indexes. Indexes make it possible to look up data from the object store by fields of the value object. By example: we have an object store person. In this object store we store persons. A person object has a first name and a last name field. By creating an index with a key path “last name”, we make it possible to retrieve data from the object store by providing the last name of the person.


In the IDBIndex interface we have the get method to retrieve a record from the object store, and the GetKey method to retrieve the key of the record from the object store. In both cases a key must be provided. This key parameter can be a valid key or a key range. For the rest every thing works the same as for retrieving data from an object store.


One think you need to keep in mind. In the Indexed DB prototype on IE9 the get and getKey method work different. In this case the getKey method retrieves the data and the get method the key of the record.



   1: var txn = dbconnection.transaction([“ObjectStoreName”]);
   2: var objectStore = txn.objectStore(“ObjectStoreName”);
   3: var index = store.index(“IndexName”);
   4: var getReq = index.getKey(key);
   5:  
   6: getReq.onsuccess = function(event) { 
   7:     var result;
   8:     if(event.result){
   9:         result = event.result;
  10:     }
  11:     else{
  12:         result = getReq.result
  13:     }
  14:     // Code to present the result on your screen
  15: };

Tuesday, October 25, 2011

Indexed DB: Defining the database structure

In previous post I have been talking about the basic concepts in the Indexed DB and how you can open/create a database and a database connection. Today I will tell you how you can construct your own database structure. This includes defining the object store where we can store our objects and defining indexes. These indexes will be used when searching into the objects we store in our database.

version_change transaction

There exists only one way to define your object stores and indexes, and this is in a VERSION_CHANGE transaction. We can start this transaction by calling the setVersion method on the database connection. When a database is initially created, the version of the database is an empty string. By passing a string as version to the setVersion method, we add object stores and indexes. This we do by defining the onsuccess event. We can also provide error handeling trough the onerror event.

var dbreq = dbConnection.setVersion(version);
dbreq.onsuccess = function (event) {
      // Here we put code to create object stores or indexes
}

dbreq.onerror = function (event) {
      // Here we put code to handle an error while changing the version
}

dbreq.onabort = function (event) {
      // Here we put code to handle an abort when changing the database version
}

If we are using the IE 9 Indexed DB prototype, we need to commit the transaction. In all other browsers this is not necessary because the implementation of Indexed DB determines that every transaction is committed by default when closed. If a transaction should be aborted you have to do this explicitly.

dbreq.onsuccess = function (event) {
       var txn;
       if (event.result) {
             txn = event.result;
       }
       // Here we put code to create object stores or indexes
      
       if(typeof(txn.commit) != "undefined")
       {
            txn.commit();
       }
}

Because a database can’t have multiple versions at one time, the only way you can change the version is by closing all other connections to the database. This also means no new connections can’t be opened until the VERSION_CHANGE transaction is completed. A blocked event will be raised when a connection to the database is still open when changing the version.

dbreq.onblocked = function (event) {
// Here we put code to handle an abort when changing the database version
}

Creating an objectStore

Once we started the VERSION_CHANGE transaction, we can start adding or deleting object stores. We can do this by calling the createObjectStore method. As first parameter we need to pass the name of the object store. Optionally you can can add two extra parameters. The first one is JSON object with two fields. The first field is a keyPath, when this is provided, the key of the object store will be determined by the field that has the name given in the string of the key path. The second field determines if the object store should have a key generator.

The last parameter we pass is the same as the autoIncrement field in the second parameter. We do this to enable the object store to have a key generator in the IE9 prototype. When using Firefox, Chrome and IE 10, you don’t need to pass this parameter.

The following combinations of a KeyPath and autoIncrement are possible:

Key path Auto increment Description
Empty False This object store can hold any kind of value, even primitive values like numbers and strings. You must supply a separate key argument whenever you want to add a new value to the object store.
Provided False This object store can only hold JavaScript objects. The objects must have a property with the same name as the key path.
Empty True This object store can hold any kind of value. The key is generated for you automatically, or you can supply a separate key argument if you want to use a specific key.
Provided True This object store can only hold JavaScript objects. Usually a key is generated and the value of the generated key is stored in the object in a property with the same name as the key path. However, if such a property already exists, the value of that property is used as key rather than generating a new key.

dbConnection.createObjectStore("ObjectStoreName"
                                              , { "keyPath": "keyPath"
                                                , "autoIncrement": true }
                                              , true);

removing an object store

When we want to remove an object store, the only thing we need to pass is the name of the object store we want to remove.

dbConnection.deleteObjectStore("ObjectStoreName");

Creating an INDEX

Once we created our object store, we can add indexes to it. These indexes are object stores as well, but the key of these key/value pairs is the key path of the index. This means that in this object store that multiple keys with the same value are allowed. When we want to create an index we need to provide a name and a key path for them. Optionally we can provide a JSON object with a unique and multiplerow field.

Field Description Support
unique A boolean value indicating whether the index allows duplicate values. If this attribute is "true", duplicate values are not allowed. Duplicate values are allowed when this attribute is "false" (the default value). This means when you add an object to an object store where an index with a unique value on "true". You will get an exception when you add an object with a duplicate value in the field of the key path of the index. IE 10
FF
Chrome
multiplerow A boolean value that describes determines the results when multiple rows in the object store match individual key values. When this happens, the resulting object is an array. If this parameter is "true", the resulting array contains a single item; the key of the item contains an array of the matching values. When this parameter is "false" (the default), the result array contains one item for each item that matches the key value. IE 10

store.createIndex("IndexName"
                         , "keyPath"
                         , { unique: false });

If we want to create an index in IE 9 Indexed DB prototype, we need to pass the boolean value for the uniqueness as parameter and not as a JSON object.

store.createIndex("IndexName"
, "keyPath"
, false);

Deleting an index

The last action we can do in the VERSION_CHANGE event is deleting an existing index. We can do this by passing the name of the index we want to delete.

store.deleteIndex("IndexName");

Sunday, October 2, 2011

Indexed DB: Creating, opening and deleting a database

In my last post I provided some more information about the structure of the Indexed DB database. Today, I will dive into code and tell you how to create, open and delete a database. There are some small differences between the browsers, but I will handle that at the specific parts of code.

The code I will show will be the Asynchronous implementation of the API. In my previous post I have said no browser does implement the synchronous API, but I was wrong. The IE 10 Preview 3 also implements the Synchronous implementation, but you should only use it in combination with web workers, because it freezes the UI if you don’t.

Initializing the Indexed DB API

First things first: the Indexed DB API is still in draft, so this means the unified Indexed DB property on the window element isn’t implemented yet. Every browser uses his on prefix to use the API, but we can assign this to the Indexed DB property. This gives us a way to us the Indexed DB on the same way.

Initializing the Firefox implementation

if (window.mozIndexedDB) {
            window.indexedDB = window.mozIndexedDB;
            window.IDBKeyRange = window.IDBKeyRange;
            window.IDBTransaction = window.IDBTransaction;
}

Initializing the Chrome implementation

if (window.webkitIndexedDB) {
            window.indexedDB = window.webkitIndexedDB;
            window.IDBKeyRange = window.webkitIDBKeyRange;
            window.IDBTransaction = window.webkitIDBTransaction;
}

Initializing the IE 10 Preview 3 implementation (You’ll need to be running the Windows 8 Developer Preview to run this)

if (window.msIndexedDB) {
            window.indexedDB = window.msIndexedDB;
}

Initializing the IE prototype implementation

if (navigator.appName == 'Microsoft Internet Explorer') {
            window.indexedDB = new ActiveXObject("SQLCE.Factory.4.0");
            window.indexedDBSync = new ActiveXObject("SQLCE.FactorySync.4.0");

            if (window.JSON) {
                window.indexedDB.json = window.JSON;
                window.indexedDBSync.json = window.JSON;
            } else {
                var jsonObject = {
                    parse: function (txt) {
                        if (txt === "[]") return [];
                        if (txt === "{}") return {};
                        throw { message: "Unrecognized JSON to parse: " + txt };
                    }
                };
                window.indexedDB.json = jsonObject;
                window.indexedDBSync.json = jsonObject;

            }

            // Add some interface-level constants and methods.
            window.IDBDatabaseException = {
                UNKNOWN_ERR: 0,
                NON_TRANSIENT_ERR: 1,
                NOT_FOUND_ERR: 2,
                CONSTRAINT_ERR: 3,
                DATA_ERR: 4,
                NOT_ALLOWED_ERR: 5,
                SERIAL_ERR: 11,
                RECOVERABLE_ERR: 21,
                TRANSIENT_ERR: 31,
                TIMEOUT_ERR: 32,
                DEADLOCK_ERR: 33
            };

            window.IDBKeyRange = {
                SINGLE: 0,
                LEFT_OPEN: 1,
                RIGHT_OPEN: 2,
                LEFT_BOUND: 4,
                RIGHT_BOUND: 8
            };

            window.IDBRequest = {
                INITIAL: 0,
                LOADING: 1,
                DONE: 2
            };

            window.IDBTransaction = {
                READ_ONLY: 0,
                READ_WRITE: 1,
                VERSION_CHANGE: 2
            };

            window.IDBKeyRange.only = function (value) {
                return window.indexedDB.range.only(value);
            };

            window.IDBKeyRange.leftBound = function (bound, open) {
                return window.indexedDB.range.leftBound(bound, open);
            };

            window.IDBKeyRange.rightBound = function (bound, open) {
                return window.indexedDB.range.rightBound(bound, open);
            };

            window.IDBKeyRange.bound = function (left, right, openLeft, openRight) {
                return window.indexedDB.range.bound(left, right, openLeft, openRight);
            };
}

Creating and opening a database

Once we have our Indexed DB initialized, we can start the real thing. Creating a database. There is no specific method to create a new database, but when we call the open method, a database will be created if it doesn’t exists. After that a connection to the database will be opened. To open or create a database, the only thing you need is a name.

var dbreq = window.indexedDB.open(“database name”);

dbreq.onsuccess = function (event) {
        var dbConnection;
        // IE prototype Implementation
        if (event.result) {
                dbConnection = event.result;
        }
        //IE 10 Preview 3, Firefox & Chrome implementation
        else {
                dbConnection = dbreq.result;
        }
}

dbreq.onerror = function (event) {
       // Log or show the error message
}

Calling the open method, will return an IDBRequest object. On this object we can attach an onerror function and an onsuccess function. The onerror event will provide handling the error returned by the open function, here you can provide logging or error handling. In the onsuccess event, the database connection will be opened. We can now start using the database connection.

As you can see in code, IE prototype implementation is handling this on an other way then IE 10 Preview 3, Firefox and Chrome. In the IE prototype implementation we get the result (the database connection) out the parameter that is passed trough with the onsuccess function. For IE 10 Preview 3, Firefox and Chrome, we get the result from the IDBRequest object. This is the implementation that was defined by the W3C.

Deleting a database

Deleting the database will happen almost the same way as opening a database. You provide the name of the database you want to delete, and the database will get deleted. The error event will only be called when something goes wrong. Providing a database name that doesn’t exists, will not result in an error and the onsuccess function will get called.

var dbreq = window.indexedDB.deleteDatabase(“database name”);
        dbreq.onsuccess = function (event) {
             // Database deleted
        }
        dbreq.onerror = function (event) {
            // Log or show the error message
        }

Now we can create, open and delete a database. In one of my next posts, I will show you how you can create/change your database structure.

Saturday, September 10, 2011

Getting started with IndexedDB on IE

I have been playing with HTML5 for a while, and more and more I’m seeing the advantage of building applications in HTML 5. In particular applications that will run online and offline as well. One of the things we need for this is storage on the client side. For now most browsers only support WebStorage for this. The disadvantage of Webstorage is the maximum of 5mb of data and every thing is stored in KeyValue pairs, which make searching in the data harder.

Some browsers are now experimenting with the IndexedDB spec. A description of the spec can be found here. In FireFox en Chrome the IndexedDB API is all ready build-in. Not in the window.IndexedDB, but in window.mozIndexedDB for FireFox and window.webkitIndexedDB for Chrome. For internet explorer, you can use an experimental API that you can find on html5labs.

To get started with IndexedDB for Firefox and Chrome we don’t need to install something, to get started with IndexedDB for Internet explorer, we need to install the IndexedDB prototype. The files we need to install can be found here. Once the file is downloaded, we unpack the zip-file.

The first thing we need to do is run the vcredist_x86.exe which can be found in the map we just unpacked. Once that is installed, we need to install the sqlcejse40.dll:

    1. Open the command prompt as administrator
    2. Navigate with the cd command to the unpacked zip-file
    3. Run the following command:
      regsvr32.exe sqlcejse40.dll

Once we pasted these steps, we should be able to open and use the demo included in the zip-file. But beware, you need to run you Internet Explorer with administrator right, otherwise you will get the following error in the error textbox:

6 - Error 0x80040E09, minor 25039 (0x000061CF).

Now when everything is properly installed, we can start to develop. But, this will be content of a next blogpost.