Angular/Ionic 和异步 SQLite - 确保数据工厂在 return 之前初始化

Angular/Ionic and async SQLite - ensuring data factory initialised before return

我正在使用 Ionic 编写 PhoneGap/Cordova 应用程序,并使用 SQLite(使用 ngCordova)进行持久存储。该应用程序的核心是从 SQLite 数据库中检索的项目的滚动列表。

listController.js

.controller('ListCtrl', [
  '$scope',
  'dataFactory',
  function($scope, dataFactory) {

    var items = dataFactory.getAllItems().then(function(data){
      $scope.allItems = data;
    });

  }
]);

dataFactory.js

.factory('dataFactory', [function($window, $log, $q, $cordovaSQLite, dummyDataGenerator){    

  var db_;

  // ...lots of SQLite fun in here
  // cascading async callbacks to load the database and inject dummy data
  var openDB_ = function(){...};
  var createTable_ = function(){...};
  // etc

  var getAllItems = function(){

    var q = $q.defer();
    $cordovaSQLite.execute(db_, sqlSelectString, []).then(
      function(results) {
        $log.log("SQL SELECT successful");
        var i, len, allItems = [];
        for(i = 0, len = results.rows.length; i < len; i++) {
          allItems.push(results.rows.item(i));
        }
        q.resolve(allItems);
      },
      function (err) {
        q.reject(err);
      }
    );
    return q.promise;
  };

  return { getAllItems: getAllItems };
]}); // <-- factory

最初我 return 直接去工厂。在数据准备好之前,控制器做了 getAllItems() 和 运行。视图最初是空的,仅在 getAllItems()

秒后显示路线 return 上的任何内容

所以我尝试通过添加 factoryReady() 函数来延迟工厂的 return,并且只在所有内部数据库都准备好后才调用它

var factoryReady = function(){
  return {
    getAllItems: getAllItems
  };
};

现在有一个未定义的错误,因为 整个 工厂在第一次调用时不可用,而不是 getAllItems() 只是 return 空手而归。我可以看到 SQL 数据库在适当的时候被正确写入,但是 Angular 在完成之前抛出异常。

我现在意识到这是可以预见的,我已经阅读了 post AngularJS : Initialize service with asynchronous data 但不太了解如何实施最热门的 运行ked 答案(来自 joakimbl )

公开服务并确保在内部异步内容完成之前控制器不调用它的最佳方法是什么?我是否需要 return 整个服务作为承诺,而不仅仅是 getAllItems 的结果?我试了一下,但现在很困惑。谢谢。

编辑

我也研究过在加载视图 http://blog.brunoscopelliti.com/show-route-only-after-all-promises-are-resolved 时使用 ui-router 的 resolve,但这并不能解决 SQL 数据/工厂的内部准备情况。如果我 return getAllCases 方法然后它仍然立即被调用,SQL 数据库中还没有任何内容, SQL 查询 returns 一个空结果设置,promise 解析并呈现视图。

设法使其最终正常运行。将此张贴在这里以供遇到问题的其他人使用。

dataFactory.js

  • dataFactory.js 中使用异步 SQL 调用的所有私有方法重写为 return promises
  • 创建了一个 public initDB 方法,它将调用链接到私有方法(例如 openDB >> dropTable_ >> createTable_ 等)。还 return 承诺(空)
  • 立即从工厂返回 initDBgetAllItems()

    .factory('dataFactory', [function($window, $log, $q, $cordovaSQLite, dummyDataGenerator){    
    
      var db_;
    
      // private methods - all return promises
    
      var openDB_ = function(dbName){
    
        var q = $q.defer();
        // ...call async SQL methods
        return q.promise;
      };
    
      var createTable_ = function(){
        var q = $q.defer();
        // ...call async SQL methods
        return q.promise;               
      };
    
      // ...etc
    
      // public methods
    
      var initDB = function(){
    
        var q = $q.defer();
        // successively call private methods, chaining to next with .then()
        openDB_("myDB").then(function(db){
          var schema = "...SQL schema here..."
          dropTable_(db, "FirstTable", schema).then(function(tableName){
            // ...etc
            // when all done, resolve the promise
            q.resolve();
          })
        })
        return q.promise;
      }
    
      var getAllItems = function(){
    
        var q = $q.defer();
        // ...call async SQL methods
        return q.promise;
      };
    
      return {
        initDB: initDB,
        getAllItems: getAllItems 
      };
    
    ]}); // <-- factory
    

app.js

  • 使用了ui-router
  • resolve能力
  • 我之前的尝试没有正确注入 promises
  • 向顶层抽象状态添加了一个 resolve 以触发对 initDB
  • 的调用
  • initDB 的承诺注入子状态的 resolve 对象
  • 将解析对象注入控制器

    // 应用程序路由(使用 ui-router) .config(函数($stateProvider, $urlRouterProvider){

    $stateProvider
    
      // top-level abstract state that houses Ionic side menu & nav
      .state('app', {
        url: '/app',
        abstract: true,
        templateUrl: "templates/sideMenu.html",
        resolve: {
          dbReady: function($log, dataFactory){
            // (1) init the DB
            return dataFactory.initDB().then(function(){
              $log.log("initDB promise resolved");
          });
        }
      }
    })
    
    // the following states are all child states of app
    
    .state('app.items', {
      url: "/items",
      views: {
        menuContent: {
          templateUrl: "templates/gbCaseList.html",
          // (3) now we can inject the items promise into our controller
          controller: function($scope, $log, items){
            // (4) uses resolved items variable injected by ui-router
            $scope.allItems = items;
          }
        }
      },
      resolve: {
        // (2) note that we MUST inject the dbReady promise, if we don't this will instantiate immediately
        items: function(dbReady, $log, dataFactory){
          // the following call returns a promise
          return dataFactory.getItems();
        }
      }
    })
    

现在一切正常。非常感谢这个 post 清理了我对 ui-router Run controllers only after initialization is complete in AngularJS

的使用