When you are creating integration points between your PhoneGap HTML5 application and the Silverlight hosting application, there are a few ways you can go about crossing the gap (pun intended). This tip is an example of one of those techniques. It’s very flexible, and is typically going to be used when you have one off types of code being written on both sides. We’re able to do this because the PhoneGap user control actually wraps a Windows Phone WebBrowser control, and we can access that internal WebBrowser and use theInvokeScript method and the ScriptNotify event to do what we want here. If you’re curious as to all the details about the WebBrowser control and if there’s other things you can do in the context of a PhoneGap application, you can read about it on the MSDN site.

As a side note, if you try the examples below and your version of PGView does not seem to have a Browser member, you’re probably working with an older version of the PhoneGap user control. To get the latest version, you’ll need to download the framework for the control, build that project, then add output of that toyour project. To do that, open the solution WP7GapClassLib in the framework folder of the WP7GapBuildproject. Build that solution. Then, when you create a project from the GapAppStarter template, simply right-click on the GapLib folder, choose Add Existing Item, then browse to the location where the WP7GapClassLib project is stored. Then drill down to the bin\debug folder and pick the WP7GapClassLib.dll file in there. Your references to it should update automatically.

This script integration technique is bidirectional. You can have the hosting page’s code call into the script in the PhoneGap application, and you can have JavaScript code in the PhoneGap application call code in the hosting Silverlight page.

Let’s start with a simple button on the Silverlight host page to trigger the code we want to run. On MainPage.xaml, go into designer view, then make room at the bottom of the page for the controls we’re going to add by resizing the PhoneGap control up just a little. Use the grab handle at the bottom of the control, it’s a downward facing triangle. Open the Toolbox at the left side of Visual Studio, if it’s not already open. In there you’ll see a lot of the basic Silverlight controls you can add to a page. Look for the Button control, drag it onto the page below the PhoneGap control. Then set its Content property to “To PhoneGap”. You can use the Property window to the right, or just change it inline in the XAML code.

To create an event handler, double click the button. That will open a code window and position your cursor inside the newly created button1_Click event handler. Here is where we call the script in PhoneGap. Type inPGView.Browser.InvokeScript(“button1_Click”); Your code window should look like this:

private void button1_Click(object sender, RoutedEventArgs e)
 {
     PGView.Browser.InvokeScript("button1_Click",textBox1.Text.ToString());
 }

Now we need to create a JavaScript function called button1_Click and put our code in it. In the <script>section at the top of index.html in the WWW directory of the project, put in the following script:

function button1_Click() {
           document.getElementById("welcomeMsg").innerHTML = "You clicked!";
       }

You could call the function anything you want, but for consistency in the demo I set it up this way. Run the project, click the button, and you should see the HTML change to “You clicked”.

You can also pass parameters into the script. Go back to the Silverlight page and add a TextBox to the right of the button. Your screen should look a little like this:

image

Set the Text property of the text box to be an empty value. Change the code in the button click event handler to include sending the Text of the textbox to the script handler.

private void button1_Click(object sender, RoutedEventArgs e)
{
    PGView.Browser.InvokeScript("button1_Click",textBox1.Text.ToString());
}

Then, we need to update the JavaScript button1_Click function to accept the parameter:

function button1_Click(textbox) {
    document.getElementById("welcomeMsg").innerHTML = "The TextBox says: " + textbox;
}

That parameter could be a collection/array too, if you need it.

(By the way, as you run this sample and click in the text box to type, you’ll see the emulator’s onscreen keyboard appear. The first thing to notice is that it slides your PhoneGap display up and off the screen. We’ll address that in a later article. Secondly, if you’d like to type with your computer’s keyboard rather than clicking the letters on the screen, just press Pause or Page Up / Page Down to toggle that.)

Going the other direction is a bit trickier, since you need to tell the host Silverlight page to expect a call “up” from the WebBrowser control inside the PhoneGap control. Add a button to the HTML page like so:

<button onclick="window.external.Notify('clearTextBox');">clear textbox</button>

Unlike calling into the PhoneGap’s browser script engine, calling out of it to the host page always raises a single event – Browser_ScriptNotify. We create an event handler for this in the host page’s constructor method. The constructor is the method with the same name as the host page. In our example it’s public MainPage(). Find that method at the top of the MainPage.xaml.cs file. At the end of that method, typePGView.Browser.ScriptNotify Then type a space, a plus, and an equals. Follow that with two presses on theTab key. Congratulations, you just learned how to create and wire up an event handler in XAML! That will be a handy set of keystrokes in the future.

In the newly generated Browser_ScriptNotify method, delete the line that throws a NotImplemented exception, since we’re implementing the function. Look at the method parameters. The second one e is of type NotifyEventArgs. All you really need to know here is that e.Value will contain the string sent from thewindow.external.Notify call from the PhoneGap control. So let’s write code to handle it.

void Browser_ScriptNotify(object sender, NotifyEventArgs e)
{
    string commandStr = e.Value;

    if (commandStr == "clearTextBox")
    {
        textBox1.Text = "";

    }
}

Run the project, type some text in the text box, then press the clear textbox button. The text in the textbox should be cleared.

As you add more places where your JavaScript inside your PhoneGap app needs to talk to the hosting page, you’ll just add new if statements in this event handler. You could get creative and pass an array serialized into json, with perhaps the name of an “action” prepended in front of it in the string.

Let me know your questions or comments about this technique below!


We are going to build a client side data layer for a note-taking web app:

From a data model point of view, it’s about as simple as it can get. The app allows users to write text notes and tag them with specific key words. Each note will have a unique identifier that will serve as its key, and apart from the note text, it will be associated with a collection of tag strings.

Here’s a sample note object represented in JavaScript object literal notation:

  1. var note = {
  2.   id: 1,
  3.   text: “Note text.”,
  4.   tags: [“sample”, “test”]
  5. };

We’ll build a NotesStore object that has the following interface:

  1. var NotesStore = {
  2.     init: function(callback) {
  3.     },
  4.     addNote: function(text, tags, callback) {
  5.     },
  6.     listNotes: function(callback) {
  7.     }
  8. };

It should be obvious what each method does. All method calls execute asynchronously (that is, when results are reported via callbacks), and where a result is to be returned to the caller, the interface accepts a reference to a callback that is to be invoked with the result. Let’s see what it takes to efficiently implement this object using an indexed database.

Testing for IndexedDB

The root object that you deal with when talking to the IndexedDB API is called indexedDB. You can check for the presence of this object to see whether the current browser supports IndexedDB or not. Like so:

  1. if(window[“indexedDB”] === undefined) {
  2.   // nope, no IndexedDB!
  3. } else {
  4.   // yep, we’re good to go!
  5. }

Alternatively, you can use the Modernizr JavaScript library to test for support for IndexedDB like so:

  1. if(Modernizr.indexeddb) {
  2.     // yep, go indexeddb!
  3. } else {
  4.     // bleh! No joy!
  5. }

Asynchronous requests

The asynchronous API calls work through what are known as “request” objects. When an asynchronous API call is made, it would return a reference to a “request” object, which exposes two events—onsuccess and onerror.

Here’s what a typical call looks like:

  1. var req = someAsyncCall();
  2. req.onsuccess = function() {
  3.     // handle success case
  4. };
  5. req.onerror = function() {
  6.     // handle error
  7. };

As you work with the indexedDB API, it will eventually become hard to keep track of all the callbacks. To make it somewhat simpler, I’ll define and use a small utility routine that abstracts the “request” pattern away:

  1. var Utils = {
  2.     errorHandler: function(cb) {
  3.         return function(e) {
  4.             if(cb) {
  5.                 cb(e);
  6.             } else {
  7.                 throw e;
  8.             }
  9.         };
  10.     },
  11.     request: function (req, callback, err_callback) {
  12.         if (callback) {
  13.             req.onsuccess = function (e) {
  14.                 callback(e);
  15.             };
  16.         }
  17.         req.onerror = errorHandler(err_callback);
  18.     }
  19. };

Now, I can write my async calls like so:

  1. Utils.request(someAsyncCall(), function(e) {
  2.     // handle completion of call
  3. });

Creating and opening the database

Creating/opening a database is done by calling the open method of the indexedDB object.

Here’s an implementation of the NotesStore object’s init method:

  1. var NotesStore = {
  2.     name: “notes-db”,
  3.     db: null,
  4.     ver: “1.0”,
  5.     init: function(callback) {
  6.         var self = this;
  7.         callback = callback || function () { };
  8.         Utils.request(window.indexedDB.open(“open”, this.name), function(e) {
  9.             self.db = e.result;
  10.             callback();
  11.         });
  12.     },
  13.     …

The open method opens the database if it already exists. It is doesn’t, it will create a new one. You can think of this as the object that represents the connection to the database. When this object is destroyed the connection to the database is terminated.

Now that the database exists, let’s create the rest of the database objects. But first, you’ll have to get acquainted with some important IndexedDB constructs.

Object stores

Object stores are the IndexedDB equivalent of “tables” from the relational database world. All data is stored inside object stores and serves as the primary unit of storage.

A database can contain multiple object stores and each store is a collection of records. Each record is a simple key/value pair. Keys must uniquely identify a particular record and can be auto-generated. The records in an object store are automatically sorted in ascending order by keys. And finally, object stores can be created and deleted only under the context of “version change” transactions. (More on that later.)

Keys and Values

Each record in the object store is uniquely identified by a “key.” Keys can be arrays, strings, dates, or numbers. For comparison’s sake, arrays are greater than strings, which are greater than dates, which are greater thannumbers.

Keys can be “in-line” keys or not. By “in-line,” we indicate to IndexedDB that the key for a particular record is actually a part of the value object itself. In our notes store sample, for instance, each note object has an id property that contains the unique identifier for a particular note. This is an example of an “in-line” key—the key is a part of the value object.

Whenever keys are “in-line,” we must also specify a “key path”—a string that signifies how the key value can be extracted from the value object.

The key path for “notes” objects for instance is the string “id” since the key can be extracted from note instances by accessing the “id” property. But this scheme allows for the key value to be stored at an arbitrary depth in the value object’s member hierarchy. Consider the following sample value object:

  1. var product = {
  2.   info: {
  3.     name: “Towel”,
  4.     type: “Indispensable hitchhiker item”,
  5.   },
  6.   identity: {
  7.     server: {
  8.       value: “T01”
  9.     },
  10.     client: {
  11.       value: “TC01”
  12.     },
  13.   },
  14.   price: “Priceless”
  15. };

Here, the following key path might be used:

  1. identity.client.value

Database versioning

IndexedDB databases have a version string associated with them. This can be used by web applications to determine whether the database on a particular client has the latest structure or not.

This is useful when you make changes to your database’s data model and want to propagate those changes to existing clients who are on the previous version of your data model. You can simply change the version number for the new structure and check for it the next time the user runs your app. If needed, upgrade the structure, migrate the data, and change the version number.

Version number changes must be performed under the context of a “version change” transaction. Before we get to that, let’s quickly review what “transactions” are.

Transactions

Like relational databases, IndexedDB also performs all of its I/O operations under the context of transactions. Transactions are created through connection objects and enable atomic, durable data access and mutation. There are two key attributes for transaction objects:

1.    Scope

The scope determines which parts of the database can be affected through the transaction. This basically helps the IndexedDB implementation determine what kind of isolation level to apply during the lifetime of the transaction. Think of the scope as simply a list of tables (known as “object stores”) that will form a part of the transaction.

2.    Mode

The transaction mode determines what kind of I/O operation is permitted in the transaction. The mode can be:

  1. Read only
    Allows only “read” operations on the objects that are a part of the transaction’s scope.
  2. Read/write
    Allows “read” and “write” operations on the objects that are a part of the transaction’s scope.
  3. Version change
    The “version change” mode allows “read” and “write” operations and also allows the creation and deletion of object stores and indexes.

Transaction objects auto-commit themselves unless they have been explicitly aborted. Transaction objects expose events to notify clients of:

  • when they complete
  • when they abort and
  • when they timeout

Creating the object store

Our notes store database will contain only a single object store to record the list of notes. As discussed earlier, object stores must be created under the context of a “version change” transaction.

Let’s go ahead and extend the init method of the NotesStore object to include the creation of the object store. I’ve highlighted the changed bits in bold.

  1. var NotesStore = {
  2.     name: “notes-db”,
  3.     store_name: “notes-store”,
  4.     store_key_path: “id”,
  5.     db: null,
  6.     ver: “1.0”,
  7.     init: function (callback) {
  8.         var self = this;
  9.         callback = callback || function () { };
  10.         Utils.request(window.indexedDB.open(“open”, this.name), function (e) {
  11.             self.db = e.result;
  12.             // if the version of this db is not equal to
  13.             // self.version then change the version
  14.             if (self.db.version !== self.version) {
  15.                 Utils.request(self.db.setVersion(self.ver), function (e2) {
  16.                     var txn = e2.result;
  17.                     // create object store
  18.                     self.db.createObjectStore(self.store_name,
  19.                                               self.store_key_path,
  20.                                               true);
  21.                     txn.commit();
  22.                     callback();
  23.                 });
  24.             } else {
  25.                 callback();
  26.             }
  27.         });
  28.     },
  29.     …

Object stores are created by calling the createObjectStore method on the database object. The first parameter is the name of the object store. This is followed by the string identifying the key path, and finally a Boolean flag indicating whether the key value should be auto-generated by the database when new records are added.

Adding data to object stores

New records can be added to an object store by calling the put method on the object store. A reference to the object store instance can be retrieved through the transaction object. Let’s implement the addNote method of our NotesStore object and see how we can go about adding a new record:

  1.     addNote: function (text, tags, callback) {
  2.         var self = this;
  3.         callback = callback || function () { };
  4.         var txn = self.db.transaction(null, TransactionMode.ReadWrite);
  5.         var store = txn.objectStore(self.store_name);
  6.         Utils.request(store.put({
  7.             text: text,
  8.             tags: tags
  9.         }), function (e) {
  10.             txn.commit();
  11.             callback();
  12.         });
  13.     },
  14.     …

This method can be broken down into the following steps:

  1. Invoke the transaction method on the database object to start off a new transaction. The first parameter is the names of the object stores that are going to be a part of the transaction. Passing null causes all the object stores in the database to be a part of the scope. The second parameter indicates the transaction mode. This is basically a numeric constant which we have declared like so:
  1. // IndexedDB transaction mode constants
  2. var TransactionMode = {
  3.     ReadWrite: 0,
  4.     ReadOnly: 1,
  5.     VersionChange: 2
  6. };
  1. Once the transaction has been created we acquire a reference to the object store in question through the transaction object’s objectStore method.
  2. Once we have the object store handy, adding a new record is just a matter of issuing an asynchronous API call to the object store’s put method passing in the new object to be added to the store. Note that we do not pass a value for the id field of the new note object. Since we passed true for the auto-generate parameter while creating the object store, the IndexedDB implementation should take care of automatically assigning a unique identifier for the new record.
  3. Once the asynchronous put call completes successfully, we commit the transaction.

Running queries with cursors

The IndexedDB way of enumerating records from an object store is to use a “cursor” object. A cursor can iterate over records from an underlying object store or an index. A cursor has the following key properties:

  1. range of records in either an index or an object store.
  2. source that references the index or object store that the cursor is iterating over.
  3. position indicating the current position of the cursor in the given range of records.

While the concept of a cursor is fairly straightforward, writing the code to actually iterate over an object store is somewhat tricky given the asynchronous nature of all the API calls. Let’s implement the listNotes method of our NotesStore object and see what the code looks like.

  1. listNotes: function (callback) {
  2.         var self = this,
  3.             txn = self.db.transaction(null, TransactionMode.ReadOnly),
  4.             notes = [],
  5.             store = txn.objectStore(self.store_name);
  6.         Utils.request(store.openCursor(), function (e) {
  7.             var cursor = e.result,
  8.                 iterate = function () {
  9.                     Utils.request(cursor.move(), function (e2) {
  10.                         // if “result” is true then we have data else
  11.                         // we have reached end of line
  12.                         if (e2.result) {
  13.                             notes.push(cursor.value);
  14.                             // recursively get next record
  15.                             iterate();
  16.                         }
  17.                         else {
  18.                             // we are done retrieving rows; invoke callback
  19.                             txn.commit();
  20.                             callback(notes);
  21.                         }
  22.                     });
  23.                 };
  24.             // set the ball rolling by calling iterate for the first row
  25.             iterate();
  26.         });
  27.     },

Let’s break this implementation down:

  1. First, we acquire a transaction object by calling the database object’s transaction method. Note that this time we’re indicating that we require a “read-only” transaction.
  2. Next we retrieve a reference to the object store via the objectStore method of the transaction object.
  3. Then we issue an async call to the openCursor API on the object store. The tricky part here is that every single iteration over a record in the cursor is itself an async operation! To prevent the code from drowning in a sea of callbacks, we define a local function called iterate to encapsulate the logic of iterating over every record in the cursor.
  4. This iterate function makes an async call to the cursor object’s move method and recursively invokes itself again in the callback if it detects that there are more rows to be retrieved. Once all the rows in the cursor have been retrieved we finally invoke the callback method passed by the caller handing in the retrieved data as a parameter.

Dive even deeper!

This is, by no means, comprehensive coverage of the API, despite what you may think! I only covered:

  1. Available options for implementing client-side storage today
  2. The various key aspects of the IndexedDB API, including:
  • Testing whether the browser supports it
  • Managing asynchronous API calls
  • Creating/opening databases
  • Key parts of the API including object stores, keys/values, versioning, and transactions
  1. Creating object stores
  2. Adding records to object stores
  3. Enumerating object stores using cursors

Hopefully it was up close and personal enough!


[This documentation is preliminary and is subject to change.]

Windows Internet Explorer 10 and Metro style apps using JavaScript support the Indexed Database API (:IndexedDB”). IndexedDB enables you to store structured data. Unlike cookies and DOM Storage, IndexedDB provides features that enable you to group, iterate, search, and filter JavaScript objects.

The IndexedDB API is defined by the World Wide Web Consortium (W3C)’s Indexed Database API specification, which is currently in the Working Draft stage.

IndexedDB objects

The IndexedDB API consists of several objects, each designed for specific tasks:

  • Data is stored in object stores, which are collections of JavaScript objects whose attributes contain individual values.
  • Each JavaScript object, sometimes called a record, in an object store has a common attribute called a key path; the value of this attribute is called a key value (or key). Key values uniquely identify individual records within an object store.
  • An index organizes objects according to the value of a common attribute. Indexes return sets of key values that can be used to obtain the individual records from the original object store.
  • cursor represents a set of values. When an index defines a cursor, the cursor represents the set of key values returned by the index. When an object store defines a cursor, the cursor represents a set of records stored in the cursor.
  • keyRange defines a range of values for an index or a set of records in an object store; key ranges allow you to filter cursor results.
  • database contains the object stores and indexes; databases also manage transactions.
  • request represents individual actions taken against objects in a database. For example, opening a database leads to a request object and you define event handlers on the request object to react to the results of the request.
  • transaction manages the context of operations and is used to maintain the integrity of database activities. For example, you can create object stores only in the context of a version change transaction. If a transaction is aborted, all operations within the transaction are canceled.

Asynchronous versus Synchronous

The Indexed Database API specification defines two APIs: a synchronous API and an asynchronous one. Internet Explorer 10 supports the asynchronous API. As a result, database operations do not execute immediately; instead operations return request objects that are executed in the background. Consequently, IndexedDB is an event-driven API. You create requests and then define event handlers to respond to the success or failure of those requests.

The following linked topics show you how to use IndexedDB to open a database, create transactions, and accomplish common database tasks:

 


IndexedDB is designed to cover most cases that need client-side storage. However, it is not designed for a few cases like the following:

  • Internationalized sorting. Not all languages sort strings in the same way, so internationalized sorting is not supported. While the database can’t store data in a specific internationalized order, you can sort the data that you’ve read out of the database yourself.
  • Synchronizing. The API is not designed to take care of synchronizing with a server-side database. You have to write code that synchronizes a client-side indexedDB database with a server-side database.
  • Full text searching. The API does not have an equivalent of the LIKE operator in SQL.

In addition, be aware that browsers can wipe out the database, such as in the following conditions:

  • The user requests a wipe out.
    Many browsers have settings that let users wipe all data stored for a given website, including cookies, bookmarks, stored passwords, and IndexedDB data.
  • The browser is in private browsing mode.
    Some browsers, have “private browsing” (Firefox) or “incognito” (Chrome) modes. At the end of the session, the browser wipes out the database.
  • The disk or quota limit has been reached.
  • The data is corrupt.
  • An incompatible change is made to the feature.

The exact circumstances and browser capabilities change over time, but the general philosophy of the browser vendors is to make the best effort to keep the data when possible.


scope
The set of object stores and indexes to which a transaction applies. The scopes of read-only transactions can overlap and execute at the same time. On the other hand, the scopes of writing transactions cannot overlap. You can still start several transactions with the same scope at the same time, but they just queue up and execute one after another.

cursor
A mechanism for iterating over multiple records with a key range. The cursor has a source that indicates which index or object store it is iterating. It has a position within the range, and moves in a direction that is increasing or decreasing in the order of record keys. For the reference documentation on cursors, see  IDBCursoror IDBCursorSync.

key range
A continuous interval over some data type used for keys. Records can be retrieved from object stores and indexes using keys or a range of keys. You can limit or filter the range using lower and upper bounds. For example, you can iterate over all values of a key between x and y.

For the reference documentation on key range, see IDBKeyRange.


key
A data value by which stored values are organized and retrieved in the object store. The object store can derive the key from one of three sources: a key generator, a key path, and an explicitly specified value. The key must be of a data type that has a number that is greater than the one before it. Each record in an object store must have a key that is unique within the same store, so you cannot have multiple records with the same key in a given object store.

A key can be one of the following type: string, date, float, and array. For arrays, the key can range from an empty value to infinity. And you can include an array within an array.

Alternatively, you can also look up records in an object store using the index.

key generator
A mechanism for producing new keys in an ordered sequence. If an object store does not have a key generator, then the application must provide keys for records being stored. Generators are not shared between stores. This is more a browser implementation detail, because in web development, you don’t really create or access key generators.

in-line key
A key that is stored as part of the stored value. It is found using a key path. An in-line key can be generated using a generator. After the key has been generated, it can then be stored in the value using the key path or it can also be used as a key.

out-of-line key
A key that is stored separately from the value being stored.

key path
Defines where the browser should extract the key from a value in the object store or index. A valid key path can include one of the following: an empty string, a JavaScript identifier, or multiple JavaScript identifiers separated by periods. It cannot include spaces.

value
Each record has a value, which could include anything that can be expressed in JavaScript, including: boolean, number, string, date, object, array, array, regexp, undefined, and null.

When an object or array is stored, the properties and values in that object or array can also be anything that is a valid value.

Note: The specification lets you store files and blobs, but this has not been implemented by any browser yet.


database

A repository of information, typically comprising one or more object stores. Each database must have the following:
Name. It identifies the database within a specific origin and stays constant throughout its lifetime. The name can be any string value (including an empty string). Current version. When a database is first created, its version is the integer 1. Each database can have only one version at any given time.
object store
The mechanism by which data is stored in the database. The object store persistently holds records, which are key-value pairs. Records within an object store are sorted according to the keys in an ascending order.

Every object store must have a name that is unique within its database. The object store can optionally have a key generator and a key path. If the object store has a key path, it is using in-line keys; otherwise, it is using out-of-line keys.

For the reference documentation on object store, see IDBObjectStore or IDBObjectStoreSync.

version
When a database is first created, its version is the integer 1. Each database has one version at a time; a database can’t exist in multiple versions at once. The only way to change the version is by opening it with a greater version than the current one. This will start a VERSION_CHANGE transaction and fire an upgradeneeded event. The only place where the schema of the database can be updated is inside the handler of that event.
Note: This definition describes the most recent specifications, which is only implemented in some up-to-date browsers. Old browsers implemented the now deprecated and removed IDBDatabase.setVersion() method.
database connection
An operation created by opening a database. A given database can have multiple connections at the same time.

transaction
An atomic and durable set of data-access and data-modification operations on a particular database. It is how you interact with the data in a database. In fact, any reading or changing of data in the database must happen in a transaction.

A database connection can have several active transaction associated with it at a time, so long as the writing transactions do not have overlapping scopes. The scope of transactions, which is defined at creation, determines which object stores the transaction can interact with and remains constant for the lifetime of the transaction. So, for example, if a database connection already has a writing transaction with a scope that just covers the flyingMonkey object store, you can start a second transaction with a scope of the unicornCentaur and unicornPegasus object stores. As for reading transactions, you can have several of them—even overlapping ones.

Transactions are expected to be short-lived, so the browser can terminate a transaction that takes too long, in order to free up storage resources that the long-running transaction has locked. You can abort the transaction, which rolls back the changes made to the database in the transaction. And you don’t even have to wait for the transaction to start or be active to abort it.

The three modes of transactions are: read/write, read only, and version change. The only way to create and delete object stores and indexes is by using a version-change transaction. To learn more about transaction types, see the reference article for IndexedDB.

Because everything happens within a transaction, it is a very important concept in IndexedDB. To learn more about transactions, especially on how it relates to versioning, see IDBTransaction, which also has reference documentation. For the documentation on the synchronous API, see IDBTransactionSync.

request
The operation by which reading and writing on a database is done. Every request represents one read or write operation.

index
A specialized object store for looking up records in another object store, called the referenced object store. The index is a persistent key-value storage where the value part of its records is the key part of a record in the referenced object store. The records in an index are automatically populated whenever records in the referenced object store are inserted, updated, or deleted. Each record in an index can point to only one record in its referenced object store, but several indexes can reference the same object store. When the object store changes, all indexes that refers to the object store are automatically updated.

Alternatively, you can also look up records in an object store using the key.

To learn more on using indexes, see Using IndexedDB. For the reference documentation on index, see IDBKeyRange.


Because the Indexed Database API specification is still evolving, Windows Internet Explorer 10 and Metro style apps using JavaScript use a vendor prefix (“ms”) for the msIndexedDB property. For best results, use feature detection to access the IndexedDB API, as shown in the following example:

var ixDB;
if ( window.indexedDB ) {
ixDB = window.indexedDB;
}

else if ( window.msIndexedDB ) {
ixDB = window.msIndexedDB;

Opening a database

When you open a database, an IDBRequest object is returned; however, the request has not yet been processed. The object is used to track the results of the request, which will be known after the requested has been processed in the background. Use this object to define event handlers that react to the results of the request, as shown in the following example:

var dbReq = ixDB.open( “Database1” );
dbReq.onsuccess = function( evt ) {
oDB = evt.target.result;
};

Request objects support events such as onsuccess and onerror. In the previous example, an inline function assigns the results of the request to a global variable that maintains the connection to the opened database. The result varies according to the request.

Request objects execute when they go out of scope; that is, when the current JavaScript block finishes.


If you have assumptions from working with other types of databases, you might get thrown off when working with IndexedDB. So keep the following important concepts in mind:

IndexedDB databases store key-value pairs. The values can be complex structured objects, and keys can be properties of those objects. You can create indexes that use any property of the objects for quick searching, as well as sorted enumeration.

IndexedDB is built on a transactional database model. Everything you do in IndexedDB always happens in the context of a transaction. The IndexedDB API provides lots of objects that represent indexes, tables, cursors, and so on, but each of these is tied to a particular transaction. Thus, you cannot execute commands or open cursors outside of a transaction.

Transactions have a well-defined lifetime, so attempting to use a transaction after it has completed throws exceptions. Also, transactions auto-commit and cannot be committed manually.

This transaction model is really useful when you consider what might happen if a user opened two instances of your web app in two different tabs simultaneously. Without transactional operations, the two instances could stomp all over each other’s modifications. If you are not familiar with transactions in a database, read the Wikipedia article on transactions. Also see transaction under the Definitions section.

The IndexedDB API is mostly asynchronous. The API doesn’t give you data by returning values; instead, you have to pass a callback function. You don’t “store” a value into the database, or “retrieve” a value out of the database through synchronous means. Instead, you “request” that a database operation happens. You get notified by a DOM event when the operation finishes, and the type of event you get lets you know if the operation succeeded or failed. This sounds a little complicated at first, but there are some sanity measures baked in. It’s also not that different from the way that XMLHttpRequest works.

IndexedDB is object-oriented. IndexedDB is not a relational database, which has tables with collections of rows and columns. This important and fundamental difference affects the way you design and build your applications.

In a traditional relational data store, you would have a table that stores a collection of rows of data and columns of named types of data. IndexedDB, on the other hand, requires you to create an object store for a type of data and simply persist JavaScript objects to that store. Each object store can have a collection of indexes that makes it efficient to query and iterate across. If you are not familiar with object-oriented database management systems, read the Wikipedia article on object database.

IndexedDB does not use Structured Query Language (SQL). It uses queries on an index that produces a cursor, which you use to iterate across the result set. If you are not familiar with NoSQL systems, read the Wikipedia article on NoSQL.

IndexedDB uses requests all over the place. Requests are objects that receive the success or failure DOM events that were mentioned previously. They have onsuccess and onerror properties, and you can call addEventListener() and removeEventListener() on them. They also have readyState, result, and errorCode properties that tell you the status of the request. The result property is particularly magical, as it can be many different things, depending on how the request was generated (for example, an IDBCursor instance, or the key for a value that you just inserted into the database).

IndexedDB uses DOM events to notify you when results are available. DOM events always have a type property (in IndexedDB, it is most commonly set to “success” or “error”). DOM events also have a target property that tells where the event is headed. In most cases, the target of an event is the IDBRequest object that was generated as a result of doing some database operation. Success events don’t bubble up and they can’t be canceled. Error events, on the other hand, do bubble, and can be canceled. This is quite important, as error events abort whatever transactions they’re running in, unless they are canceled.

IndexedDB adheres to a same-origin policy. An origin is the domain, application layer protocol, and port of a URL of the document where the script is being executed. Each origin has its own associated set of databases. Every database has a name that identifies it within an origin.


IndexedDB is a way for you to persistently store data inside a user’s browser. Because it lets you create web applications with rich query abilities, these applications can work both online and offline. IndexedDB  is useful for applications that store a large amount of data (for example, a catalog of DVDs in a lending library) and applications that don’t need persistent internet connectivity to work (for example, mail clients, to-do lists, and notepads).

IndexedDB lets you store and retrieve objects which are indexed with a “key.” All changes that you make to the database happens within transactions. Like most web storage solutions, IndexedDB follows a same-origin policy. So while you can access stored data within a domain, you cannot access data across different domains.

The API includes both an asynchronous API and a synchronous API. The asynchronous API can be used in most cases, while the synchronous API is for use with WebWorkers. Currently, no major browser supports the synchronous API. But even if synchronous APIs are supported, in the majority of cases where you use IndexedDB, you are likely to use the asynchronous API anyway.

IndexedDB is an alternative to WebSQL Database, which the W3C deprecated on November 18, 2010. While both IndexedDB and WebSQL are solutions for storage, they do not offer the same functionalities. WebSQL Database is a relational database access system, whereas IndexedDB is an indexed table system.


Introduction

Because web databases let you store data and assets in users’ browsers and provide you with rich query abilities, they enable you to create a new breed of web applications that can work online, offline, and states in between where the connection is flaky. Here are the main differences between the two databases.

 

Category WebSQL IndexedDB
Advantages A real, relational database implementation on the client (SQLite). * Allows fast indexing and searching of objects, so in a web application scenario, you can manage your data and read/write it fast.
* A NoSQL database that let you work with your JavaScript objects and indexing them based on your application needs.
* Works in asynchronous mode with moderately granular locking per transaction. This allows you to work inside the event-driven module of JavaScript.
Disadvantages * The spec is deprecated.
* Overhead of SQL language you need to master and transform your JavaScript objects into relational schema
* Not object driven
Harder to understand if you are coming from the world of relational databases.
Location Tables contain columns and rows objectStore contains Javascript objects and keys
Query Mechanism SQL Cursor APIs, Key Range APIs, and Application Code
Transaction Lock can happen on databases, tables, or rows on READ_WRITE transactions Lock can happen on database VERSION_CHANGE transaction, on an objectStore READ_ONLY and READ_WRITE transactions.
Transaction Commits Transaction creation is explicit. Default is to rollback unless we call commit. Transaction creation is explicit. Default is to commit unless we call abort or there is an error that is not caught.

Since November 18, 2010, the W3C announced that Web SQL database is a deprecated specification. This is a recommendation for web developers to no longer use the technology as effectively, the spec will receive no new updates and browser vendors aren’t encouraged to support this technology. The new alternative is IndexedDB which is already available on Chrome 12+ and Firefox 5+, and, soon, in IE 10 as well.


What is Sencha Touch?

Sencha Touch enables you to quickly and easily create HTML-5 based mobile apps that work on Android, iOS, and Blackberry devices and produce a native-app-like experience inside a browser.

Things you’ll need

Here’s what you need to get started:

  • The free Sencha Touch 2.0 SDK, currently in developer preview
  • A web server running locally on your computer
  • A modern web browser; Chrome or Safari are recommended

Download and unzip the latest version of the SDK. Place the unzipped folder into your web server’s document root. If you don’t have a web server or aren’t sure, we recommend using a simple one-click installer like WAMP or MAMP.

Once you have the folder in the right place just open your web browser, point it to http://localhost/sencha-touch-folder (or wherever your web server is configured to serve from) and you should see the Sencha Touch Welcome page. If that’s all working you’re ready to start your first app.

Aptana + Spket + ExtJS
  • Download and install Aptana Studio (includes Eclipse).
  • Start Aptana and navigate the application menu to: Help → Software Updates → Find and Install… → Search for new features to install → New remote site…
  • Name: “Spket”, Url: “http://www.spket.com/update/
  • Restart Aptana
  • Watch this Spket IDE Tutorialto see how to easily add Ext code assist (you can point it at the latest /src/ext.jsb to keep code assist up to date with the latest Ext version). The steps are basically:
    • Window → Preferences → Spket → JavaScript Profiles → New
    • Enter “ExtJS” and click OK
    • Select “ExtJS” and click “Add Library”, then choose “ExtJS” from the dropdown
    • Select “ExtJS” and click “Add File”, then choose the “ext.jsb” file in your “./ext-2.x/source” directory
    • Set the new ExtJS profile as the default by selecting it an clicking the “Default” button on the right-hand side of the “JavaScript Profiles” dialog.
    • Restart Aptana
    • Create a new JS file and type: Ext. and you should get the Ext Code completion options.

Spket IDe

AptanaIDE

  • Aptana is an Eclipse-based JavaScript IDE with powerful features with integrated Ext support. There’s also an impressive list of instruction videos for a quick tour. And it has Adobe Air support.
  • As of February 2008, Aptana is still being shipped with Code Assist for Ext 1.x only. You can download an updated Ext 2.x plugin here.

Today’s PhoneGap on Windows Phone 7 tip involves handling orientation changes. This means when a user rotates their phone from portrait to landscape, the content should rotate as well. Not only that, in some scenarios elements on the screen should reformat for readability and ease of interaction.

As I mentioned in the introductory post,, you can do a lot of things within your application just working with HTML/JS/CSS inside the PhoneGap control. But you’ll gain a whole new set of app superpowers if you step outside of that. We’re starting today with something small – how to have the PhoneGap part of application rotate properly as the phone changes orientation.

To get ready for today’s tip, let’s create a new project in Visual Studio using the PhoneGap template that’s described in the introduction to the tip series.

image

It’s sneaky because you need to click on the Visual C# section on the left pane of the window to display the entire list of templates.

Once you create the project, what you’ll have is essentially a Silverlight for Windows Phone project. PhoneGap is running in a host Silverlight XAML page. By default, Silverlight for Windows Phone applications do not run in landscape mode. You can observe this by running the application (press F5) and click one of the screen rotation buttons on the phone emulator.

image

You’ll see that the emulator rotates, but the text does not.

image

This is a characteristic of the host Silverlight application as well as the HTML rendering inside of the PhoneGap control.

Allowing the host application to support landscape mode is pretty easy. In Solution Explorer, double click on the MainPage.xaml file. In the Properties window, find the SupportedOrientations and change it toPortraitOrLandscape. Note that you can do this directly in the XAML if you are feeling adventurous. Also note that there is an Orientation property which controls the starting orientation, so if you want your app to launch in landscape orientation, this is how you would control that.

image

Run the app again and rotate the emulator.

image

Success! We now have the host page and the PhoneGap control rotating.

At this point you could also put JS code in to respond when the devices is rotated, to adjust the UI via swapping out divs or using CSS to control it. Here’s a simple example, based on the boilerplate index.htmlpage included in the template.

 1: <!DOCTYPE html>
 2: <html>
 3:   <head>
 4:     <meta name="viewport" content="width=device-width, height=device-height, initial-scale=1.0, maximum-scale=1.0, user-scalable=no;" />

 5:     <meta http-equiv="Content-type" content="text/html; charset=utf-8"/>
 6:     
 7:     <title>PhoneGap WP7</title>
 8:     
 9:       <link rel="stylesheet" href="master.css" type="text/css" media="screen" title="no title" 
 10:       charset="utf-8"/>
 11:       <script type="text/javascript">
 1:  
 2:           // provide our own console if it does not exist, huge dev aid!
 3:           if(typeof window.console == "undefined")
 4:           {
 5:           window.console = {log:function(str){window.external.Notify(str);}};
 6:           }
 7:         
 8:           // output any errors to console log, created above.
 9:           window.onerror=function(e)
 10:           {
 11:               console.log("window.onerror ::" + JSON.stringify(e));
 12:           };
 13:         
 14:           console.log("Installed console ! ");
 15:       </script>
 1:  
 2:       
 3:       <script type="text/javascript" charset="utf-8" src="phonegap-1.2.0.js">
 1: </script>
 2:        
 3:       <script type="text/javascript">
 4:  
 5:         document.addEventListener("deviceready",onDeviceReady,false);
 6:  
 7:         // once the device ready event fires, you can safely do your thing! -jm
 8:         function onDeviceReady()
 9:         {
 10:             document.getElementById("welcomeMsg").innerHTML += "PhoneGap is ready! version=" + window.device.phonegap;
 11:             window.addEventListener("orientationchange", orientationChange, true);
 12:             
 13:         }
 14:         function orientationChange(e) {
 15:             var orientation = "portrait";
 16:             if (window.orientation == -90 || window.orientation == 90) orientation = "landscape";
 17:             document.getElementById("orientation").innerHTML = orientation;
 18:         }
 19:       </script>
 12:       
 13:      
 14:       
 15:  
 16:   </head>
 17:   <body>
 18:     <h1>Hello PhoneGap</h1>
 19:     <div id="welcomeMsg"></div>
 20:     <div id="orientation">portrait</div>
 21:   </body>
 22: </html>

Note the new DIV to hold the text identifying the current orientation state, and the orientationChangefunction wired up to the event in the onDeviceReady function. It’s the host page rotation that causes this event to fire inside the PhoneGap user control. Without the property to allow both orientations, this event would never fire.

If you want to handle the orientation changed outside the PhoneGap part of your app, you can wire up a C# event handler at the page level as well. To do this, return to the properties windows for MainPage.xaml and switch to Events at the top of the window. Find the OrientationChanged event. We’ll create an event handler by double clicking on the event name.

image

The MainPage.xaml.cs window will open, ready for us to type in our code.

image

Allowing the user to work with the application in both portrait and landscape layout makes it more flexible. In later articles I will cover how there are even aspects of Windows Phone development that respond automatically to the orientation changing.

Let me know your feedback in the comments, and look for many more PhoneGap on Windows Phone tips coming in subsequent weeks, in increasing levels of complexity. 🙂


“So two applications walk into a bar…”

One of the user interface constructs that’s unique to Windows Phone is the Application Bar. The App Bar (as we affectionately call it) gives users a standard place to find buttons for commonly used tasks within an application, and menus for lesser used but still necessary tasks.

Here’s a screenshot of the Search screen where you can see the App Bar at the bottom.

appbar1

While it’s possible to create something inside your HTML in PhoneGap that mimics the look and behavior of the native one, in my opinion it makes more sense to use the native Windows Phone one since it’s already there. There are a lot of advantages to using the native App Bar:

  • It doesn’t scroll with your content.
  • It automatically rotates the icons when the user changes from portrait to landscape orientation.
  • It will recolor the icons if the user switches between light and dark themes.
  • The buttons have descriptions to help the user realize what they are for.
  • It will make your application look like it belongs on Windows Phone!

Because of the ability to communicate between the hosting Silverlight page and the HTML content in the PhoneGap control, you can do all the coding for the button or menu functionality in JavaScript and just invoke those functions when the button is clicked.

Start by creating a new project using the GapAppStarter template I described in Tip #1.  We’re going to add the icon for the button to the project. There are several that can be found on your system after you install the Windows Phone developer tools. They’re stored in your Program Files folder (typically C:\Program Files (x86)\Microsoft SDKs\Windows Phone\v7.1\Icons). Create a folder in the project called Icons (right click on the project in Solution Explorer and chose Add, New Folder. Let’s call it Icons. Then, right click on the Icons folder and choose Add Existing Item. Browse to the Dark folder beneath the one mentioned above and pick your favorite. I’m using the refresh icon. Make sure you click on the png file and, in the Properties window, change Build Action to Content.

image

Now we’re ready to add the App Bar to the page. Open up mainpage.xaml and scroll to the bottom in source view. Add this XAML in below the </Grid> tag.

<phone:PhoneApplicationPage.ApplicationBar>
    <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True" Opacity="0.5">
        <shell:ApplicationBarIconButton IconUri="/icons/appbar.refresh.rest.png"
        x:Name="AppbarButtonRefresh"  Text="Refresh"/>
    </shell:ApplicationBar>
</phone:PhoneApplicationPage.ApplicationBar>

This will result in an App Bar having 50% opacity, with one button on it. If you want more buttons, just add more ApplicationBarIconButton lines. If you run the app in the emulator, you’ll see the button on the App Bar at the bottom. In addition to ApplicationBarIconButtons you could also add ApplicationBarMenuItems which show up when the user expands the App Bar by tapping the ellipses.

appbar2

Clicking the button doesn’t do anything yet, so let’s add the code. First, inside the PhoneGap portion, let’s add some JavaScript to call. Here’s an example.

function appbar_refresh() {
    var currentTime = new Date();
    document.getElementById("welcomeMsg").innerHTML = "You clicked refresh at " + currentTime.toString();
    }

To call this from our Silverlight code, just add an event handler for the button in XAML and put some code in. Go back to the MainPage.xaml page, put your cursor just inside the closing tag /> for the ApplicationBarIconButton element, type Click= and chose New Event Handler from the dropdown. Then in the C# code, just pass control into your JavaScript by typingPGView.Browser.InvokeScript(“appbar_refresh”);

Run the project again, and every time you click the button, the time should update on the page. Note that in this approach, we’re putting all the logic to handle the button click in JavaScript, so if you have the same PhoneGap application on other platforms, you would just need buttons in the HTML in that version to invoke that functionality.

If you want to experience the App Bar in landscape mode, just follow the instructions in Tip #1 to make your application able to rotate when the user changes device orientation. The app bar will automatically adjust for landscape. You will note that in Landscape the App Bar is covering part of the PhoneGap output. You could adjust that by changing the layout within the HTML when the screen rotates. But it’s useful here because here we get to see the semi-opacity of the bar as we configured it.

appbar3

It’s worth mentioning that sometimes applications need to change the App Bar, like hiding it, changing or hiding or disabling buttons or menus, etc. You can create an event handler for the ScriptNotify event as in Tip #2 and invoke that from JavaScript to manipulate the App Bar. The steps for manipulating the App Bar in code can be found in this MSDN article.

Now let’s see some great PhoneGap apps with App Bars!


In this tip, I’m going to show you how to send an SMS message from PhoneGap on Windows Phone using the SMSCompose member of the Microsoft.Phone.Tasks namespace. There are a lot of useful launchers and choosers in that namespace, and I’ll be writing about some in detail in a future blog post. I’m also using the SMSCompose task to introduce another way to interoperate between your PhoneGap application and the underlying platform, and that is through a PhoneGap plugin.

In the previous tip, I showed how you could write some code in the Silverlight application that is hosting the PhoneGap application, and call it from PhoneGap via the scripting interface. Plugins are simply a different mechanism to invoke platform specific code. The technique is pretty well known to PhoneGap developers already, so it makes sense to explore how to use it in Windows Phone.

PhoneGap has quite a few cross platform APIs in place to handle interop with each platform, in areas such as location, storage, sounds, etc. These are very useful in allowing you to write code that uses those features only once and run it on any device that PhoneGap supports. But, sometimes there are platform specific things you want to take advantage of (and in the case of Windows Phone there are many) so Plugins is a handy way to do that. Jesse MacFayden has also written a great blog post to get you started on writing PhoneGap plugins for Windows Phone.

We’re going to write a plugin to allow us to send an SMS message from JavaScript in PhoneGap using Windows Phone. First, let’s look at the code in C# for sending an SMS message.

SmsComposeTask smsTask = new SmsComposeTask();
smsTask.To = "4045551212";
smsTask.Body = "Message for you, sir!";
smsTask.Show();

Pretty straightforward. Note that in this API, it’s fire-and-forget in that we do not receive a return value. What we’d like to do now is provide a JavaScript function we can call from our PhoneGap application that could invoke that. Maybe we want it to look like this:

sendSMS.send(‘4045551212’, ’Hello’);

Let’s connect those two ends. First of all, we need to put the above C# code into a method in a class that can be called by the PhoneGap runtime. These classes can conveniently be placed in the Plugins folder in your PhoneGap project.

image

To add the class, right click on the Plugins folder and chose Add New Item. Pick Class and name it whatever you want. I named this one SendSMS.cs. The class has a lot of extra using statements at the top we don’t need, so feel free to remove them for simplicity.

Next, we need to change the namespace. By default, the namespace is our Visual Studio project. However, we want this class to be part of the PhoneGap runtime library’s namespace, so change the namespace at the top of the code to be WP7GapClassLib.PhoneGap.Commands. We also need to change our class to inherit from the PhoneGap runtime’s BaseCommand class so it has all the plumbing in place to be invoked from our JavaScript. So after the name of the class, put : BaseCommand.

Then, inside the class, we need a method to hold our SMS Compose code. I created one called send but you can call it whatever you want. Now we can put the code from above in that method. In order to reference the SmsCompose task we need to bring its namespace reference into our class by adding using Microsoft.Phone.Tasks to the top of the class. By now,you should have something that looks like this:

using Microsoft.Phone.Tasks;

namespace WP7GapClassLib.PhoneGap.Commands
{
    public class SendSMS : BaseCommand
    {
        public void send(string args)
        {
            SmsComposeTask smsTask = new SmsComposeTask();
            smsTask.To = "(678) 555-1212”;
            smsTask.Body = "Hello";
            smsTask.Show();
        }
    }
}

Although the recipient and message body are hard-coded, let’s leave that for now so we can get it working.

The next step is to set up a way to invoke this send method from the JavaScript in your PhoneGap application. The way you implement this method is up to you, but most PhoneGap developers will agree that it’s important to provide a consistent way to call Plugins across platforms. To that end, I went out and looked at a list of PhoneGap plugins developed by the community, and I found one that handles sending SMS messages on the iPhone. So I will create a method signature in JavaScript that matches the one for use on iOS. The JavaScript is below (inside of a .js file in your www directory in the project).

function SMSComposer(){    
this.resultCallback = null;
}

SMSComposer.prototype.showSMSComposer = function(toRecipients, body)
{
    var args = {};    
    if(toRecipients)        
        args.toRecipients = toRecipients;    
    if(body)
        args.body = body;

    PhoneGap.exec(null, null, "SendSMS", "send", args);

    }

PhoneGap.addConstructor(function () {
    if (!window.plugins) {
        window.plugins = {};
    }
    window.plugins.smsComposer = new SMSComposer();
    }
   );

This is a little different from the iPhone one. Since the SmsComposeTask doesn’t return a result, we don’t need any callbacks in JavaScript. The connection point in here is in the PhoneGap.exec call. The first two parameters (currently null) would be the function names that you’d write to handle a callback and an exception coming out of this method call. The third parameter is the name of the C# class, and the fourth is the C# method name. The fifth parameter is a JavaScript object containing any information we want to send to the function we’re calling. For now remember we are ignoring them in our C# code.

With the Plugin’s API in place, just reference that .js file in a <script> tag, and write some JavaScript to callwindow.plugins.smsComposer.showSMSComposer(recipients, message) with one or more recipient and a message. That’s the same JavaScript you’d write to call the iOS version using that plugin on that platform. By the way, this is what the SMS composer screen will look like to your end users, and if you run your project in the emulator, you’ll see the same thing.

ss1

The only thing we have to finish here is to actually make use of the recipient list and message that are sent into the C# function via its args parameter. Since the arguments are coming in as a JSON string, we can use a .NET deserialization library to handle that for us. We create a class that has the data fields that match the JSON object graph (recipients and message) and then deserialize the args parameter into an instance of that class. The deserialization is automatic provided the members of the class match the names and data types of the JSON object graph. Here’s the full version of the C# class with those changes:

using Microsoft.Phone.Tasks;
using System.Runtime.Serialization;

namespace WP7GapClassLib.PhoneGap.Commands
{

    public class sendSMSargs
    {
        [DataMember]
        public string toRecipients;

        [DataMember]
        public string body;
     }
    public class SendSMS : BaseCommand
    {
        public void send(string args)
        {
            sendSMSargs myargs = JSON.JsonHelper.Deserialize<sendSMSargs>(args);

            SmsComposeTask smsTask = new SmsComposeTask();
            smsTask.To = myargs.toRecipients;
            smsTask.Body = myargs.body;
            smsTask.Show();

            this.DispatchCommandResult();
        }
    }
}

If you want to use this plugin in other PhoneGap on Windows Phone 7 projects, you’ll just need the C# class file and the JavaScript that calls PhoneGap.exec. As I mentioned, there are many Windows Phone APIs that can be wrapped in plugins for use in PhoneGap, and I’ll be covering some in future blog posts.

There are definitely times you’ll want to use plugins and times you’ll want to use the script interface, and I’ll cover those in more detail in a later post as well. Happy coding!





Enter your email address to subscribe to this blog and receive notifications of new posts by email.

Join 4 other followers