Code examples for a Dojo Mobile one page application. Backend in Ruby/Sinatra
I wrote a few days ago that I am excited about how easy it is to make simple one page web apps using Dojo Mobile that look good and work fine on portable devices (Android and iOS) and regular web browsers. I don't do a lot of UI development for my work but when I do write web apps it is great to also be able to support mobile devices with a small amount of additional simple code. In this post I will show you hopefully useful code snippets for cookingspace.com that may save you some time if you want to write the same type of apps.
Last weekend I decided that I wanted a new mobile web interface to my old Cooking Space web site. I wanted to be able to quickly look up a recipe on my cellphone, see the ingredient list, and be able to specify how many people need to be served. I also want to see the nutrition data for the recipe. A top level requirement is that once the web page renders then everything is updated with AJAX. There are only two user interactions:
- Enter a few search terms to locate recipes and show them in a format compatible with both small devices and a web browser on a laptop.
- A control for changing the number of people a recipe will serve - this adjusts the amounts in the ingredients list using AJAX.
There is a lot of backend Ruby model code that I won't show. I'll show the controller code, the Javascript, and the HTML5 and Dojo application code I used.
The Sinatra controller code is very simple since it only has to serve up the web page and then handle AJAX calls using the model code. I reformatted the Ruby code to make it a little more verbose and easier to understand:
enable :sessions get '/' do session["n"] ||= "4" erb :index end get '/getr' do # handles AJAX calls from Javascript ret = "No recipes found" q = params['q'] n = params['n'] session["n"] = n.to_s if n if q && q.length > 0 ids = search q # calls model code to do search if ids && ids.size > 0 num_served = session["n"].to_i ret = recipes_to_html num_served,ids.collect {|x| x[0]} end end ((!q || q.length == 0) && n) ? "*" : ret endThe handler for the route '/getr' uses two parameters for search terms and number of people to be served. The web page has two sections: the top section contains the search input form and a control to set the number of people servered. The bottom section just contains an HTML element that receives the AJAX response. I usually put Javascript in a separate file but the Javascript code for this app is so short and simple I just included it in the HTML/ERB template file. Much of the following Dojo boilerplate comes from the excellent Dojo Mobile Documentation:
<!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=no"/> <meta name="apple-mobile-web-app-capable" content="yes"/> <title>CookingSpace</title> <script src="http://ajax.googleapis.com/ajax/libs/dojo/1.7.1/dojo/dojo.js" data-dojo-config="async:false"></script> <script> require(["dojox/mobile/parser", "dojox/mobile/deviceTheme", "dojox/mobile/compat", "dojox/mobile", "dojox/mobile/TextBox"], function(parser, deviceTheme) { parser.parse(); } ); function ajax_helper() { dojo.xhrGet({ url: "/getr?q=" + dojo.byId("q").value + "&n=" + dojo.byId("served").value, handleAs:"text", timeout: 8000, // 8 seconds load: function(data, args) { if (data != "*") { dojo.byId("rcontent").innerHTML = data; } }, error: function(err, args){ dojo.byId("rcontent").innerHTML = "An unexpected error occurred: " + err; } }); } dojo.connect(dojo.byId("q"), 'onchange', ajax_helper); </script> </head> <body> <div id="settings" data-dojo-type="dojox.mobile.View" data-dojo-props="selected: true"> <h3 data-dojo-type="dojox.mobile.Heading"> CookingSpace Mobile Recipe Lookup </h3> <div data-dojo-type="dojox.mobile.RoundRect" data-dojo-props='shadow:true'> <input id="q" name="q" size="40" data-dojo-type="dojox.mobile.TextBox" placeHolder="Search for recipes" /> Number served: <select name="served" id="served"> <option value="1">1 person</option> <option value="2" selected="selected">2 people</option> <option value="3">3 people</option> <option value="4">4 people</option> <option value="5">5 people</option> <option value="6">6 people</option> </select> </div> <div id="rcontent" data-dojo-type="dojox.mobile.RoundRect"> ... </divPlease note that I set asynchronous loading of Dojo resources to false to make sure that the Dojo parser had a chance to parse the HTML on this page marking elements with Dojo types before processing my Javascript. This simplest approach is OK for a one page web app that only gets loaded once.
This is a simple example and hopefully will give you a good start creating one page mobile HTML5 web apps using Dojo Mobile.
Thanks, very helpful! Also, your code where not well formatted while viewing from my phone, but I guess that's a fault of blogger.
ReplyDeleteHello John, I try to narrow the width of code listings to look OK on a web browser, but not so easy for a smartphone :-)
ReplyDelete