hifiveを使ってYouTube検索アプリを作ってみよう(前編)
hifiveを使って実際にWebアプリを作ってみるチュートリアルになります。徐々に作り込んでいきますので、hifiveがどのように動いているかも分かっていただけるかと思います。まず今回は、hifiveの基本的な仕組みを使って、YouTubeを検索して動画を再生できる、そんなWebアプリケーションを作ってみましょう。
完成図
まずは完成図を見てみましょう。

Webページが表示されたら適当なワードで検索してみましょう。今回はhtml5としています。

検索結果が表示されました。YouTubeのWeb APIを使ってデータを取得し、それをリスト表示しています。

再生ボタンを押せば動画が再生されます。なお動画プレーヤについては表示されている部分だけレンダリングしていますので、大量の動画プレーヤがあっても素早く結果が表示できています。

ページの一番下まで移動すると検索結果の追加読み込みが行われます。都度インジケータが表示されるので処理状態が分かりやすいです。
ではここから実際に作っていきます。
ベースのHTMLを作る
まずはベースになるHTMLを作ります。 index.html とします。全体像としては以下のようになります。
<!doctype html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta charset="UTF-8">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Cache-Control" content="no-cache">
<meta http-equiv="Expires" content="-1">
<meta name="viewport" content="width=device-width">
<!-- 必要なライブラリを適時読み込んでください -->
<title>hifive YouTube検索サンプル</title>
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css" rel="stylesheet">
<link href="//code.htmlhifive.com/h5.css" rel="stylesheet">
</head>
<body>
<!-- header -->
<div class="navbar navbar-inverse">
<div class="container">
<p class="navbar-brand" >hifive YouTube検索サンプル</p>
</div>
</div>
<!-- container -->
<div id="container" class="container theme-showcase">
<!-- 検索窓 -->
<div class="page-title jumbotron">
<p>キーワードを入力し検索ボタンを押下すると、関連する動画を表示します。</p>
<form id="search" class="search-form corner-all search">
<div class="search search-form-content">
<input type="text" id="keyword" class="corner-all form-control search-keyword" placeholder="キーワード"/>
</div>
<div class="search-form-content">
<input type="submit" class="corner-all btn btn-info" value="検索"/>
</div>
</form>
</div>
<!-- 検索結果 -->
<div class="page-header">
<h3 class="result-message">検索結果: <span id="totalCount">0</span>件</h3>
<ul id="result" class="result-lists list-group"></ul>
</div>
</div>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="//code.htmlhifive.com/ejs-h5mod.js"></script>
<script src="//code.htmlhifive.com/h5.js"></script>
</body>
</html>
Bootstrapを使っていますので、そのためのスタイルシートとhifiveのスタイルシートを読み込んでいます。HTMLのデザインはBootstrapに則った形になっています。また、JavaScriptのライブラリ(jQuery、hifive)もHTML下部で読み込んでいます。こちらを表示させてみましょう。
JavaScript、スタイルシートの表示を // からはじめていますので何らかのHTTPサーバが必要です。例えばPythonが入っているコンピュータであれば、HTMLファイルの置いたディレクトリで、
$ python -m SimpleHTTPServer
とすることで http://localhost:8000/ でアクセスできるようになります。
コントローラを実装する
次に検索ボタンを押した時の処理を実装していきます。まずはJavaScriptファイルを追加します。パスは自由ですが今回は javascripts/controllers というフォルダを作成し、その中に youtube-controller.js というファイルを作成しました。そのファイルをHTML側で読み込みます。
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="//code.htmlhifive.com/ejs-h5mod.js"></script>
<script src="//code.htmlhifive.com/h5.js"></script>
<script src="javascripts/controllers/youtube-controller.js"></script> <!-- ← 追加 -->
youtube-controller.jsの中身は以下のようになります。
(function($) {
var youtubeController = {
__name: 'youtube.sample.YoutubeController',
_keyword: '',
'#search submit': function(context, $el) {
context.event.preventDefault();
alert(context);
}
};
h5.core.expose(youtubeController);
})(jQuery);
$(function(){
h5.core.controller('#container', youtube.sample.YoutubeController);
});
__nameは適当ですが、〜Controller となっている必要があります。jQueryのreadyになったタイミングでhifiveのコントローラとして定義されます。
h5.core.expose(youtubeController);
の処理はコントローラをグローバルに公開するための処理になります。 __name で定義した名前でアクセスできるようになります。今回は youtube.sample.YoutubeController という定義なので、
youtube.sample.YoutubeController
// または
window.youtube.sample.YoutubeController
でアクセスができるようになります。
イベント処理
HTML側の #search を押したタイミングで定義したイベントが呼ばれるようになっています。先ほどの内容で実行すると次のようになります。

確かにアラートが表示されました。
alert(context) を console.log(context) にするとデベロッパーツールには次のように出力されます。
z
controller: Ea
#search submit: function (){var j=this;return La.call(j,{target:j,func:a,funcName:b,args:arguments,proceed:function(){return a.apply(j,this.args)}})}
__controllerContext: Object
__name: "youtube.sample.YoutubeController"
__templates: null
_keyword: ""
initPromise: Object
isInit: true
isPostInit: true
isReady: true
log: l
parentController: null
postInitPromise: Object
preInitPromise: Object
readyPromise: Object
rootController: Ea
rootElement: div#container.container.theme-showcase
view: Xa
event: m.Event
:
なので context.event に イベントオブジェクトが入っているのが分かります。同様に $el は #search のDOMがjQueryのオブジェクトとして入ってきます。そのため、検索キーワードが取得したい場合は、
$el.find("#keyword").val()
とすればテキストが取得できます。今回はコントローラの _keywordに検索キーワードを入れておくため、次のようになります。
'#search submit': function(context, $el) {
context.event.preventDefault();
this._keyword = $el.find("#keyword").val();
}
さらに入力が何もなかった場合はその後の処理自体を行う必要がありませんので次のような条件を追加しましょう。
'#search submit': function(context, $el) {
context.event.preventDefault();
this._keyword = $el.find("#keyword").val()
if (!this._keyword || $.trim(this._keyword).length === 0) {
return;
}
}
これでコントローラの基本的処理の完了です。
ロジック処理を実装する
今回のロジック処理とは、YouTubeからの動画検索になります。まず、javascripts/logics/youtube-logic.js というファイルを作成します。まずは以下のような内容とします。
(function($) {
var URL = 'http://gdata.youtube.com/feeds/api/videos';
var youtubeLogic = {
__name: 'youtube.sample.YoutubeLogic',
search: function(keyword, startIndex, maxResults) {
console.log("Search youtube")
}
};
//ロジックを単体で実行できるように公開する
h5.core.expose(youtubeLogic);
})(jQuery);
先ほどのコントローラと大きくは変わっていないかと思います。
ロジックを単体で実行する
ロジックはUIと切り離して作ることで、その部分だけをテストしたり繰り返し実行ができるようになります。今回もまずはロジック単体で実行してみましょう。まずはHTML側でロジックのJavaScriptを読み込みます。後ほど、コントローラでロジックを使いますのでロジックを先に読み込んでください。
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="//code.htmlhifive.com/ejs-h5mod.js"></script>
<script src="//code.htmlhifive.com/h5.js"></script>
<script src="javascripts/logics/youtube-logic.js"></script> <!-- ← 追加 -->
<script src="javascripts/controllers/youtube-controller.js"></script>
HTMLを再読み込みして、さらにデベロッパーツールを開きます。コンソールで実行してみます。
> youtube.sample.YoutubeLogic
Object {__name: "youtube.sample.YoutubeLogic", search: function}
このようにロジックに直接アクセスできます。ではsearchメソッドを実行してみましょう。
> youtube.sample.YoutubeLogic.search("HTML5", 1, 10);
Search youtube
undefined
このように表示されるかと思います。
YouTubeの検索処理を追加する
では次にロジックにYouTubeの検索処理を追加してみましょう。YouTubeのWeb APIアクセスは次のようになります。
http://gdata.youtube.com/feeds/api/videos?q=html5&start-index=11&max-results=10&v=2&alt=json-in-script&callback=test

パラメータは以下です。
- q : 検索キーワード
- start-index : 検索開始インデックス(1始まり)
- max-results : 何件取得するか(結果の最大件数)
- alt : レスポンスのフォーマット(今回は’json-in-script’を指定)
- v : バージョン。2固定。
JSONPを使う場合、callbackを別途指定する必要があります。jQueryの場合、デフォルトでcallbackという名称を使いますので指定は不要です。
ではロジックのsearchメソッドを書き直してみましょう。
search: function(keyword, startIndex, maxResults) {
var promise = h5.ajax(URL, {
dataType: 'jsonp',
data: {
'q': keyword,
'v': 2,
'max-results': maxResults,
'alt': 'json-in-script',
'start-index': startIndex
},
cache: true
});
// Promiseオブジェクト(jqXHRオブジェクト)を返す
return promise;
}
コツとしてはpromiseを返しているところでしょうか。これでdoneやerrorでメソッドを引き継げるようになります。
この状態でブラウザを再読み込みして、コンソールで実行してみましょう。
> youtube.sample.YoutubeLogic.search("HTML5", 1, 10).done(function(data){console.log(data);});
k {readyState: 1, getResponseHeader: function, getAllResponseHeaders: function, setRequestHeader: function, overrideMimeType: function…}
Object {version: "1.0", encoding: "UTF-8", feed: Object}
返ってくる Object の内容は次のようになります。
Object {version: "1.0", encoding: "UTF-8", feed: Object}
encoding: "UTF-8"
feed: Object
author: Array[1]
category: Array[1]
entry: Array[10]
gd$etag: "W/"CEQDQXgzfip7I2A9XRZUEk0.""
generator: Object
id: Object
link: Array[6]
logo: Object
openSearch$itemsPerPage: Object
openSearch$startIndex: Object
openSearch$totalResults: Object
title: Object
updated: Object
xmlns: "http://www.w3.org/2005/Atom"
xmlns$gd: "http://schemas.google.com/g/2005"
xmlns$georss: "http://www.georss.org/georss"
xmlns$gml: "http://www.opengis.net/gml"
xmlns$media: "http://search.yahoo.com/mrss/"
xmlns$openSearch: "http://a9.com/-/spec/opensearch/1.1/"
xmlns$yt: "http://gdata.youtube.com/schemas/2007"
__proto__: Object
version: "1.0"
data.feed.entry が今回使いたい検索結果の一覧になります。
コントローラに組み込む
ではこのロジックをコントローラに組み込んでみましょう。
__name: 'youtube.sample.YoutubeController',
_youtubeLogic: youtube.sample.YoutubeLogic, // ← 追加
_keyword: '',
をコントローラに追加します。そしてsearchメソッドを追加します。
_search: function(keyword, index) {
//検索ロジックが返すpromiseオブジェクトを変数に保持する
var promise = this._youtubeLogic.search(keyword, index, NUMBER_TO_LOAD_AT_ONCE);
//検索ロジック完了
promise.done(function(data) {
//総件数を表示
alert(data.feed.openSearch$totalResults.$t);
});
// promiseオブジェクトを返す
return promise;
},
NUMBER_TO_LOAD_AT_ONCEは別途、
var NUMBER_TO_LOAD_AT_ONCE = 6;
として定義してあります。
続いてイベント側のメソッドも修正します。
'#search submit': function(context, $el) {
context.event.preventDefault();
this._keyword = $el.find("#keyword").val();
this._search(this._keyword, 1);
},
この状態で実行すると、トータルの検索結果件数がアラート表示されます。

今回はここまでの実装となります。後編では表示周りを処理していきます。
今回までのコードはGitHubにアップロードしてあります。うまく動かない場合などはこちらでご確認ください。
コメントは受け付けていません。