AngularJS + OnsenUI + YoutubeAPIでYoutube検索結果取得アプリ


<img width="1276" alt="スクリーンショット 2015-11-23 14.01.46.png" src="https://qiita-image-store.s3.amazonaws.com/0/96323/0d09d8a4-17a1-25ab-55c6-372c26d3afa7.png">

androidでエラーが出てます...

#開発環境構築

まずはじめにcordova の開発環境を構築します。
monacaでクラウドでやるのもいいのですが今回はlocalで環境を構築します。

node, npmの開発環境は整っていることを前提に進めていきます。

まだの人はnodebrewでnodeの開発環境の構築法を書いてくださってるかたがいるのでこちらを参考にどうぞ


http://qiita.com/sinmetal/items/154e81823f386279b33c



開発環境
node -v v4.1.2
npm -v 2.14.4


##cordova環境構築

```
$sudo npm install -g cordova
//cordovaのバーション
$cordova -v 5.4.0
//確認できないかたはsudo をつけてみてください。
```

###cordovaの実行権限をrootユーザから変更する

僕の場合だとこのままcordovaのプロジェクトを作ろうとすると
毎回sudoをつけてcorodovaコマンドを実行
iOSやandroidで実行する場合にemulateできないといった問題がありました。

なのであらかじめ対策として実行権限とその関連ファイルをrootから外しておきます。

```
$chown -R {terminalにログインしているuser} {対処のファイルとディレクトリ}
//terminalにログインしているuserは次のコマンドで確認できます。
$who am i
{terminalにログインしているuser} ttys000  Nov 23 14:35
```

###cordova プロジェクト作成とemulateの確認
cordovaプロジェクトの作成をします。

```
 $cordova create hello com.example.hello HelloWorld -d
```

この意味は
corodovaのプロジェクトをhelloという名前、com.example.hello というアプリの識別子、HelloWorldというプロジェクト名で作成するという意味です。
-dは作成過程をはかせるオプションです。

この時点ですでに雛形はできたので実際にemulateします。
まずは実行するプラットフォームを追加します。

```
$cd hello
$cordova platform add ios
$cordova platform add android
```

次に実際にemulaterを起動して実行します。

```
$cordova emulate ios
$cordova emulate android 
```

これでemulateされたらokです。

ここで一から作るのは大変なのですでに出来上がっている公式サイトからtemplateを取得してきます。


http://onsen.io/download.html#download-templates


＊このページはブラウザを大きめに開いて置かないと出てきません。

ダウンロードしたら先ほどの手順と同様にemulateまでやってみてください。

今回はtab-barのtemplateを使用します。

http://onsen.io/project-templates/gen/onsenui-tab-bar.zip

これでclientのデザイン面は完成です。笑



#youtubeAPIのキーを取得


google developer's consoleに登録してyoutubeAPIを取得してきます。

認証情報はサーバーキーとしてください。

登録した後にまた認証情報を開くとキー情報があると思うのでメモしておきます。


#Node Expressでサーバーサイド実装

今回はExpressでyoutubeから検索情報を取得してきます。

なのでcordovaをwebで表示させる際にはcorssドメインとなってしまうので
戦略としてはjsonpとしてデータを取得します。



```javascript:youtube.js
var express = require('express');
var router  = express.Router();
var Youtube = require('youtube-node');
var youtube = new Youtube();
youtube.setKey('{Youtube api Key}');
router.get('/youtube', function(req, res, next){
  var limit = 10;
  var keyword = 'かわいい';
  youtube.search(keyword, limit, function(err,result){
    res.type('application/json');
    res.jsonp(result);
    //console.log(result.items);
  });
});
module.exports = router;

```



ほぼexpressのテンプレートと同じです。



```javascript:app.js
var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var youtube = require('./routes/youtube');

var app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
app.set('jsonp callback name', 'callback');
// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use('/', routes);
app.get('/youtube', youtube);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
  var err = new Error('Not Found');
  err.status = 404;
  next(err);
});

// error handlers

// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
  app.use(function(err, req, res, next) {
    res.status(err.status || 500);
    res.render('error', {
      message: err.message,
      error: err
    });
  });
}

// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
  res.status(err.status || 500);
  res.render('error', {
    message: err.message,
    error: {}
  });
});


module.exports = app;
```

これでサーバサイドは終わりです。

#Expressにデータをリクエストする

これは結構大変でした。まずajaxでデータを取得してやるぜと思ってjqueryを使って、

```
$.ajax
```

してたのですが、angularにはちゃんとajax用のサービスがあったので変更しました。

ちなみにこれが完成系のコード

```
    <script>
        angular.module('app', ['onsen'])
        .factory('youtubeResult', function($http){
          return {

                    searchYoutube : function(){
                      return $http.jsonp('http://localhost:3002/youtube?callback=JSON_CALLBACK') //1
                        .success(function(result){
                            return result;
                          });
                    }
                  }
          }).controller('AppController',['$scope','youtubeResult', function ($scope, youtubeResult) {
            youtubeResult.searchYoutube().then(function(res){
              console.log(res.data.items);
              $scope.result = res.data.items;
            });
        }]);
    </script>

```

ちなみに1とした箇所を安易に

```
$http.jsonp('http://localhost:3002/youtube')
```

とすると

```
because its MIME type ('application/json') is not executable, and strict MIME type checking is enabled.
```

怒られます。コードはこんな感じ

```html:index.html
    <script>
        angular.module('app', ['onsen'])
        .factory('youtubeResult', function($http){
          return {
            searchYoutube : function(){
              return $http.jsonp('http://localhost:3002/youtube')
                .success(function(result){
                  console.log(JSON.stringify(data));
                    return data;
                  });
            }
          }
          }).controller('AppController',['$scope','youtubeResult', function ($scope, youtubeResult) {
            youtubeResult.searchYoutube().then(function(res){
              console.log(JSON.stringify(res));
              $scope.result = res.items;
            });
        }]);
    </script>
```

そしてjsonpだからserverサイドでつけたcallbackの名前にすればいけるんじゃねと思ってやります。

```html:index.html
    <script>
        angular.module('app', ['onsen'])
        .factory('youtubeResult', function($http){
          return {

                    searchYoutube : function(){
                      return $http.jsonp('http://localhost:3002/youtube?callback=callback')
                        .success(function(result){
                          console.log(result);
                            return result;
                          });
                    }
                  }
          }).controller('AppController',['$scope','youtubeResult', function ($scope, youtubeResult) {
            youtubeResult.searchYoutube().then(function(res){
              $scope.result = res.items;
            });
        }]);
    </script>
```

結果は確かに帰ってくるんですが??な感じになってしまいました。
(ここメモっておくの忘れました)

コマンドで確かめるとなんか成功しています。....

```
$curl -i -X GET http://localhost:3002/youtube
HTTP/1.1 200 OK
X-Powered-By: Express
X-Content-Type-Options: nosniff
Content-Type: application/json; charset=utf-8
Content-Length: 7956
ETag: W/"1f14-lQsR/MS3nAElWULed7EeGQ"
Date: Mon, 23 Nov 2015 03:52:20 GMT
Connection: keep-alive

{"kind":"youtube#searchListResponse","etag":"\"mPrpS7Nrk6Ggi_P7VJ8-KsEOiIw/N-YUGKCS9F2QV-2KXoFHYgtKKdc\"","nextPageToken":"CAoQAA","pageInfo":{"totalResults":675639,"resultsPerPage":10},"items":[{"kind":"youtube#searchResult","etag":"\"mPrpS7Nrk6Ggi_P7VJ8-KsEOiIw/MMLdMrfpRdZG9zJxGH2EX1WTgWk\""
```

最終的にangularだとcallback=?JSON_CALLBACKとつけないとダメっぽいいです。
するともとのjsonがちょっと加工された形で帰ってきます。
具体的には{中身}が data:{中身}担ってきます。なので次のコードだとだめ。
正解は一番上のコードになる訳です。

```index.html
    <script>
        angular.module('app', ['onsen'])
        .factory('youtubeResult', function($http){
          return {

                    searchYoutube : function(){
                      return $http.jsonp('http://localhost:3002/youtube?callback=JSON_CALLBACK')
                        .success(function(result){
                            return result;
                          });
                    }
                  }
          }).controller('AppController',['$scope','youtubeResult', function ($scope, youtubeResult) {
            youtubeResult.searchYoutube().then(function(res){
              console.log(data.items);
              $scope.result = res.items;
            });
        }]);
    </script>
```

力尽きたので途中飛ばしている部分があります。
要望があれば加筆修正します。Androidは取得してきた画像との解像度のエラーが出てるっぽいです。暇があれば直してみます。