Angular / Ionic和异步SQLite - 确保数据工厂在返回之前初始化 [英] Angular/Ionic and async SQLite - ensuring data factory initialised before return

查看:90
本文介绍了Angular / Ionic和异步SQLite - 确保数据工厂在返回之前初始化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm writing a PhoneGap/Cordova app with Ionic, and using SQLite (with ngCordova) for persistent storage. The core of the app is a scrolling list of items which are retrieved from the SQLite database.

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

最初我是立即回到工厂。控制器在数据准备好之前运行了 getAllItems()。该视图最初为空,仅在第二个 getAllItems()

Initially I was returning the factory straight away. The controller did getAllItems() which ran before the data was ready. The view was initially empty, only showing anything on return to the route after a second getAllItems()

后返回路线时显示任何内容所以我尝试通过添加factoryReady()函数来延迟工厂的返回,并且只有在所有内部数据库内容都准备好后才调用它

So I tried delaying the return of the factory by adding a factoryReady() function and only calling it once all the internal DB stuff was ready

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

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

And now there's an undefined error as the entire factory is unavailable when first called, rather than getAllItems() simply returning empty-handed. I can see that the SQL database is being correctly written to in due course, but Angular throws an exception before this has finished.

我现在意识到这是可以预测的,我已阅读帖子 AngularJS:使用异步数据初始化服务但不非常了解如何实现排名靠前的答案(通过joakimbl)

I realise now that this is predictable, I've read the post AngularJS : Initialize service with asynchronous data but don't quite understand how to implement the top-ranked answer (by joakimbl)

揭示服务的最佳方式是什么,并确保在内部异步内容之前控制器不会调用它已完成?我是否需要将ENTIRE服务作为承诺而不仅仅是 getAllItems 的结果?我有一个去,但现在我很困惑。谢谢。

What's the best way to expose the service and ensure it's not called by the controller until the internal async stuff has finished? Do I need to return the ENTIRE service as a promise rather than just the result from getAllItems? I had a go at this but am now confused. Thanks.

编辑

我还研究过使用ui-router的解析加载视图时 http ://blog.brunoscopelliti.com/show-route-only-after-all-promises-are-resolved 但这并不能解决SQL数据/工厂的内部准备问题。如果我返回 getAllCases 方法,那么它仍然会被立即调用,SQL数据库中没有任何内容,SQL查询返回空结果集,promise解析并且视图渲染。

I've also looked into using ui-router's resolve when loading the view http://blog.brunoscopelliti.com/show-route-only-after-all-promises-are-resolved but that doesn't fix the internal readiness of the SQL data / factory. If I return the getAllCases method then it is still immediately called, there is nothing in the SQL database yet, the SQL query returns an empty results set, the promise resolves and the view is rendered.

推荐答案

管理以使其最终运作。在这里发布此问题给其他有问题的人。

Managed to get it working in the end. Posting this here for anyone else having the issue.

dataFactory.js


  • 使用 dataFactory.js 中的异步SQL调用重写所有私有方法以返回promises

  • 创建一个公共initDB方法,该方法将调用链接到私有方法(例如 openDB >> dropTable _ >> createTable _ 等等)。还返回了一个promise(空)

  • 返回 initDB getAllItems()立即出厂

  • Reworked all private methods using async SQL calls in dataFactory.js to return promises
  • Created a public initDB method which chained calls to the private methods (e.g. openDB >> dropTable_ >> createTable_ etc). Also returned a promise (empty)
  • Returned initDB and getAllItems() from the factory immediately

.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 到顶级抽象状态以触发对 initDB的调用

  • 从<$ c $注入承诺c> initDB 到子状态的 resolve 对象

  • 将解析对象注入控制器

  • Used the resolve ability of ui-router
  • My previous attempts had not correctly injected promises
  • Added a resolve to the top-level abstract state to fire off the call to initDB
  • Injected the promise from initDB to the child state's resolve object
  • Inject the resolve object into the controller

// APP ROUTING(使用ui-router)
.config(函数($ stateProvider,$ urlRouterProvider){

// APP ROUTING (using ui-router) .config(function($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();
    }
  }
})


现在全部工作。非常感谢这篇文章清理我对ui-router的使用仅在AngularJS中完成初始化后运行控制器

All working now. Massive thanks to this post for clearing up my use of ui-router Run controllers only after initialization is complete in AngularJS

这篇关于Angular / Ionic和异步SQLite - 确保数据工厂在返回之前初始化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆