取り急ぎ備忘録として掲題の現象について記載.
ブラウザで使えるオフラインのツールを,AngularJS1系で作ろうと思っていた時に陥った現象.
WebSQLでSELECTして帰ってきたデータを,html上にbindしてある$scopeの変数に格納したところ,Error: $rootScope:infdig Infinite $digest Loopというエラーが発生した.
細かく言うと,ng-repeatでWebSQLの結果の配列をDOMに反映させようとしたらエラーが発生した.
Error: $rootScope:infdig Infinite $digest Loopについて
このエラー自体は,$applyの実行により,内部で$rootScope.$digest()が呼び出され,全$watch式が評価される際に出るエラー.まずは$applyの仕様として以下を引用(こちらのサイトから).
$rootScope.$digest() は全ての $watch 式の評価結果が変化しなくなるまで, 「全ての $watch 式の評価 → 変更があった $watch 式のコールバックを実行」 という処理を繰り返します. この繰り返しの処理を $digest ループ,もしくは $digest サイクルと呼びます.
この時,ブラウザが$digestループに陥る恐れがある場合にこのエラーが発生するらしい.以下は公式のドキュメント引用.
This error occurs when the application’s model becomes unstable and each
$digest
cycle triggers a state change and subsequent$digest
cycle. Angular detects this situation and prevents an infinite loop from causing the browser to become unresponsive.
今回の原因
そして,今回の場合何が行けなかったかというと,理由は単純で,WebSQLのデータってただのArrayじゃなくてSQLResultSetRowListっていう特殊なオブジェクトなのでAngularJSのng-repeatでエラーが出たみたい.WebSQL初心者なので気づかなかった.なので,一度Arrayのオブジェクトを作って,そのオブジェクトにfor文を使うなりしてSQLの結果を格納してやればOK.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
db.transaction( function(trans){ trans.executeSql( 'SELECT * FROM table_name;',[], function(trans,r){ for(var i=0;i<r.rows.length;i++){ items.push(r.rows.item(i)); } //itemsはArrayなので普通に扱ってOK } ) } ) |
余談
AngularJS2系全く知らなかったですが,かなり仕様が変わっているらしく,その変更の一つとして「$scopeは使わない」というのがあるらしいので,2系への移行を考えると$scopeを使わない方が良い.最近の1系ではなるべく2系に移行しやすいように,コントローラ自身についてthis.変数名みたいにして扱うらしい.流行についていけていないのを実感した…