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

Youtubeでもレブル250/グラディウス400/GN125の情報を発信してます! (登録してもらえると超喜びます!!)

はじめに

みなさん、こんにちは!

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

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

ons-navigatorについて

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

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

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

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

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

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

ここから実装です

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

index.html

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

【www/index.html】

[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>

[/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】

[javascript] {
‘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("エラーが発生しました");
});
}
});
}
[/javascript]

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

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

[javascript] myNav.getCurrentPage().options
[/javascript]

のように、ナビゲーションオブジェクトの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実装ができますよ!というお話でした。

今回は以上です!

Youtubeでもレブル250/グラディウス400/GN125の情報を発信してます! (登録してもらえると超喜びます!!)
最新情報をチェックしよう!