Mulgard
Mulgard

Reputation: 10589

Angular Factory returns null

I'm trying to build a database in my angular factory:

angular.module("App")

.factory("DatabaseFactory", function () {
    var database = null;
    var factory = {};

    factory.getDatabase = function () {
        if (database == null) {
            window.sqlitePlugin.openDatabase({
                name: "myDB.db",
                androidDatabaseImplementation: 2,
                androidLockWorkaround: 1
            }, function (db) {
                database = db;

                database.transaction(function (transaction) {
                    transaction.executeSql(create_user, [], function (transaction, result) {
                        console.log("table user created: " + JSON.stringify(result));
                    }, function (error) {
                        console.log("table user error: " + error.message);
                    });
                }, function (error) {
                    console.log("transaction error: " + error.message);
                }, function () {
                    console.log("transaction ok");

                    return database;
                });
            });
        } else {
            return database;
        }
    }

    return factory;
});

The creation of the database works, the transaction is also ok. I now provide a service with a function to init the database:

angular.module("App")

.service("DatabaseService", function (DatabaseFactory) {
    var database;

    function initDatabase() {
        console.log("init before database: " + JSON.stringify(database));

        database = DatabaseFactory.getDatabase();

        console.log("intit after database: " + JSON.stringify(database));
    }

    return {
        initDatabase: function () {
            initDatabase();
        }
    };
});

It gets called on device ready:

angular.module("App", ["ionic", "ngCordova", "App.Home"])

.config(function ($stateProvider, $urlRouterProvider) {
    $stateProvider.state("app", {
        url: "/app",
        abstract: true,
        templateUrl: "templates/main.html"
    });

    $urlRouterProvider.otherwise("/app/home");
})

.run(function ($rootScope, $ionicPlatform, DatabaseService) {
    $ionicPlatform.ready(function () {
        console.log("ionic Ready");

        if (window.cordova && window.cordova.plugins.Keyboard) {
            cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
        }

        if (window.StatusBar) {
            StatusBar.styleDefault();
        }

        DatabaseService.initDatabase();
    });
});

The log output:

init before database: + undefined
init after database: + undefined

So the return of the database in my factory returns undefined, but I don't know why. It should return the database since it is correctly initialized.

Upvotes: 0

Views: 243

Answers (2)

Dan Prince
Dan Prince

Reputation: 29989

You can't return the database from the function, because the function that receives it is an asynchronous callback.

You can only use the return statement if the entire function is synchronous (e.g. doesn't do any async work, such as reading from files, connecting to databases, network requests, sockets etc).

In your case, window.sqlitePlugin.openDatabase does some asynchronous work and asks for a callback as the second argument. This callback will be called after the database connection has opened, which will be after your getDatabase function has returned a value.

window.sqlitePlugin.openDatabase({
  name: "myDB.db",
  androidDatabaseImplementation: 2,
  androidLockWorkaround: 1
}, function (db) {
  database = db
  // this happens at some point in the future
});

// this happens straight away
// at this point database is still undefined
return database;

A good way to test this for future reference is to use console.log to see at what time and in what order your code is run.

window.sqlitePlugin.openDatabase({
  // ...
}, function (db) {
  database = db
  console.log('A');
});

console.log('B');
return database;

You would see that rather than being executed in the order the statements are written, B is logged first, then A second.

If you make your getDatabase method take a callback argument, you can pass the db object into it as soon as it is ready.

factory.getDatabase = function (callback) {
  window.sqlitePlugin.openDatabase({
    // ...
  }, function (db) {
    // do some stuff with db, when you are ready
    // pass it to the callback, with null as the
    // first argument (because there isn't an error
    callback(null, db);
  });

Then you would rewrite your code to make use of the callback.

DatabaseFactory.getDatabase(function(err, db) {
  console.log("intit after database: " + JSON.stringify(database));
});

You might be wondering why the callback has an err argument too.

In node.js, it is considered standard practice to handle errors in asynchronous functions by returning them as the first argument to the current function's callback. If there is an error, the first parameter is passed an Error object with all the details. Otherwise, the first parameter is null.

(From NodeJitsu)

Upvotes: 1

Manouk
Manouk

Reputation: 13

I think you should replace

var database = null;
var factory = {};

by

var factory = {};

and do

return factory.database

in your factory.getDatabase

Upvotes: 0

Related Questions