Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TypeError: Cannot call method 'then' of undefined Angularjs

I am pretty new to Angular and have problems with making a synchronous operation. I have resolved few issues which came my way with the angular controller, where I get the error 'Cannot call method then of undefined' thrown from the newController file.

angular.module('newApp.newController', ['angularSpinner', 'ui.bootstrap'])

.controller('newController', function($q, $scope, utilityFactory, $http) {
    utilityFactory.getData().then(function(data) {

        console.log("success");
        console.log(data);

    });
});


angular.module('newApp.utility', [])
    .factory('utilityFactory', function($q, $http) {

        var utils = {};

        //This is a cordova plugin
        var getLauncher = function() {
            return window.plugin.launcher;
        };

        var success = function(data) {
            console.log(device);
            return device;
        }
        var fail = function(error) {
            console.log("error", error);
        };

        utils.getData = function() {
            /* Get the store number details initially before initalizing the application */
            if (window.plugin) {
                var launcher = getLauncher();
                console.log("Fetching data from device");
                //Cordova js is returning this method
                return launcher.getDevice(success, fail);
            }
        };
        return utils;
    })
like image 654
Bharath Avatar asked Mar 02 '16 03:03

Bharath


2 Answers

With the understanding that :

Launcher.prototype.getDevice = function(successCallback, failureCallback) {
    exec(successCallback, failureCallback, KEY, 'getDevice', []);
}

, we know that window.plugin.launcher.getDevice() returns undefined, not a data object. Instead, it provides its response via its success/failure callbacks.

Therefore, to work with promises, window.plugin.launcher.getDevice() needs to be "promisified", involving the explicit creation of a new Promise() and its resolution/rejection by .getDevice's callbacks. (Simply wrapping in $q(...) is not the same thing, and won't work).

angular.module('newApp.utility', []).factory('utilityFactory', function($q, $http) {
    return {
        getDevice: function() {
            return $q.defer(function(resolve, reject) {
                window.plugin.launcher.getDevice(resolve, reject); // If this line throws for whatever reason, it will be automatically caught internally by Promise, and `reject(error)` will be called. Therefore you needn't explicitly fork for cases where `window.plugin` or `window.plugin.launcher` doesn't exist.
            }).promise;
        }
    };
});

Calling from the controller should now work :

angular.module('newApp.newController', ['angularSpinner', 'ui.bootstrap']).controller('newController', function($q, $scope, utilityFactory, $http) {
    return utilityFactory.getDevice().then(function(data) {
        console.log(data);
    }).catch(function(error) {
        console.error(error);
    });
});
like image 72
Roamer-1888 Avatar answered Sep 18 '22 15:09

Roamer-1888


    return launcher.getDevice(success, fail);

this line is the issue, I would just wrap it with a promise:

    return $q(launcher.getDevice.bind(launcher, success, fail));

Edit: also you need to take care of else condition, so code would be:

    utils.getData = function() {
        /* Get the store number details initially before initalizing the application */
        if (window.plugin) {
            var launcher = getLauncher();
            console.log("Fetching data from device");
            //Cordova js is returning this method
            return $q(launcher.getDevice.bind(launcher, success, fail));
        }
        return $q.resolve(); // or $q.reject(reason);
    };
like image 23
mido Avatar answered Sep 17 '22 15:09

mido