【Phonegap/Cordova + Onsen UI 05】ナビゲーションでページ遷移!再帰的キーワードサジェストアプリを作る

はじめに

みなさん、こんにちは!

今回は、OnsenUIがOnsenたる理由である、SPA(Single Page Application)のアプリを作りましょう。
SPA = スパ = Onsen ということらしいですね。はい。
OnsenUIでSPAしなかった今までが逆に宝の持ち腐れだったということですね。はい。

単にページ遷移しておしまい、というのはあんまりなので、今回も何かのアプリを作りつつやっていきましょう。
今回は、Amazon Suggest APIを使用して、
とあるキーワードを投げる⇒そのキーワードの関連キー一覧を取得⇒さらにそのキーワードを選択して関連キー一覧を取得
みたいなアプリを作ることにしましょう。

ons-navigatorについて

OnsenUIには、いくつかのページ管理方式があり、それらを組み合わせて使用することもできます。
が、今回はナビゲーション型のページ管理にしましょう。
これは、ルート(親)になる画面から、子の画面に遷移していくようなパターンのアプリに向くページ管理方式です。
割と多くのアプリに当てはまると思います。

使用するためには、HTMLにons-navigator要素を配置します。

<ons-navigator var="myNav" page="main.html"></ons-navigator>

var属性は、このディレクティブに限らず、OnsenUIで共通的に使用できます。
Javascriptのコード内で参照できる変数名を与えることができます。

page属性は、アプリが起動した時に、どのテンプレートページを表示するかを指定できます。

また、OnsenUIでは、テンプレートをサポートしています。
ons-templateタグでHTMLのテンプレートを記述すれば、後にテンプレートを使用できます。このテンプレートにidを与えることで、参照が可能です。
テンプレートを使用しないで、htmlファイル自体を分けることももちろん可能です。

ここから実装です

さて。
それでは、実装を始めていきましょう。
今回ももちろん、OnsenとAngularを使用するので、前回と同様にライブラリを配置しておいてください。

index.html

まずは、HTMLファイルを編集していきます。

【www/index.html】

<html>

  <head>
  <meta charset="utf-8" />
  <meta name="format-detection" content="telephone=no" />
  <meta name="msapplication-tap-highlight" content="no" />
  <!-- WARNING: for iOS 7, remove the width=device-width and height=device-height attributes. See https://issues.apache.org/jira/browse/CB-4323 -->
  <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi" />

    <link rel="stylesheet" href="lib/onsen/css/onsenui.css" />
    <link rel="stylesheet" href="lib/onsen/css/onsen-css-components-blue-basic-theme.css" />

    <script src="lib/onsen/js/angular/angular.js"></script>
    <script src="lib/onsen/js/onsenui.js"></script>

    <script src="js/mainController.js"></script>

  <style>
   ons-page{
    font-family: "ヒラギノ角ゴシック Pro", "Hiragino Kaku Gothic Pro", 'メイリオ' , Meiryo , Osaka, "MS Pゴシック", "MS PGothic", sans-serif !important;
   }
   #input_searchkey{
    width: 100%;
    height: 48px;
    font-size: 42px;
    margin-top: 4px;
    margin-bottom: 4px;
   }
   .delete-button{
    background-color: rgb(240, 80, 80);
   }
   .op-buttons ons-button{
    width: 100%;
    text-align: center;
   }
   .result{
    font-size: 32px;
   }
   .result > p{
    margin: 4px;
   }
  </style>

  <script type="text/javascript" src="cordova.js"></script>

  <title>Amazon Suggestion</title>

  </head>

  <body>
    
    <ons-navigator var="myNav" page="main.html"></ons-navigator>

    <ons-template id="main.html">
      <ons-page ng-controller="MainPageController">

        <ons-toolbar>
          <div class="center">Amazon Suggestion</div>
        </ons-toolbar>

        <ons-row>
          <input id="input_searchkey" type="text" ng-model="searchkey" />
        </ons-row>

        <ons-row>
          <ons-button modifier="large" ng-click="searchSuggestion()"><ons-icon icon="search"></ons-icon>search</ons-button>
        </ons-row>

      </ons-page>
    </ons-template>

    <ons-template id="result.html">
      <ons-page ng-controller="resultPageController">
          <ons-toolbar>
            <div class="left"><ons-back-button>Back</ons-back-button></div>
            <div class="center">Amazon Suggestion</div>
            <div class="right"><ons-button modifier="quiet" ng-click="move2top()">Home</ons-button></div>
          </ons-toolbar>

          <div>
            <ons-list>
              <ons-list-item ng-repeat="item in keywords" ng-click="recursiveSearch($index)">
                {{item.keyword}}
              </ons-list-item>
            </ons-list>
          </div>

      </ons-page>
    </ons-template>

  </body>
</html>

52行目で今回のテーマであるons-navigatorを宣言しています。
var属性にはmyNavを与えています。これでjs側でmyNavとして参照することができます。
page属性には、main.htmlを与えています。テンプレートにmain.htmlというidを与えておくか、main.htmlというhtmlファイルを同階層に用意する必要がありますが、今回は前者でいきましょう。

54行目でtemplateを宣言しています。ここから70行目の/templateまでの間に記述された要素をid名main.htmlというテンプレートとして呼び出すことができます。

72行目から89行目まで、また別のテンプレートです。
こちらは、キーワード検索結果表示用のテンプレートとして使用します。
75行目で、ons-back-buttonを使用していますが、これは、ボタンが押下されるとnavigationの持つページスタックから自動で1ページ分ポップしてくれる便利なディレクティブです。
77行目は、キーワードを掘りすぎるとトップ画面に戻るのが大変そうだったので、Homeボタンを用意しています。クリックイベントをアタッチしていますので、コントローラ側でイベントを定義します。

mainController.js

今度は、コントローラ側です。

【www/js/mainController.js】

{
  'use strict';
  var module = ons.bootstrap("ONSEN_03_APP", ['onsen']);

  module.controller("MainPageController", function ($scope) {

  	$scope.searchkey = "";

    // 住所検索を行う
    $scope.searchSuggestion = function(){
      if(!!$scope.searchkey){
        myNav.pushPage("result.html", { keyword: $scope.searchkey });
      }
      else{
        alert("検索キーを入力してください...");
      }
    }

  });

  module.controller("resultPageController", function ($scope, $http, GetAmazonSuggestionsService){
    $scope.keywords = [];

    var nav_options = myNav.getCurrentPage().options;

    GetAmazonSuggestionsService.getsuggestions(nav_options.keyword, function(res){
      $scope.keywords = res.results;
    });

    $scope.recursiveSearch = function(index){
      myNav.pushPage("result.html", {
        keyword: $scope.keywords[index].keyword
      });
    };

    $scope.move2top = function(){
      myNav.resetToPage("main.html");
    }
  });

  module.service("GetAmazonSuggestionsService", function ($http){
    this.getsuggestions = function(searchkey, callback){
      var params = {
        q: searchkey,
        method: "completion",
        mkt: 6,
        "search-alias": "aps",
        callback: "JSON_CALLBACK"
      };

      // angularのhttpサービスでjsonp
      $http.jsonp(
        "http://completion.amazon.co.jp/search/complete",
        {
          params: params
        }
      ).success(function(data){
        console.log(data);

        var search_title = data[0];
        var search_result_arr = (function(items){

          var res = [];

          if(!items){ return res; }

          for(var i = 0; i < items.length; i++){ res.push({keyword: items[i]}); }

          return res;
        })(data[1]);

        callback({
          searchkey: search_title,
          results: search_result_arr
        });

      }).error(function(){
        alert("エラーが発生しました");
      });
    }
  });
}

12行目で、html側で定義したNavigationオブジェクトであるmyNavを使用しています。
次ページに遷移するためには、pushPageメソッドを使用します。
第1引数には遷移先のページ名(htmlファイル又はテンプレート)、
第2引数には遷移先に渡すパラメータを指定できます。
今回の場合は、次ページに検索キーワードを渡して、検索⇒表示をしてもらう方向でいきたいので、検索キーをパラメータとして渡すようにしています。

24行目で、遷移先側ページのコントローラにて、受け渡されたパラメータを取得しています。
前画面からのパラメータの取得は、

myNav.getCurrentPage().options

のように、ナビゲーションオブジェクトのgetCurrentPageメソッドの持つoptionsプロパティにアクセスすればokです。

26行目で、前画面からのキーワードを取得するサービスのメソッドを呼び出しています。
このサービスは41行目以降で定義しています。

30行目~34行目が、検索結果一覧のとあるキーワードを押下した時のイベントハンドラです。
再び自分自身のページに遷移(再帰)することで、キーワードの掘り下げを行っています。

36行目が、html側で定義したHomeボタンのハンドラです。
ナビゲーションオブジェクトのresetToPageメソッドで、引数にとったページに遷移し、ページスタックをリセットしています。

41行目~81行目が、Amazon Suggest APIをコールして、戻り値を組み立てるサービスの定義です。
与えるパラメータのうち、qは検索文字列、他のパラメータは…良く分かっていません…ネット情報です。こんな感じで与えると、いい感じのキーワード提案をしてくれます。

あとは、前回の住所検索APIコールと同様ですね。Angularのhttpサービスでajaxして、コールバックで受け取った値を編集して…のようなことを行っています。はい。

動作確認

さて!

こんな感じのコーディングをすると、実機では下記のような動きのアプリができているはずです。

初期画面はons-navigatorで指定したmain.htmlが表示されます。ここで、検索キーワードを入力してSearchボタンを押下します。
Screenshot_2016-09-02-23-44-03

すると、myNav.pushPageで次画面に遷移し、キーワード検索結果が表示されます。
Screenshot_2016-09-02-23-44-13

さらに、この検索結果をタップすると(この画面だとGN125 シート をタップ)、さらにキーワードを掘り下げます。
Screenshot_2016-09-02-23-44-20

こんな感じですね!

OnsenUIのons-navigatorを使用すると、簡単に親子関係のSPA実装ができますよ!というお話でした。

今回は以上です!

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です