Friday, March 11, 2016

TypeError: Cannot call method 'then' of undefined Angularjs

Leave a Comment

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;     }) 

3 Answers

Answers 1

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 (like nodebacks but with the args reversed).

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);     }); }); 

Answers 2

    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);     }; 

Answers 3

1) Your actual Module should be "newApp", not "newApp.newController" and 'newApp.utility'. That is placing those two components into separate modules instead of in the myApp module.

2) You should only use the syntax of

angular.module('newApp', []) 

whenever you are declaring a new module. When you want to access the module, you should use

angular.module('newApp') 

https://docs.angularjs.org/api/ng/function/angular.module

3) Your utilityFactory is returning a variable 'device' that hasn't been declared anywhere

4) You can't use 'then' without returning a promise in your getData function. Then is a method that is implemented in Javascript promises, so you can't just use it anywhere in your code. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then

utils.getData = function() {     var deferred = $q.defer();      if (window.plugin) {         var launcher = getLauncher();         console.log("Fetching data from device");         //Cordova js is returning this method         return launcher.getDevice(success, fail);     }      return deferred.promise;     }; 

Here is a codepen I used when debugging your code. I modified your code a little, but it will give an example of the function working when returning a promise. http://codepen.io/anon/pen/QNEEyx?editors=1010

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment