monacaでスマホアプリを作ろう #5

# Serviceを使ってみる
MVCのV(html),C(Controller)に続き、Mの役割を担うServiceを使ってみます。
Model関連の処理をService内にカプセル化することにより、バックエンドをMySQLだろうがOracleだろうがGoogle Cloud Endpointsだろうが、他のレイヤーに影響を与えず自由自在に切り替えらえるわけです。
実際にはバックエンドを切り替えることはほぼ無い(ですよね？)ので、 ここでは実践的な例として、まずモックアップ用のデータを扱うサービスを作ってみます。



js/service/osservice.jsを作成
![service_create.png](https://qiita-image-store.s3.amazonaws.com/0/74328/5c6da35f-3fd1-d3f7-875e-f4d2727ccc42.png)




中身

```js:osservice.js

app.factory('OSService', function () {
    var service = {};
    
    var oss= [
        {"name":"KiKat", "kind":"android", "desc":"androidos4.4系です"},
        {"name":"JellyBean", "kind":"android", "desc":"androidos4.1-3系です"},
        {"name":"Honycomb", "kind":"android","desc":"androidos3.x系です"},
        {"name":"Mavericks", "kind":"osx","desc":"Mac 10.9"},
        {"name":"Mountain Lion", "kind":"osx", "desc":"Mac 10.8"},
        {"name":"Lion", "kind":"osx","desc":"Mac 10.7"},
        {"name":"SnowLeopard", "kind":"osx", "desc":"Mac 10.6"},
    ];

    /**
     * OSのリストを取得する
     * この関数内で遅延が発生しても良いようにcallbackで返す
     */
    service.getOSList= function(callback) {
            callback(oss);
    }

    // serviceオブジェクトを返す
    return service;
});

```


js/controller/page1controller.js を修正

```js:page1controller.js

app.controller('Page1Ctrl',function($scope, OSService){ // OSServiceをインジェクト
    
    /**
     * 検索結果を受け取るコールバック関数
     */
    var searchCallback = function(data){
        $scope.oss = data;
    };
    
    OSService.getOSList(searchCallback);
        
}) ;
    
```



index.htmlにosservice.jsの読み込みを追加

```html:index.html
       .
       .
    <link rel="stylesheet" href="css/style.css">
    <script src="js/controller/page1controller.js"></script>
    <script src="js/service/osservice.js"></script> <!-- 追加 -->
    <script>
        var app = ons.bootstrap();
       .
       .

```

どうでしょうか。プレビューするとまったく変わらない結果が表示されるはずです。


ポイントは、Controller側で``OSService``を使用するためにインジェクトしている箇所と、検索の遅延を考慮して検索結果をコールバック関数経由でControllerに返しているところです。javascriptぽいですね。

本当に遅延に対応しているのかよ？ということで、遅延するようコードを修正

```js:osservice.js
       .
       .
   service.getOSList= function(callback) {
            setTimeout(function(){callback(oss)},3000); //3秒待って結果を返す
    }
       .
       .

```

どうでしょう？プレビューしたら表示されましたか？されませんね。 :cry: 
AngularJSに``$scope.oss``が更新されたことを通知する必要があるようです。

というわけで``page1controller.js``を修正    
    
```js:page1controller.js
       .
       .
    var searchCallback = function(data){
        $scope.oss = data;
        $scope.$apply();  // VIEWを更新する
    };
       .
       .

```

これで3秒後にOSの一覧が表示されるはずです。


さらに``page1.html``を修正して読み込み待ちを表示してみます。

```html:page1.html
<ons-page>
<div ng-controller="Page1Ctrl">
  <ons-toolbar>
    <div class="center">List With Header</div>
  </ons-toolbar>

<div ng-if="!!oss">
  <!-- ossが無い場合は表示しない-->
  <ons-list>
    <ons-list-header>Android Versions</ons-list-header>
    <ons-list-item modifier="chevron" ng-repeat="os in oss" ng-if="os.kind == 'android'">
    {{os.name}}
    </ons-list-item>
    
    <ons-list-header>MacOSX Versions</ons-list-header>
    <ons-list-item modifier="chevron" ng-repeat="os in oss" ng-if="os.kind == 'osx'">
    {{os.name}}
    </ons-list-item>
  </ons-list>
</div>
<div ng-if="!oss" style="text-align:center;">
  <!-- ossが無い場合に読み込み待ちを表示 -->
      <br>
      <br>
      <ons-icon icon="spinner" spin="true"></ons-icon>
</div>
</div>
</ons-page>
```

angularjsの機能である``ng-if``を使って``oss``が無い場合の表示を制御しています。
これでまた一歩アプリっぽくなってきました！