コードリーディング
AngularJS初心者です。
コードリーディングという形で、AngularJSで実装されたWebサービスのソースコードを題材に使って、その動きや機能がどのように実装されているのかを、ソースコードのレベルで解析していこうと思います。
※時間のある時に、随時追記していきます。
題材「FMTube 」について
コードリーディングの題材とするのはこちら。
ゆーすけべーさんが、AngularJSの勉強中に作成したサービスだそうです。
アーティストの名前を入れると、楽曲リストを順番に再生してくれます。
素晴らしいです。勉強させて頂きます。
参考資料
正直、全然わからない。。。この辺りを参考にしながら進めていきます。
初心者向けAngularJS - その1 - albatrosary's blog
Service, Factory, Providerが何の事か分からない方はこちら。
AngularJs Service,Factory,Providerなどなど - senta.me/blog
エントリポイント
var app = angular.module('fmtube', ['ng']);
ここでは、このアプリケーションを初期化して、依存するモジュールを登録します。
view
次に、view部分をざっと眺めます。
#content, #listあたりに注目すれば処理内容を把握できそうです。
<html ng-app="fmtube" ng-controller="controller" class="ng-scope"><head><style type="text/css">@charset "UTF-8";[ng\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,.ng-hide{display:none !important;}ng\:form{display:block;}.ng-animate-start{border-spacing:1px 1px;-ms-zoom:1.0001;}.ng-animate-active{border-spacing:0px 0px;-ms-zoom:1;}</style> <meta charset="utf-8"> <title ng-bind="title" class="ng-binding">Eis essen by GReeen - FMTube!</title> <link href="css/bootstrap.min.css" rel="stylesheet"> <link href="css/typeahead.js-bootstrap.css" rel="stylesheet"> <link href="css/app.css" rel="stylesheet"> <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries --> <!--[if lt IE 9]> <script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script> <script src="https://oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script> <![endif]--> <script id="www-widgetapi-script" src="https://s.ytimg.com/yts/jsbin/www-widgetapi-vflAiKzF-/www-widgetapi.js" async=""></script><script src="http://www.youtube.com/iframe_api"></script><script id="twitter-wjs" src="http://platform.twitter.com/widgets.js"></script><script async="" src="//www.google-analytics.com/analytics.js"></script><script src="js/jquery.js"></script> <script src="js/typeahead.min.js"></script> <script src="js/bootstrap.min.js"></script> <script src="js/angular.min.js"></script> <script src="js/angular-resource.min.js"></script> <script src="js/app.js"></script> <script> (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); ga('create', 'UA-492497-67', 'yusukebe.github.io'); ga('send', 'pageview'); </script> </head> <body style="" data-twttr-rendered="true"> <div class="container"> <div id="header" class="clearfix"> <h1><a href="http://yusukebe.github.io/FMTube/">FMTube!</a></h1> </div> <!-- /header --> <div class="row"> <div id="form" class="col-md-4"> <form role="form" ng-submit="submit(true)" class="ng-valid ng-dirty"> <div class="form-group"> <span class="twitter-typeahead ng-valid ng-dirty" style="position: relative; display: inline-block; direction: ltr;"><input class="tt-hint" type="text" autocomplete="off" spellcheck="off" disabled="" style="position: absolute; top: 0px; left: 0px; border-color: transparent; box-shadow: none; background: none 0% 0% / auto repeat scroll padding-box border-box rgb(255, 255, 255);"><input type="text" class="form-control typeahead tt-query" placeholder="Artist name" ng-model="artist" autocomplete="off" spellcheck="false" dir="auto" style="position: relative; vertical-align: top; background-color: transparent;"><span style="position: absolute; left: -9999px; visibility: hidden; white-space: nowrap; font-family: 'Alegreya Sans', sans-serif; font-size: 14.2857141494751px; font-style: normal; font-variant: normal; font-weight: 400; word-spacing: 0px; letter-spacing: 0px; text-indent: 0px; text-rendering: auto; text-transform: none;">GReeen</span><span class="tt-dropdown-menu" style="position: absolute; top: 100%; left: 0px; z-index: 100; display: none; right: auto;"><div class="tt-dataset-artist" style="display: block;"><span class="tt-suggestions" style="display: block;"><div class="tt-suggestion" style="white-space: nowrap; cursor: pointer;"><p style="white-space: normal;">Greeen Linez</p></div><div class="tt-suggestion" style="white-space: nowrap; cursor: pointer;"><p style="white-space: normal;">GReeen</p></div><div class="tt-suggestion" style="white-space: nowrap; cursor: pointer;"><p style="white-space: normal;">Greeen</p></div><div class="tt-suggestion" style="white-space: nowrap; cursor: pointer;"><p style="white-space: normal;">Dr.Greeen</p></div><div class="tt-suggestion" style="white-space: nowrap; cursor: pointer;"><p style="white-space: normal;">Al Greeen</p></div></span></div></span></span> <button type="submit" class="btn btn-default"><b>Play!</b></button> </div> </form> </div> </div> <div id="social-buttons"> <span style="margin-right:4px;"> <iframe class="hatena-bookmark-button-frame" title="このエントリーをはてなブックマークに追加" frameborder="0" scrolling="no" width="130" height="20" src="javascript:false" style="width: 111px; height: 20px;"></iframe><script type="text/javascript" src="http://b.st-hatena.com/js/bookmark_button.js" charset="utf-8" async="async"></script> </span> <iframe id="twitter-widget-0" scrolling="no" frameborder="0" allowtransparency="true" src="http://platform.twitter.com/widgets/tweet_button.d58098f8a7f0ff5a206e7f15442a6b30.en.html#_=1416154421769&count=horizontal&id=twitter-widget-0&lang=en&original_referer=http%3A%2F%2Fyusukebe.github.io%2FFMTube%2Findex.html&size=m&text=FMTube!&url=http%3A%2F%2Fyusukebe.github.io%2FFMTube%2F" class="twitter-share-button twitter-tweet-button twitter-share-button twitter-count-horizontal" title="Twitter Tweet Button" data-twttr-rendered="true" style="width: 109px; height: 20px;"></iframe> <script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+'://platform.twitter.com/widgets.js';fjs.parentNode.insertBefore(js,fjs);}}(document, 'script', 'twitter-wjs');</script> <span style="margin-left:-20px;"> <iframe src="//www.facebook.com/plugins/like.php?href=http%3A%2F%2Fyusukebe.github.io%2FFMTube%2F&width=150&layout=button_count&action=like&show_faces=true&share=true&height=21&appId=640667512644377" scrolling="no" frameborder="0" style="border:none; overflow:hidden; height:21px; width:150px;" allowtransparency="true"></iframe> </span> </div> <hr> <div class="row"> <div id="content"> <iframe id="player" frameborder="0" allowfullscreen="1" title="YouTube video player" width="600" height="400" src="https://www.youtube.com/embed/5sveP1gSXGA?autoplay=1&rel=0&enablejsapi=1&origin=http%3A%2F%2Fyusukebe.github.io"></iframe> </div> <div id="list"> <!-- ngRepeat: track in tracks --><div class="list-item ng-scope" ng-repeat="track in tracks"> <a class="clearfix list-active" ng-click="click($index)" ng-class="active_class($index)"> <img ng-src="" class="pull-left"> <b class="pull-left ng-binding">Eis essen</b> </a> </div><!-- end ngRepeat: track in tracks --><div class="list-item ng-scope" ng-repeat="track in tracks"> <a class="clearfix" ng-click="click($index)" ng-class="active_class($index)"> <img ng-src="http://userserve-ak.last.fm/serve/34s/97541019.jpg" class="pull-left" src="http://userserve-ak.last.fm/serve/34s/97541019.jpg"> <b class="pull-left ng-binding">Gesundes Ego</b> </a> </div><!-- end ngRepeat: track in tracks --><div class="list-item ng-scope" ng-repeat="track in tracks"> <a class="clearfix" ng-click="click($index)" ng-class="active_class($index)"> <img ng-src="" class="pull-left"> <b class="pull-left ng-binding">Eis essen - JuliensBlogContest</b> </a> </div><!-- end ngRepeat: track in tracks --><div class="list-item ng-scope" ng-repeat="track in tracks"> <a class="clearfix" ng-click="click($index)" ng-class="active_class($index)"> <img ng-src="http://userserve-ak.last.fm/serve/34s/92538915.jpg" class="pull-left" src="http://userserve-ak.last.fm/serve/34s/92538915.jpg"> <b class="pull-left ng-binding">Ein gesundes Ego</b> </a> </div><!-- end ngRepeat: track in tracks --><div class="list-item ng-scope" ng-repeat="track in tracks"> <a class="clearfix" ng-click="click($index)" ng-class="active_class($index)"> <img ng-src="http://userserve-ak.last.fm/serve/34s/92538915.jpg" class="pull-left" src="http://userserve-ak.last.fm/serve/34s/92538915.jpg"> <b class="pull-left ng-binding">Nur das Beste</b> </a> </div><!-- end ngRepeat: track in tracks --><div class="list-item ng-scope" ng-repeat="track in tracks"> <a class="clearfix" ng-click="click($index)" ng-class="active_class($index)"> <img ng-src="http://userserve-ak.last.fm/serve/34s/92538915.jpg" class="pull-left" src="http://userserve-ak.last.fm/serve/34s/92538915.jpg"> <b class="pull-left ng-binding">Die Blüte meiner Selbst</b> </a> </div><!-- end ngRepeat: track in tracks --><div class="list-item ng-scope" ng-repeat="track in tracks"> <a class="clearfix" ng-click="click($index)" ng-class="active_class($index)"> <img ng-src="http://userserve-ak.last.fm/serve/34s/92538915.jpg" class="pull-left" src="http://userserve-ak.last.fm/serve/34s/92538915.jpg"> <b class="pull-left ng-binding">Blauer Planet</b> </a> </div><!-- end ngRepeat: track in tracks --><div class="list-item ng-scope" ng-repeat="track in tracks"> <a class="clearfix" ng-click="click($index)" ng-class="active_class($index)"> <img ng-src="" class="pull-left"> <b class="pull-left ng-binding">JottBeBe Qualifikation</b> </a> </div><!-- end ngRepeat: track in tracks --><div class="list-item ng-scope" ng-repeat="track in tracks"> <a class="clearfix" ng-click="click($index)" ng-class="active_class($index)"> <img ng-src="http://userserve-ak.last.fm/serve/34s/92538915.jpg" class="pull-left" src="http://userserve-ak.last.fm/serve/34s/92538915.jpg"> <b class="pull-left ng-binding">Hovercraft</b> </a> </div><!-- end ngRepeat: track in tracks --><div class="list-item ng-scope" ng-repeat="track in tracks"> <a class="clearfix" ng-click="click($index)" ng-class="active_class($index)"> <img ng-src="" class="pull-left"> <b class="pull-left ng-binding">Lächeln Exklusiv</b> </a> </div><!-- end ngRepeat: track in tracks --><div class="list-item ng-scope" ng-repeat="track in tracks"> <a class="clearfix" ng-click="click($index)" ng-class="active_class($index)"> <img ng-src="" class="pull-left"> <b class="pull-left ng-binding">Hippie 2.0</b> </a> </div><!-- end ngRepeat: track in tracks --><div class="list-item ng-scope" ng-repeat="track in tracks"> <a class="clearfix" ng-click="click($index)" ng-class="active_class($index)"> <img ng-src="" class="pull-left"> <b class="pull-left ng-binding">Scheiss doch einfach drauf (feat. Der Rote)</b> </a> </div><!-- end ngRepeat: track in tracks --><div class="list-item ng-scope" ng-repeat="track in tracks"> <a class="clearfix" ng-click="click($index)" ng-class="active_class($index)"> <img ng-src="" class="pull-left"> <b class="pull-left ng-binding">Hab Wörter für dich</b> </a> </div><!-- end ngRepeat: track in tracks --><div class="list-item ng-scope" ng-repeat="track in tracks"> <a class="clearfix" ng-click="click($index)" ng-class="active_class($index)"> <img ng-src="" class="pull-left"> <b class="pull-left ng-binding">JottBeBe Winin Battle</b> </a> </div><!-- end ngRepeat: track in tracks --><div class="list-item ng-scope" ng-repeat="track in tracks"> <a class="clearfix" ng-click="click($index)" ng-class="active_class($index)"> <img ng-src="" class="pull-left"> <b class="pull-left ng-binding">Vaubetee Quali</b> </a> </div><!-- end ngRepeat: track in tracks --><div class="list-item ng-scope" ng-repeat="track in tracks"> <a class="clearfix" ng-click="click($index)" ng-class="active_class($index)"> <img ng-src="" class="pull-left"> <b class="pull-left ng-binding">Pinselstrich für die Ohren</b> </a> </div><!-- end ngRepeat: track in tracks --><div class="list-item ng-scope" ng-repeat="track in tracks"> <a class="clearfix" ng-click="click($index)" ng-class="active_class($index)"> <img ng-src="" class="pull-left"> <b class="pull-left ng-binding">Abseits der Norm</b> </a> </div><!-- end ngRepeat: track in tracks --><div class="list-item ng-scope" ng-repeat="track in tracks"> <a class="clearfix" ng-click="click($index)" ng-class="active_class($index)"> <img ng-src="" class="pull-left"> <b class="pull-left ng-binding">Ich bin hier</b> </a> </div><!-- end ngRepeat: track in tracks --><div class="list-item ng-scope" ng-repeat="track in tracks"> <a class="clearfix" ng-click="click($index)" ng-class="active_class($index)"> <img ng-src="" class="pull-left"> <b class="pull-left ng-binding">Frauen</b> </a> </div><!-- end ngRepeat: track in tracks --><div class="list-item ng-scope" ng-repeat="track in tracks"> <a class="clearfix" ng-click="click($index)" ng-class="active_class($index)"> <img ng-src="" class="pull-left"> <b class="pull-left ng-binding">VauBeTee Qualifikation - Instrumental</b> </a> </div><!-- end ngRepeat: track in tracks --> </div> </div> <hr> <div id="footer"> <address> © <a href="http://yusukebe.github.io/">yusukebe</a>, <b>FMTube</b> using Last.fm Web API, YouTube API, jQuery, AngularJS, Twitter Bootstrap and GitHub! </address> </div> </div> </body></html>
run
またjs/app.jsに戻って、処理内容を確認します。
app.run(function(){ var tag = document.createElement('script'); tag.src = "http://www.youtube.com/iframe_api"; var first_tag = document.getElementsByTagName('script')[0]; first_tag.parentNode.insertBefore(tag, first_tag); });
runって何ですか。。。?
StackOverflowさんによると、constantsやinstanceはrunブロックでしか注入できないらしい。
[参考]
breeze - AngularJS app.run() documentation? - Stack Overflow
Factory, Service, Providerについて
Service,Value,Factory,Provider ってなんぞ?そこについて前提知識がないと、以降のコードを理解するのは難しそうです。
先ず、サービスとは…
Angularサービスはシングルトンオブジェクト、またはWebアプリケーション共通の特定のタスクを実行する関数です。 Angularは、サーバにリクエストを送るブラウザのXMLHttpRequestオブジェクトにアクセスする$httpサービスのような、 いくつかの組み込みサービスを持ちます。 他のコアなAngular変数と識別子と同様に、組み込みのサービスは常に$から始まります。(前述した$httpのように) また、独自のカスタムサービスを作ることも可能です。
とのこと。 (http://js.studio-kingdom.com/angularjs/guide/understanding_services)
このサービスをDIに登録する方法が複数あり、大分とまどいました。
DI
この際、ぶっちゃけると「DIとか何それおいしいの?」な人もたくさんいらっしゃる事でしょう。
僕もその1人です。こちらを読むと何となくわかった気になれます。
要するに DI って何なのという話 - 猫型の蓄音機は 1 分間に 45 回にゃあと鳴く
明日から、ドヤ顔で新人君に説明できますね。どや!
今日はここまで。また追記します。。。