jQuery Mobile + MTによるAjax検索

ちまちまと遊びで作っているMTベースのjQuery MobileサイトでAjaxを利用した検索ページを作ったのでメモ。

利用シチュエーション

  • 項目選択を検索結果から行うため、呼び出した検索ページでの検索実施後に元ページに戻るというUターン型(?)画面遷移
  • 検索ページでの画面遷移を防ぐためAjaxでmt-search.cgiを呼び出し、項目選択で元画面に戻る

前提条件

  • データは同じカテゴリがまとまって取得可能なようにソートされているか、そのように取得できるよう検索時のソートパラメータを指定する(カテゴリでグルーピングするため)
  • 同じファイルに複数のページが存在する複数ページテンプレート形式
  • 検索エラー(条件未入力、未発見)についてはmt-search.cgiと検索テンプレートにお任せ

利用技術

  • mt-search.cgi
  • 代替検索テンプレート
  • jQuery Mobile

画面定義

  • 検索ボックス
  • 検索ボタン
  • キャンセルボタン
  • リストビュー

イベントへの処理のマッピング

pagebeforeshow

  • 検索ボックスクリア

pageinit

  • 検索ボタンへの処理マッピング(処理内容については後述)

pagehide

  • リストビュークリア

検索ボタンにマッピングする処理

  1. 検索ボックスから値取得
  2. Ajaxでmt-search.cgi呼び出し

Ajax結果処理

成功時処理

  1. リストビュークリア
  2. 取得データのリストビューへの追加
  3. 各リスト内のアンカータグに対するリスト選択時処理のマッピング(処理内容については後述)
  4. リストビューのrefresh

失敗時処理

  1. 適切なエラー処理

各リストのアンカータグにマッピングする処理

  1. アンカータグ内テキスト取得
  2. 設定先フォームへの選択値設定処理など
  3. キャンセルボタンのクリック(前画面への遷移)

コードイメージ

スタイルシート

    .ui-li-desc{
        white-space: normal;
    }

上記の定義は、検索エラー時に<li>項目に表示するエラーテキストが折り返されるようにする事が目的であるため、別のエラー通知方式をとる場合は不要。

HTML/JavaScript記述本体

<div data-role="page" id="base_area_search">
        <div data-role="header" data-position="fixed">
                <h1>エリア選択</h1>
        </div><!-- /header -->

        <div data-role="content">
        <form action="#" method="get">
                <input type="search" name="search" id="input_area_search" value="" placeholder="検索条件を入力" />
                <input type="button" name="name" id="area_list_search" value="検索" data-theme="b">
                <a href="#" data-role="button" id="area_list_cancel" data-rel="back">キャンセル</a>
        </form>
        <ul data-role="listview"  id="list_disp" data-inset="true" >
        </ul>
        
        <script>
        $("#base_area_search").live('pagebeforeshow',function(event) {
            $('#input_area_search').val('');
        });
        $("#base_area_search").live('pageinit',function(event) {
            $("#area_list_search").bind("tap",function() {
                var ul = $('#list_disp');
                var input_area_search = $('#input_area_search').val();
                $.ajax({
                    url: '<$mt:CGIPath$><$mt:SearchScript$>',
                    type: 'GET',
                    timeout: 10000,
                    data: {
                        search: input_area_search,
                        IncludeBlogs: <$mt:BlogID$>,
                        limit: 9999,
                        SearchResultDisplay: 'ascend',
                        Template: 'altsearch'
                    },
                    dataType: 'html',
                    success: function(data) {
                        ul.empty();
                        $(data).appendTo(ul);
                        $('#list_disp li a').each(function() {
                            $(this).bind("tap",function() {
                                // ここは必要な値設定処理などを入れる
                                $('#xxxxxxx').val($(this).text());
                                $('#area_list_cancel').click();
                            });
                        });
                        ul.listview('refresh');
                    },
                    error: function(data) {
                        // エラー処理はもう少しましな処理に
                        alert("error !");
                    }
                });
            });
        });
        $("#base_area_search").live('pagehide',function(event) {
            $('#list_disp').empty().listview('refresh');
        });
        </script>

        </div><!-- /content -->
</div><!-- /page -->

代替テンプレート(altsearch.tmpl)

<mt:SetVar name="current_category" value="">
<mt:SearchResults>
      <mt:SetVarBlock name="new_category">
      <mt:EntryPrimaryCategory><$mt:CategoryLabel$></mt:EntryPrimaryCategory>
      </mt:SetVarBlock>
      <mt:If name="current_category" ne="$new_category">
      <li data-role="list-divider" data-mini="true">
      <mt:EntryPrimaryCategory><$mt:CategoryLabel$></mt:EntryPrimaryCategory></li>
      </mt:if>
      <li data-icon="plus"><a href="#"><$mt:EntryTitle$></a></li>
      <mt:Var name="current_category" value="$new_category">
</mt:SearchResults>

<mt:NoSearchResults>
      <li data-role="list-divider" data-mini="true">検索エラー</li>
      <li><p>「<$mt:SearchString$>」と一致する結果は見つかりませんでした。</p></li>
</mt:NoSearchResults>

<mt:NoSearch>
      <li data-role="list-divider" data-mini="true">検索エラー</li>
      <li>
        <p>すべての単語が順序に関係なく検索されます。フレーズで検索したいときは引用符で囲んでください。</p>
        <pre><code>"movable type"</code></pre>
        <p>AND、OR、NOTを入れることで論理検索を行うこともできます。</p>
        <pre><code>個人 OR 出版</code></pre>
        <pre><code>個人 NOT 出版</code></pre>
      </li>
</mt:NoSearch>

エラー処理は元のテンプレートからほぼ流用。

mt-config.cgi

SearchAltTemplate altsearch altsearch.tmpl

動作イメージ

実際に動かした画面イメージは以下の通り。検索フォームを開閉パネル(?)の中に入れているため、見栄えが上記コードの結果と少し異なる。

検索画面

kensakumae.jpg

検索結果表示

normalresult.jpg

検索文字列未入力エラー

noinput.jpg

検索結果未発見エラー

noresult.jpg

いまいちな点

  • JavaScriptを長々と書くのは美しくない(足していったら長くなったとは言え)
  • mt-search.cgiを非同期呼び出し中にキャンセルボタンを押されたりした場合などへの配慮が足りない

参考ページ