2015-01-09 10 views
8

Sto scrivendo un'app PhoneGap/Cordova con Ionic e utilizzando SQLite (con ngCordova) per l'archiviazione permanente. Il nucleo dell'app è un elenco a scorrimento di elementi che vengono recuperati dal database SQLite.SQLite angolare/ionico e asincrono - garanzia dell'inizializzazione dei dati dei dati prima della restituzione

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 

Inizialmente stavo tornando alla fabbrica subito. Il controller eseguiva getAllItems() che veniva eseguito prima che i dati fossero pronti. La vista era inizialmente vuota, mostrando solo qualcosa al ritorno sulla rotta dopo un secondo getAllItems()

Così ho provato a ritardare il ritorno della fabbrica aggiungendo una funzione factoryReady() e chiamandola solo una volta che tutta la roba DB interna era pronta

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

E ora c'è un errore non definito come l'intero fabbrica non è disponibile con il primo richiamo, piuttosto che getAllItems() semplicemente tornare a mani vuote. Posso vedere che il database SQL è stato scritto correttamente a tempo debito, ma Angular lancia un'eccezione prima che questo sia finito.

Mi rendo conto ora che questo è prevedibile, ho letto il post AngularJS : Initialize service with asynchronous data, ma non capisco come implementare la soluzione top-ranked (da joakimbl)

Qual è il modo migliore per esporre il servizio e assicurati che non venga chiamato dal controller fino a quando le cose asincrone interne non saranno terminate? Devo restituire il SERVIZIO INTERO come una promessa anziché solo il risultato di getAllItems? Ho provato a farlo ma ora sono confuso. Grazie.

EDIT

Ho anche guardato in utilizzando del resolve quando si carica la vista http://blog.brunoscopelliti.com/show-route-only-after-all-promises-are-resolved ma che non risolve la disponibilità interna del SQL data/factory ui-router. Se restituisco il metodo getAllCases, viene comunque chiamato immediatamente, non c'è ancora nulla nel database SQL, la query SQL restituisce un set di risultati vuoto, la promessa viene risolta e la vista viene renderizzata.

risposta

7

Gestito per farlo funzionare alla fine. Pubblicando questo qui per chiunque abbia il problema.

dataFactory.js

  • rielaborati tutti i metodi privati ​​che utilizzano le chiamate asincrone SQL in dataFactory.js per tornare promesse
  • Creato un metodo initdb pubblico che incatenato chiamate ai metodi privati ​​(ad esempio openDB >>dropTable_ >>createTable_ ecc.).Anche restituito una promessa (vuoto)
  • Tornato initDB e getAllItems() dalla fabbrica immediatamente

    .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

  • usato il resolve capacità di ui-router
  • My precedenti tentativi non erano correttamente promesse
  • Aggiunto un resolve al livello superiore iniettato stato astratto di sparare la chiamata a initDB
  • iniettato la promessa dal initDB allo Stato bambino resolve oggetto
  • Iniettare l'oggetto risolvere in il controller

    // APP Routing (usando 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(); 
        } 
        } 
    }) 
    

Tutto funziona ora. Massiccia grazie a questo post per chiarire il mio uso di ui-router Run controllers only after initialization is complete in AngularJS

Problemi correlati