Angular/Ionic and asynchronous SQLite - guaranteeing the data factory is initialized prior to being returned

I am currently developing a PhoneGap/Cordova application with Ionic framework, utilizing SQLite (with ngCordova) for persistent storage. The main functionality of the app revolves around a scrolling list of items fetched 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 };
]}); 

Initially, I faced an issue where the controller executed getAllItems() before the data was ready, resulting in an empty view. To address this, I attempted delaying the factory return by implementing a factoryReady() function.

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

However, this led to an undefined error as the entire factory was not available when initially called. Despite observing the SQL database being populated correctly over time, Angular threw an exception prematurely.

Realizing the predictability of this behavior, I explored solutions like using resolve in ui-router to ensure proper readiness of asynchronous data sources. Unfortunately, these approaches did not address the core issue with internal async operations within my service.

If you are knowledgeable about AngularJS services and handling asynchronous data initialization, your insights would be greatly appreciated. Thank you.

Answer №1

After some troubleshooting, I finally managed to get it working. Sharing my solution here in case anyone else encounters the same issue.

dataFactory.js

  • I revamped all private methods in dataFactory.js, implementing async SQL calls to return promises
  • Introduced a public method called initDB which orchestrated calls to the private methods sequentially (e.g. openDB >> dropTable_ >> createTable_ etc). This method also returned an empty promise
  • Immediately returned initDB and getAllItems() from the factory

    .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

  • Utilized the resolve feature of ui-router
  • Prior attempts did not correctly inject promises
  • Added a resolve to the top-level abstract state to trigger the call to initDB
  • Injected the promise from initDB into the child state's resolve object
  • Injecting the resolve object into the controller

    // 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) initialize 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) utilizes 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();
        }
      }
    })
    

Everything is functioning as intended now. A huge thanks to the insightful post that helped me clarify my understanding of ui-router Run controllers only after initialization is complete in AngularJS

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

JavaScript now assigns a value of null in place of undefined

When working with JavaScript, it's important to understand that undefined can be reassigned. Because of this, it is recommended to create a self-executing function to ensure that undefined remains undefined. Are there any other values that are loosely ...

Mastering Backbone views and router functionality is essential for building scalable and efficient web

In my code, I have a series of page states that mimic the steps of a shopping cart checkout process. The first set of code sets up the ItemsCollection, ItemsView, and ItemsApp in Backbone.js. var ItemsCollection = Backbone.Collection.extend({ model: I ...

Utilizing jQuery to implement a function on every individual row within a table

I have a collection of objects that requires applying a function to each item, along with logging the corresponding name and id. Below is the snippet of my code: var userJson = [ { id: 1, name: "Jon", age: 20 }, { ...

Tips for adding a string variable to a JQuery HTML element attribute

Hi there, I've been working on a JQuery code to append data to a div element and it's been successful so far. Here is the code: $('#conversation').append('<div id="receiver"><p><b>' + username + ':< ...

Create a feature that allows users to view photos similar to the way it

I want to incorporate a Twitter feed on my website that displays images posted. Instead of having the images redirecting users to Twitter when clicked, I would like them to reveal themselves underneath when a link labeled "View Photo" is clicked, and then ...

Ways to invoke a JavaScript function within a child window that is already open

I am working on a project where I have a main html file. In this file, I want the user to click on something that will trigger a new window or tab to open. This new window/tab should display the dynamically generated content of a hidden div in the main htm ...

Attempting to retrieve data from an object by utilizing an external URL, however

Upon starting the bot console, the console displays: Online with undefined/5 After 10 seconds, an error is thrown: undefined:1 [object Promise] ^ SyntaxError: Unexpected token o in JSON at position 1 This is the code snippet being used: let players clie ...

Transform spaces into %20 from an HTML input field by utilizing JavaScript or jQuery

Hi there, I'm encountering an issue with the input field on my website where users can enter search terms. I am currently extracting the user's input value and adding it to a URL string. jQuery("#searchButton").click(function(){ var simpl ...

What is the best way to access the authData while deleting a user from Firebase?

Previously, when deleting data from Firebase, I used the following code: MyFirebaseRef.on('child_removed', function (oldChildSnapshot) { /* oldChildSnapshot => the data that's been erased */ }); Now, however, I want to achieve the ...

Check to see if a guest has shown support or followed an outside party

When you already follow a company on Twitter, the "Follow Us" button of that company will automatically turn grey, regardless of the domain. So, how can you determine if User-X is following companies A, B, and/or C based on their Twitter handles? The same ...

Error encountered: Mongodb db.collection.find() function is not successfully retrieving data, whereas the collection.insert() function

Working with node.js/express and utilizing a Mongodb database to store various data sets. The functionality for adding, editing, and deleting data on a webpage is functioning properly. For instance, the code snippet for adding data is as follows: router. ...

Extracting an instance through a foreign key in Django models

As a novice in django and sqlite, I'm curious if it's possible to retrieve the instance.name value from its foreign key related to modeldb. My goal is to store my PDF file in the same directory as the modeldb class. Unfortunately, my attempt with ...

Sequential queuing of transitions in CSS3

Is it possible to queue CSS transitions with the same properties? My goal is to translate an element to a specific position (with a transition duration of 0) before translating it again. For example, in this mockup, clicking "move" should move the box 100 ...

AngularJS encountering issues with JSON parsing

My Play framework project utilizes AngularJS for its views. The controller responsible for fetching data sends 2 requests, each returning a JSON block. While the first request works fine and displays the data correctly, the second request's data gets ...

Ways to verify if fields within an Embedded Document are either null or contain data in Node.js and Mongodb

I need to verify whether the elements in the Embedded Document are empty or not. For instance: if (files.originalFilename === 'photo1.png') { user.update( { userName: userName ...

Executing Python and JavaScript with Selenium

I am encountering an issue while trying to run this code. The error message indicates that I may be missing a closing ) or using an illegal character. It seems like there might be confusion with inserting arguments where they are not needed. Could someon ...

Issues arise when attempting to extract data from a data provider using JSON within the context of the Ionic framework

Hey there! I'm relatively new to the world of Angular and Ionic, and I've embarked on a project to create a pokedex app. My approach involves using a JSON file containing an array of "pocket monsters". However, my current challenge lies in extrac ...

What causes an "Internal Server Error" when attempting to use data for a database request with AJAX GET/POST in Laravel?

There's a unique issue that I'm struggling to resolve... Every time I drag and drop an event into the calendar, an Ajax Post Request is sent to my controller. The controller then inserts some data into the database with the event name received v ...

Is there a way to remove a value from the search bar while updating the table at the same time?

Although I can successfully search the table based on the values in my search bar, I am having trouble with updating the state when deleting a value. To see my code in action, check out my sandbox here. ...

How can I utilize data from the server on the client using React and NodeJS?

I am looking to implement data received from a server on the client side. My setup involves a NodeJS Server with NextJS and React. Here is the function used on the server: function addEmailToMailChimp(email, callback) { var options = { method ...