jQuery Mobileによるアプリケーション開発
はじめに
本稿では、モバイルWebアプリケーション用フレームワークであるjQuery Mobileについて説明します。jQuery Mobileの特長は、マークアップを主体としているため、レイアウト・デザインはHTMLで定義し、ロジックをJavaScriptで書くという、これまでのWebアプリケーション開発に用いた技術を使って簡単にモバイルWebアプリケーションを開発できるという点です。
また、クロスプラットフォームで動作するように設計されており、jQuey Mobileを使うことにより、様々な端末で動作するアプリケーションを開発することができます。
jQuery Mobileを使うことで、モバイル向けのWebサイトや簡単なフォームを持つWebサイトを簡単に作ることができますが、本稿ではモバイルWebアプリケーションを開発することを主題として説明します。
本稿では、執筆時点での最新版であるjQuery Mobile 1.3.0を前提として説明します。
また、本稿で利用する全ソースはこちらにあります。
セットアップ
それではjQuery Mobileを使ったモバイルWebアプリケーションを開発するために、開発環境のセットアップを行います。
まずは必要なファイルをダウンロードします。
jQuery Mobileのダウンロードページからここではjquery.mobile–1.3.0.zip をダウンロードします。
また、jQuery MobileはjQueryが依存ライブラリとなりますので、jQueryのダウンロードページから執筆時点での最新版である1.9.1をダウンロードします。
画面デザインを定義するindex.htmlを作成し、必要なファイルの定義を行います。
index.html
1 | < br /><!DOCTYPE html>< br /><html>< br /><head>< br /><%%KEEPWHITESPACE%%> <title>Memo</title>< br /><%%KEEPWHITESPACE%%> <meta name="viewport" content="width=device-width, initial-scale=1">< br /><%%KEEPWHITESPACE%%> <link href="../lib/css/jquery.mobile-1.3.0.css" rel="stylesheet">< br /><%%KEEPWHITESPACE%%> <!-- 以下にscriptを定義する -->< br /><%%KEEPWHITESPACE%%> <!-- jQuery -->< br /><%%KEEPWHITESPACE%%> <script src="../lib/js/jquery-1.9.1.js"></script>< br /><%%KEEPWHITESPACE%%> <!-- jQuery Mobileの初期設定用script -->< br /><%%KEEPWHITESPACE%%> <script src="jqm-setting.js"></script>< br /><%%KEEPWHITESPACE%%> <!-- jQuery Mobile -->< br /><%%KEEPWHITESPACE%%> <script src="../lib/js/jquery.mobile-1.3.0.js"></script>< br /></head>< br /><body>< br /><%%KEEPWHITESPACE%%> <!-- ここにマークアップを記述する -->< br />< br /><%%KEEPWHITESPACE%%> <!-- アプリケーションロジックを書くscript -->< br /><%%KEEPWHITESPACE%%> <script src="app.js"></script>< br /></body>< br /></html>< br /> |
JavaScriptを定義する部分は、順番も含めて上記のように定義する必要であります。
jqm-setting.jsはjQueryMobile全体に対する初期化の設定を定義したJavaScriptであり、jquery–1.9.1.jsの後で、jquery.mobile–1.3.0.jsより前に定義する必要があります。
app.jsがアプリケーションのロジックを記述するJavaScriptになります。
head内にmetaタグでviewportを設定しています。これは画面の表示サイズを定義するもので、デバイスのサイズに合わせて画面サイズを決めるように以下のように定義します。
この設定によりスマートフォンやタブレットの縦横を変更しても、その横幅に合わせた画面サイズになります。
1 | < br /><meta name="viewport" content="width=device-width, initial-scale=1">< br /> |
jQueryMobile全体に対する初期化設定
jQueryMobile全体に対して初期化設定を行うためのjqm-setting.jsについて確認しましょう。
jqm-setting.js
1 | <br />$(document).on( "mobileinit" , function () {<br /><%%KEEPWHITESPACE%%> "use strict" ;<br /><%%KEEPWHITESPACE%%> $.mobile.page.prototype.options.addBackBtn = true ;<br />});<br /> |
jQuery Mobile全体に関わる初期設定処理を行うためには、mobileinitiイベントの発生を検知し、指定したコールバック関数の中で設定を行います。
ここでは、各ページに自動的にBackボタンが配置されるように、「$.mobile.page.prototype.options.addBackBtn」を ture に設定しています。
jQuery Mobileによる画面デザイン
jQuery Mobileは画面のデザインをHTMLを書くことで行います。
本稿ではjQuery Mobileを使って、モバイル用のMemoアプリケーションを開発します。
モバイル用のMemoアプリケーションの画面イメージ
jQuery Mobileを使ってモバイルWebアプリケーションを作る場合、通常は Single Page Application として開発します。
Single Page Applicationとは1回のページロードでアプリケーションに必要なHTML、CSS、JavaScriptなどの必要ファイルを取得し、
それ以降はサーバに対してページロードは行わずに、クライアント側にて処理を行うアプリケーションです。
表示に必要なデータはサーバからajaxで取得し、クライアント側にてDOMを書き換えて画面に反映します。
それでは実際にMemoアプリケーションの画面デザインを定義するindex.htmlを見てみましょう。
画面レイアウトは header、content、footer という構成になるようにします。
画面構成イメージ
ページを定義するには、data-role=“page”と定義したdivタグを作成します。
id属性がページを一意に特定するキーとなります。
そして、header、content、footerの各部分は、data-roleをheader、content、footerに指定したdivダグを作って定義します。
index.html
1 | <br /><body><br /><div data-role= "page" id= "index" data-add-back-btn= "false" ><br /><%%KEEPWHITESPACE%%> <div data-role= "header" ><br /><%%KEEPWHITESPACE%%> <h1>Memo</h1><br /><%%KEEPWHITESPACE%%> <a href= "#add" class = "ui-btn-right" data-icon= "plus" >Add</a><br /><%%KEEPWHITESPACE%%> </div><br /><br /><%%KEEPWHITESPACE%%> <div data-role= "content" ><br /><%%KEEPWHITESPACE%%> <ul id= "memolist" data-role= "listview" data-inset= "true" ><br /><%%KEEPWHITESPACE%%> <li><a><h2>Title</h2><p>Content</p></a></li><br /><%%KEEPWHITESPACE%%> <li><a><h2>Title2</h2><p>Content2</p></a></li><br /><%%KEEPWHITESPACE%%> </ul><br /><%%KEEPWHITESPACE%%> </div><br /><br /><%%KEEPWHITESPACE%%> <div data-role= "footer" class = "ui-bar" data-position= "fixed" ><br /><%%KEEPWHITESPACE%%> <a href= "#info-dialog" data-icon= "info" data-rel= "dialog" >About</a><br /><%%KEEPWHITESPACE%%> </div><br /></div><br /><br /><div data-role= "page" id= "add" ><br /><%%KEEPWHITESPACE%%> <div data-role= "header" ><br /><%%KEEPWHITESPACE%%> <h1>Memo Add</h1><br /><%%KEEPWHITESPACE%%> <a href= "#add" class = "ui-btn-right" data-icon= "plus" >Add</a><br /><%%KEEPWHITESPACE%%> </div><br /><br /><%%KEEPWHITESPACE%%> <div data-role= "content" ><br /><%%KEEPWHITESPACE%%> <div data-role= "filedcontain" ><br /><%%KEEPWHITESPACE%%> <fieldset data-role= "controlgroup" ><br /><%%KEEPWHITESPACE%%> <legend>Memo</legend><br /><%%KEEPWHITESPACE%%> <input type= "text" id= "add-title" placeholder= "Title" /><br /><%%KEEPWHITESPACE%%> <textarea id= "add-content" placeholder= "Content" ></textarea><br /><%%KEEPWHITESPACE%%> </fieldset><br /><%%KEEPWHITESPACE%%> </div><br /><%%KEEPWHITESPACE%%> </div><br /><br /><%%KEEPWHITESPACE%%> <div data-role= "footer" class = "ui-bar" data-position= "fixed" ><br /><%%KEEPWHITESPACE%%> <a href= "#" data-icon= "check" id= "save-addbtn" >Save</a><br /><%%KEEPWHITESPACE%%> </div><br /></div><br />(略)<br /> |
indexページとaddページの定義は上記のようになります。
jQuery Mobileの各コンポーネントの定義方法については公式ドキュメントを参照してください。
jQuery Mobileを使うことにより、モバイル用のSingle Page Applicationの画面デザインが簡単にできることが分かると思います。
アプリケーションのロジックを書く
アプリケーションには、ボタンがタップされた場合の処理、サーバへのデータ取得、取得したデータの画面への反映など、ロジックを書く必要があります。
jQuery MobileではこれらのロジックはJavaScriptによって記述します。
具体的に今回のMemoアプリケーションのロジック部を見てみましょう。
基本的なスタイルは、ボタンがタップされた、ページ遷移するといったイベントの発生を監視し、イベントリスナーとなるコールバック関数を実装していくことになります。
最初に表示するindexページ(メモの一覧画面)の初期表示ロジックについて見てみましょう。
1 | <br />$( "#index" ).on( 'pagebeforeshow' , function () {<br /><%%KEEPWHITESPACE%%> $.get( '/memo' , function (data) {<br /><%%KEEPWHITESPACE%%> $( "#memolist" ).empty();<br /><%%KEEPWHITESPACE%%> for ( var index = 0; index < data.length; index++) {<br /><%%KEEPWHITESPACE%%> $( "#memolist" ).append( '<li><a href="#view" id="' + data[index]._id +<br /><%%KEEPWHITESPACE%%> '"><h2>' + data[index].title + '</h2><p>' + data[index].content + '</p></a></li>' );<br /><%%KEEPWHITESPACE%%> }<br /><%%KEEPWHITESPACE%%> $( "#memolist" ).listview( 'refresh' );<br /><%%KEEPWHITESPACE%%> });<br />});<br /> |
indexページのpagebeforeshowイベントをjQueryのonメソッドによって監視し、コールバック関数において、
ajaxによるGETリクエストに対するレスポンスからul#memolistのDOMにli要素を追加します。
また、全ての要素を追加した後に、listviewメソッドによってリストの再描画を行います。
ページに対するpagebeforeshowイベントは、ページ遷移時のページ切り替え処理において、
- ページ遷移元のpagebeforehide
- ページ遷移先のpagebeforeshow
- ページ遷移元のpagehide
- ページ遷移先のpageshow
という段階を経る際の2番目の状態を示すイベントです。
jQuery Mobileのイベントについては、こちらを参照してください。
どのタイミングのイベントを監視して処理を実装するかは、複数の実装方法が存在するためバラバラになりがちです。
ロジックを書くために監視するイベントをどれにするかは、あらかじめルールとして決めておくことが必要です。
次は、リストの行をタップし、選択した行のメモを表示するviewページ(参照画面)に遷移する処理について見てみましょう。
index.html
1 | <br /><div data-role= "page" id= "view" ><br /><%%KEEPWHITESPACE%%> <div data-role= "header" ><br /><%%KEEPWHITESPACE%%> <h1>Memo View</h1><br /><%%KEEPWHITESPACE%%> <a href= "#add" class = "ui-btn-right" data-icon= "plus" >Add</a><br /><%%KEEPWHITESPACE%%> </div><br /><br /><%%KEEPWHITESPACE%%> <div data-role= "content" ><br /><%%KEEPWHITESPACE%%> <div data-role= "filedcontain" ><br /><%%KEEPWHITESPACE%%> <h2 id= "view-title" ></h2><br /><%%KEEPWHITESPACE%%> <p id= "view-content" ></p><br /><%%KEEPWHITESPACE%%> </div><br /><%%KEEPWHITESPACE%%> </div><br /><br /><%%KEEPWHITESPACE%%> <div class = "ui-bar" data-position= "fixed" data-role= "footer" data-role=<br /><%%KEEPWHITESPACE%%> "controlgroup" data-type= "horizontal" ><br /><%%KEEPWHITESPACE%%> <a href= "#edit" data-icon= "gear" >Edit</a><br /><%%KEEPWHITESPACE%%> <a href= "#confirm-dialog" data-icon= "delete" data-rel= "dialog" >Delete</a><br /><%%KEEPWHITESPACE%%> </div><br /></div><br /> |
viewページ(参照画面)のHTMLは上記のようになります。対応するJavaScriptは以下のようになります。
app.js
1 | <br /><%%KEEPWHITESPACE%%> var app = app || {};<br /><br /><%%KEEPWHITESPACE%%> // (1)<br /><%%KEEPWHITESPACE%%> $("#memolist").on('tap', 'a', function () {<br /><%%KEEPWHITESPACE%%> app.selectedid = this.id;<br /><%%KEEPWHITESPACE%%> });<br /><br />(略)<br /><br /><%%KEEPWHITESPACE%%> // (2)<br /><%%KEEPWHITESPACE%%> $("#view").on('pagebeforeshow', function () {<br /><%%KEEPWHITESPACE%%> $.get('/memo/' + app.selectedid, function (data) {<br /><%%KEEPWHITESPACE%%> $("#view-title").html(data.title);<br /><%%KEEPWHITESPACE%%> $("#view-content").html(data.content);<br /><%%KEEPWHITESPACE%%> });<br /><%%KEEPWHITESPACE%%> });<br /> |
先ほどの場合と同じく、このviewページのpagebeforeshowイベントを監視し、indexページにて選択した行に対応するデータをajaxによるGETリクエストで取得し、DOMに変更を反映します(2)。
ここでindexページにて選択した行のデータをどのように遷移先のviewページに渡すのかが一つ問題になります。
jQuery Mobileは仕様として、クエリパラメータを使って次のページにパラメータを渡すことができません。
そこで今回はタップした行に対応付けられたidをメモリに保存し(1)、GETリクエストをする際にそのidを利用するようにしています(2)。
行がタップされた際のイベントを監視するには、行(li要素)が動的に追加されるため、その親であるul#memolist要素に対して、子のli要素内のaタグに対するtapイベントを監視するようにしています。
jQuery Mobile単体でのアプリケーション開発を考察する
ロジックを書いたapp.jsは全体としては以下のようになります。
app.js
1 | <br />$( function () {<br /><%%KEEPWHITESPACE%%> "use strict" ;<br /><br /><%%KEEPWHITESPACE%%> var app = app || {};<br /><br /><%%KEEPWHITESPACE%%> $( "#memolist" ).on( 'tap' , 'a' , function () {<br /><%%KEEPWHITESPACE%%> app.selectedid = this .id;<br /><%%KEEPWHITESPACE%%> });<br /><br /><%%KEEPWHITESPACE%%> $( "#index" ).on( 'pagebeforeshow' , function () {<br /><%%KEEPWHITESPACE%%> $.get( '/memo' , function (data) {<br /><%%KEEPWHITESPACE%%> $( "#memolist" ).empty();<br /><%%KEEPWHITESPACE%%> for ( var index = 0; index < data.length; index++) {<br /><%%KEEPWHITESPACE%%> $( "#memolist" ).append( '<li><a href="#view" id="' + data[index]._id +<br /><%%KEEPWHITESPACE%%> '"><h2>' + data[index].title + '</h2><p>' + data[index].content + '</p></a></li>' );<br /><%%KEEPWHITESPACE%%> }<br /><%%KEEPWHITESPACE%%> $( "#memolist" ).listview( 'refresh' );<br /><%%KEEPWHITESPACE%%> });<br /><%%KEEPWHITESPACE%%> });<br /><br /><%%KEEPWHITESPACE%%> $( "#add" ).on( 'pagebeforeshow' , function () {<br /><%%KEEPWHITESPACE%%> $( "#add-title" ).val( '' );<br /><%%KEEPWHITESPACE%%> $( "#add-content" ).val( '' );<br /><%%KEEPWHITESPACE%%> });<br /><br /><%%KEEPWHITESPACE%%> $( "#view" ).on( 'pagebeforeshow' , function () {<br /><%%KEEPWHITESPACE%%> $.get( '/memo/' + app.selectedid, function (data) {<br /><%%KEEPWHITESPACE%%> $( "#view-title" ).html(data.title);<br /><%%KEEPWHITESPACE%%> $( "#view-content" ).html(data.content);<br /><%%KEEPWHITESPACE%%> });<br /><%%KEEPWHITESPACE%%> });<br /><br /><%%KEEPWHITESPACE%%> $( "#edit" ).on( 'pagebeforeshow' , function () {<br /><%%KEEPWHITESPACE%%> $.get( '/memo/' + app.selectedid, function (data) {<br /><%%KEEPWHITESPACE%%> $( "#edit-title" ).val(data.title);<br /><%%KEEPWHITESPACE%%> $( "#edit-content" ).val(data.content);<br /><%%KEEPWHITESPACE%%> });<br /><%%KEEPWHITESPACE%%> });<br /><br /><%%KEEPWHITESPACE%%> $( "#save-addbtn" ).on( 'tap' , function () {<br /><%%KEEPWHITESPACE%%> $.post( '/memo' , {<br /><%%KEEPWHITESPACE%%> title: $( "#add-title" ).val(),<br /><%%KEEPWHITESPACE%%> content: $( "#add-content" ).val()<br /><%%KEEPWHITESPACE%%> }, onSuccess, 'json' );<br /><%%KEEPWHITESPACE%%> });<br /><br /><%%KEEPWHITESPACE%%> $( "#save-editbtn" ).on( 'tap' , function () {<br /><%%KEEPWHITESPACE%%> $.ajax({<br /><%%KEEPWHITESPACE%%> type: 'PUT' ,<br /><%%KEEPWHITESPACE%%> url: '/memo/' + app.selectedid,<br /><%%KEEPWHITESPACE%%> data: {<br /><%%KEEPWHITESPACE%%> title: $( "#edit-title" ).val(),<br /><%%KEEPWHITESPACE%%> content: $( "#edit-content" ).val()<br /><%%KEEPWHITESPACE%%> },<br /><%%KEEPWHITESPACE%%> success: onSuccess,<br /><%%KEEPWHITESPACE%%> dataType: 'json' <br /><%%KEEPWHITESPACE%%> });<br /><%%KEEPWHITESPACE%%> });<br /><br /><%%KEEPWHITESPACE%%> $( "#del-btn" ).on( 'tap' , function () {<br /><%%KEEPWHITESPACE%%> $.ajax({<br /><%%KEEPWHITESPACE%%> type: 'DELETE' ,<br /><%%KEEPWHITESPACE%%> url: '/memo/' + app.selectedid,<br /><%%KEEPWHITESPACE%%> success: onSuccess<br /><%%KEEPWHITESPACE%%> });<br /><%%KEEPWHITESPACE%%> });<br /><br /><%%KEEPWHITESPACE%%> function onSuccess(data) {<br /><%%KEEPWHITESPACE%%> $( "#message" ).html(data.message);<br /><%%KEEPWHITESPACE%%> $.mobile.changePage( '#msg-dialog' , {<br /><%%KEEPWHITESPACE%%> transition: 'slidedown' ,<br /><%%KEEPWHITESPACE%%> role: 'dialog' <br /><%%KEEPWHITESPACE%%> });<br /><%%KEEPWHITESPACE%%> }<br /><br />}());<br /> |
イベントに対して、コールバック関数を記述するという簡潔な書き方になっています。
一見して簡単にコードを書くことができていますが、縦方向にダラダラと処理を記述しており、アプリケーションにアーキテクチャと言えるものがありません。
今回レベルのアプリケーションではあまり問題になりませんが、本格的なアプリケーションの開発を想定した場合、規模が大きくなるにつれて問題となります。
また、ページ遷移する際にクエリパラメータを使って次のページにパラメータを渡すことができない、という点も問題です。
次回
今回でjQuery Mobileを使ってモバイルWebアプリケーションが簡単に開発できることが分かったと思います。
しかし、アーキテクチャ面や細かい制御をしたいと考えた場合に課題があることも見えてきました。
そこで、次回はjQuery MobileとBackbone.jsを併用し、jQuery MobileはViewフレームワークとしてのみ利用し、ルーティングおよびアプリケーションロジックをBackbone.jsを利用して開発するという内容を取り上げます。
執筆者プロフィール 村田賢一郎
Acroquest Technology 株式会社勤務。Javaによるミッションクリティカルな集中監視システムのフレームワーク開発、およびライフラインを支えるシステム開発に携わる。非同期処理、メッセージング、HAなどが本業である傍ら、Webによる新しいUI表現、開発手法に興味があり、あれこれ模索している。