Building Mobile Apps with WebMatrix and jQuery Mobile

jquery Mobile was launched just a month or so ago, and is designed to make it as easy to build JavaScript-enabled web applications for mobile phones as for the desktop browser. Still in Alpha, the library has a way to go, but it already offers a rather interesting experience when used as part of a mobile phone targeted web application. Here's a first look at how to use jQuery Mobile with WebMatrix to build a mobile web application.

Before I start, here's some stuff you should know about jQuery Mobile. The jquery Mobile library works on top of the core jQuery library. In essence, it is an alternative jQuery UI library which in the Alpha release applies a very iOS (iPhone, iPad) looking styling to elements. It also makes use of CSS3 and HTML 5 custom data attributes. Consequently, to get the full effect, you need to use a browser which has reasonable support for HTML 5. That effectively excludes Internet Explorer 8 and below. The jQuery team have produced a chart showing how much support they plan to offer for each browser here, but if you want to test your application in a browser, you should use FireFox, Safari, Google Chrome or Opera. Alternatively, you can use an emulator such as iBBDemo2, which is an Adobe Air based application. Finally, you could use IE9, which is in Beta at the moment (and doesn't seem to manage rounded corners that well). IE8 does have one handy use - it can be used to debug your site. Any jQuery Mobile application is designed to degrade in a browser that doesn't support HTML 5 or CSS 3, which means that compilation and runtime error messages are clearly visible in IE8.

The sample site I have built, and which is available for download at the end of the article is extremely simple. It makes use of a SQL CE 4.0 version of the Northwind database. It doesn't do much - it lists product categories and allows you to drill down to details on specific products. I haven't added any data writing capabilities yet. Here's how the main page looks in the emulator:

The jQuery Mobile library consists of a JavaScript file which makes use of the jQuery Core, and a style sheet together with some images. I downloaded the zip containing the library from the jquery Mobile site and placed jquery.mobile-1.0a2.min.js in a newly created folder which I called Scripts, along with the latest version of the jquery Core library - jquery-1.4.4.min.js. I also created a folder called Styles, in which I placed the jquery.mobile-1.0a2.min.css file that came with the download. To the Styles folder, I also added the images which came with the download in a folder called images. I wouldn't normally add images to a style sheet folder, but this saved me having to edit the css file to point to images in a new location.

As you can see from the image, I also created a folder called Shared and added a new CSHTML file to that called _Layout.cshtml. This is the Layout page, which contains the references to jQuery and the style sheets:

<!DOCTYPE html>

<html lang="en">
    <head>
        <meta charset="utf-8" />
        <title>@Page.Title</title>
        <link rel="stylesheet" href="@Href("~/Styles/jquery.mobile-1.0a2.min.css")" type="text/css" />

        <script src="@Href("~/Scripts/jquery-1.4.4.min.js")" type="text/javascript"></script>
        <script src="@Href("~/Scripts/jquery.mobile-1.0a2.min.js")" type="text/javascript"></script>
    </head>
    <body>
        <div data-role="page" data-theme="c"> 
            <div data-role="header">
                <h1>@Page.Header</h1>
            </div>
            @RenderBody()
        </div>
    </body>
</html>

The first thing to point out is that since jQuery Mobile likes HTML 5, you need the HTML 5 doctype:

<!DOCTYPE html>

Razor uses this doctype by default, which makes Web Pages an ideal platform to develop jQuery Mobile sites with. The same is true of ASP.NET MVC 3 if you are using the Razor View Engine, or another View Engine which supports HTML 5. The default doctype for Web Forms is still XHTML 1.1, so if you wanted to create a Web Forms app, or an MVC app that makes use of the Web Forms View Engine, you need to amend the doctype.

The content is housed in a div with two attributes: data-role and data-theme. When page is specified as the value for the data-role attribute you are telling jQuery that the content is a View. There are a number of themes (styles) built into jQuery Mobile. These are specified via the data-theme attribute. I have chosen the one labeled as "c". The first image shows a bar across the top of the View containing "Categories". This styling is created by applying "header" as a value to the data-role attribute. I have designed it so that the header is part of the Layout, but dynamic so that I can set it from each of the individual pages.

Next I added a new CSHTML file called Default.cshtml. This is the "home page" of my mobile site:

@{
    Layout = "~/Shared/_Layout.cshtml";
    Page.Title = "Home";
    Page.Header = "Categories";
    var db = Database.Open("Northwind40");
    var sql = @"SELECT [Category ID] AS ID, [Category Name] As Name 
              FROM Categories ORDER BY [Category Name]";
    var data = db.Query(sql);
}
 <div data-role="content">
    <ul data-role="listview" data-inset="true" data-theme="d">
        @foreach(var item in data){
            <li><a href="/Category/@item.ID">@item.Name</a></li>
        }
    </ul>
</div>

The code block at the top of the page defines the layout page, and sets the title and the header content. Then it queries the database for the list of Categories which it returns in alphabetical order. The Categories are displayed as list items within an unordered list whose data-role is defined as listview. The list view is placed inside an element which has been given the data-role="content" attribute, which separates it from headers and footers. I have also applied data-inset="true" to the list so that the list acquires some borders and margins. The result is the same as you see in the first image. You can see that any list item which contains a hyperlink will automatically get an arrow pointing to the right. That's applied by jQuery itself. At the moment, there's nothing for the hyperlink to point to so the next step is to add a page called Category.cshtml:

@{
    Layout = "~/Shared/_Layout.cshtml";
    Page.Title = "Products";
    Page.Header = "Products";
    if(UrlData[0].IsEmpty()){
        Response.Redirect("~/");
    }
    var db = Database.Open("Northwind40");
    var sql = @"SELECT [Product ID] AS ID, [Product Name] As Name FROM Products 
                WHERE [Category ID] = @0 ORDER BY [Product Name]";
    var category = UrlData[0];
    var data = db.Query(sql, category);
    var firstLetter = "";
}
<div data-role="content">
    <ul data-role="listview" data-inset="true" data-theme="d" data-dividertheme="b">
    @foreach(var item in data){
        if(item.Name.Substring(0,1) != firstLetter){
            <li data-role="list-divider">@item.Name.Substring(0,1).ToUpper()</li>
            firstLetter = item.Name.Substring(0,1);
        }
        <li><a href="/Product/@item.ID">@item.Name</a></li>
    }
    </ul>
</div

This page detects which category was selected from the Url. If none was, the user is redirected back to the home page. Otherwise the products belonging to the selected category are taken from the database and displayed in alphabetical order. There's also a variable called firstLetter which is set to an empty string. That's used to act as a seed value for the alphabetical listing. You can see the unordered list being used as in the previous page, but this time a new data-role is introduced: data-role="list-divider". This is not an item in the list itself. Instead, I have chosen to categorise the result by the first letter in the product name. You can see how this appears in the next image, which also shows the preview in the desktop version of Safari:

As you can see, each letter is displayed in a blue background list divider, whereas the actual data items are displayed with the now familiar right arrow. The logic which causes the alphabet to display goes as follows:

If the first letter of the current item in the foreach loop (obtained using Substring) is not the same as the value of the firstLetter variable, print the first letter of the current item. then set the value if firstLetter to the value of the first letter of the current item. This prevents the condition returning true if the next item begins with the same letter. When the next item in the result starts with a different letter, the condition returns false, so its first letter is printed.

Apart from seeing how the View is formatted in Safari, you also get an indication of how one View transitions to another, as the experience emulates that of the iPhone for example. New pages are retrieved via AJAX calls, and the resulting HTML is appended to the current DOM by jQuery Mobile. Then an effect is applied to the existing View so that it slides to the left by default, with the new View sliding in to replace it. Other transition effects can be achieved through the data-transition attribute which you would apply to the link itself eg:

<li><a href="/Product/@item.ID" data-transition="flip">@item.Name</a></li>

Options include slideup, slidedown, fade, pop and flip.

The final page shows the details for any selected product:

@{
    Layout = "~/Shared/_Layout.cshtml";
    Page.Title = "Products";
    Page.Header = "Product details";
    if(UrlData[0].IsEmpty()){
        Response.Redirect("~/");
    }
    var product = UrlData[0];
    var db = Database.Open("Northwind40");
    var sql = @"SELECT Products.[Product Name] AS Name, 
                Categories.[Category Name] AS Category, 
                Suppliers.[Company Name] AS Supplier, 
                Products.[Quantity Per Unit]  AS Quantity,
                Products.[Unit Price] AS Price, 
                Products.[Units In Stock] AS InStock, 
                Products.[Units On Order] AS OnOrder
                FROM Products 
                INNER JOIN
                Categories ON Products.[Category ID] = Categories.[Category ID] 
                INNER JOIN
                Suppliers ON Products.[Supplier ID] = Suppliers.[Supplier ID]
                WHERE Products.[Product ID] = @0";
    
    var item = db.QuerySingle(sql, product);
}

<div data-role="content" data-theme="d">
    <ul data-role="listview" data-inset="true" data-theme="d" data-dividertheme="b">
        <li data-role="list-divider">@item.Name</li>
        <li>Supplier: @item.Supplier</li>
        <li>Category: @item.Category</li>
        <li>Quantity per unit: @item.Quantity</li>
        <li>Units In Stock: @item.InStock</li>
        <li>Units On Order: @item.OnOrder</li>
    </ul>
</div>    

You should be getting the gist by now, and it shouldn't be too much of a surprise to see that the code above renders like the image below:

jQuery Mobile still has a way to go. Support across browsers is a little patchy, but even in its currrent Alpha state, it provides a really powerful UI framework for building cross-platform mobile web sites that look and feel very familiar to mobile app users. Windows 7 users will be a little disappointed with the result because of the lack of support for HTML 5 in IE 8, but they can be persuaded to download and use Opera Mobile until IE 9 makes an appearance. There will be a lot more work done to jQuery Mobile before its full release, which seems to be slated for early 2011.

You can dowload the