MENU

cordovaSQLite使用上の落とし穴とその対策

ionicハイブリッドアプリ開発 ionic
ionicハイブリッドアプリ開発

$cordovaSQLiteでエラー

ngCordovaはモバイルアプリ開発において、スマートフォン本体が持つハードウェアの機能にアクセスできる橋渡しをしてくれる機能がたくさん提供されているプラグイン集です。
その中の1つに、cordovaSQLiteがあります。
例えばハイブリッドアプリでありながら「カメラ」機能にアクセスしたり、GPS機能や重力傾きセンサーを使うことができるようになります。
しかもHTML上で。つまりホームページを軽く作れるレベルで、カメラやGPS機能が使えるようになる夢のプラグイン群なのです。
今回はその実機機能の一つ、SQLiteのプラグインを使っている最中にエラーが発生しました

SQLiteのエラー症状と原因

エラーの症状としては、.service内で$cordovaSQLiteを呼び出してデータを取得後、戻り値が正常に受け取れなかったのです。
原因は、cordovaSQLiteは非同期で処理されるため、適切な処理をしないとダメだったのです。

CordovaSQLiteで失敗した例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<span class="hljs-keyword">var</span> db = <span class="hljs-keyword">null</span>;
<span class="hljs-comment">// 〜〜中略 </span>
<span class="hljs-comment">// .runでデータベースの接続とか、初期化処理をします</span>
.run(<span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">($ionicPlatform , $cordovaSQLite)</span> </span>{
  $ionicPlatform.ready(<span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">()</span> </span>{
    db = $cordovaSQLite.openDB(<span class="hljs-string">"DBName"</span>);
  })
})
 
 
.service(<span class="hljs-string">'GLOBAL'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">($ionicPlatform  ,$cordovaSQLite )</span> </span>{
  this.getAll = <span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">()</span></span>{
    Data = [];
    $ionicPlatform.ready(<span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">()</span></span>{
      <span class="hljs-keyword">var</span> query = <span class="hljs-string">"select * from data"</span>;
      $cordovaSQLite.execute(db, query).then(<span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">(res)</span> </span>{
        <span class="hljs-keyword">for</span>(i = <span class="hljs-number">0</span> ; i <res.rows.length;i++){
          <span class="hljs-keyword">var</span> obj = {
            id:res.rows.item(i).qid ,
            val:res.rows.item(i).val
          };
          Data.push(obj);
        }
      }, <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">(err)</span> </span>{
        console.error(err);
        alert(<span class="hljs-string">"データの読み込みに失敗しました"</span>);
      });
    })
    <span class="hljs-keyword">return</span> Data;
  }

SQLiteからデータを取得するserviceを作成して

1
2
3
4
.controller(<span class="hljs-string">'sampleCtrl'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">($scope , GLOBAL )</span> </span>{
  $scope.Data = <span class="hljs-keyword">GLOBAL</span>.getQuizAll();
  <span class="hljs-comment">//〜その後$scope.Dataを使って足したり引いたり色々な処理をする</span>
})

コントローラからSQLiteのデータを取得して、その後使用したいのだが・・・エラー。
console.logで$scope.Dataの内容を出力されると

と表示される。つまり$scope.Dataの中身は空っぽなのです。

原因は、$cordovaSQLiteの処理は非同期で行われるため、$scope.Data = GLOBAL.getQuizAll();した時点ではまだQueryの結果が帰ってきていないために起こる模様。
例えば$scope.Dataをng-repeatで書き出すだけとかならエラーにならないのですが、取得した値を元に演算するとダメです。

CordovaSQLiteのトラブルを修正した例

よって、修正後はこのようになりました

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<span class="hljs-comment">//前略</span>
.service(<span class="hljs-string">'GLOBAL'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">($ionicPlatform  ,$cordovaSQLite , $q )</span> </span>{
  this.getAll = <span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">()</span></span>{
    <span class="hljs-comment">/* 非同期 */</span>
    <span class="hljs-keyword">var</span> deferred = $q.defer();
    Data = [];
    $ionicPlatform.ready(<span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">()</span></span>{
      <span class="hljs-keyword">var</span> query = <span class="hljs-string">"select * from data"</span>;
      $cordovaSQLite.execute(db, query).then(<span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">(res)</span> </span>{
        <span class="hljs-keyword">for</span>(i = <span class="hljs-number">0</span> ; i <res.rows.length;i++){
          <span class="hljs-keyword">var</span> obj = {
            qid:res.rows.item(i).qid ,
            val:res.rows.item(i).val
          };
          Data.push(obj);
        }
        deferred.resolve(Data);
      }, <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">(err)</span> </span>{
        console.error(err);
        deferred.reject(<span class="hljs-string">"データの読み込みに失敗しました。"</span>);
      });
    })
    <span class="hljs-keyword">return</span> deferred.promise;
  }
<span class="hljs-comment">//後略〜</span>

そして、呼び出し側は次の通り

1
2
3
4
5
6
7
8
.controller(<span class="hljs-string">'sampleCtrl'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">($scope , GLOBAL )</span> </span>{
  <span class="hljs-keyword">GLOBAL</span>.getAll().then(
    <span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">(res)</span></span>{
      $scope.Data = res;
      <span class="hljs-comment">//このあと、$scope.Dataを使って足したり、引いたり、色々したりします。</span>
    }
  );
})

これで意図したとおりに動きました。

PAGE TOP