2016-03-02 66 views
14

我对Angular来说很新颖,并且在进行同步操作时遇到问题。我已经解决了一些问题,这些问题来自于角控制器,我从newController文件中得到错误'Can not call method then undefined'。TypeError:无法调用未定义的Angularjs的方法'then'

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

'getData'不返回任何东西 –

+0

我用这个也 回报launcher.getD设备(成功,失败); – Bharath

+0

但是它不会为'else'返回任何东西,而'launcher.getDevice'返回什么? – charlietfl

回答

1
return launcher.getDevice(success, fail); 

此行是问题,我只想有一个承诺把它包:

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

编辑:还需要照顾其他条件,所以代码会:

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

我会检查这一点。 – Bharath

+0

试过这个,在调用getDevice方法后,它进入错误回调函数并打印消息 - “错误” – Bharath

5

有了这样的理解:

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

,我们知道window.plugin.launcher.getDevice()返回undefined,而不是数据对象。相反,它通过其成功/失败回调来提供响应。

因此,要使用promise,window.plugin.launcher.getDevice()需要“promisified”,涉及明确创建new Promise()及其解析/拒绝.getDevice的回调。 (简单地包装在$ q(...)中是不一样的,并且不起作用)。

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

从控制器调用现在应该工作:

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

如何解析并拒绝获取数据 – Bharath

+0

通过将Promise的resolve和reject函数作为successCallback和errorCallback 'window.plugin.launcher.getDevice()'。 –

+0

'ReferenceError:Promise is not defined' 得到这个错误 – Bharath

1

1)你的实际模块应该是 “NEWAPP”,而不是 “newApp.newController” 和 'newApp.utility'。这就是将这两个组件放在单独的模块中,而不是放在myApp模块中。

2)每当你宣布一个新的模块时,才应使用

angular.module('newApp', []) 

的语法。当你要访问的模块,你应该使用

angular.module('newApp') 

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

3)你utilityFactory返回一个变量“设备”尚未宣布任何地方

4)你可以用” t使用'then'而不返回getData函数中的承诺。然后是一个在Javascript承诺中实现的方法,所以你不能在你的代码中的任何地方使用它。 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;  
}; 

这里是调试代码时,我用了一个codepen。我稍微修改了一下你的代码,但是它会给出一个返回promise的函数的例子。 http://codepen.io/anon/pen/QNEEyx?editors=1010

0

正如其他一些答案中提到的,您需要从utils.getData函数中返回Promise。 Angular的$q助手可以让你做到这一点。但是,其他一些答案显示您这样做的方式违背了最佳做法。当使用$q,最好的做法是要做到以下几点:

var myPromise = $q(function (resolve, reject) { 
    // Do some logic in here that is asynchronous and either invoke resolve with 
    // the results or reject with an error that may have occurred 
}); 

因此,你的代码就变成了:

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 */ 
      return $q(function (resolve, reject) { 
       if (!window.plugin) { 
        // You can handle this case as a rejection of the promise 
        reject(new Error('Window plugin not found')); 
        return; 
       } 

       var launcher = getLauncher(); 
       console.log("Fetching data from device"); 
       //Cordova js is returning this method 
       // When device is ready it will "resolve" the promise and 
       // invoke any of the ".then()" functions you add to the promise 
       // If an error occurs, it will invoke any ".catch()" functions 
       // that you have added. 
       launcher.getDevice(resolve, reject); 
      }); 
     }; 
     return utils; 
    }) 

有关$q服务的更多信息,请从官方AngularJS文档,检查这个职位: https://docs.angularjs.org/api/ng/service/ $ q

此外,如果您想了解更多关于承诺和JavaScript中的异步编程的一些资源:

的承诺整洁的可视化工具 - http://bevacqua.github.io/promisees/#

教程上的承诺 - https://www.toptal.com/javascript/javascript-promises

另一件事是寻找到作为AngularJS最佳实践的一般指导是角风格导游约翰爸爸:https://github.com/johnpapa/angular-styleguide

最后,你有你的模块设置的方式稍微关闭。每次调用angular.module(moduleName, dependencies)将创建一个具有这些依赖关系的新模块。虽然将角度应用分解为多个模块是一个好主意,但您需要确保使用ng-app指令引用的根或“主”应用具有对所有子模块的引用,并且任何引用依赖关系的模块从另一个模块中将该模块包含在其依赖列表中。

在你的情况,你创建一个名为newApp.newController模块,但你拥有它,因为它试图引用utilityFactory,这是在所谓的newApp.utility一个单独的模块定义的,但不是由你newApp.newController模块引用它不会工作。为了解决这个问题,请执行下列操作:

angular.module('newApp.newController', ['angularSpinner', 'ui.bootstrap', 'newApp.utility']) 
// Make sure to add 'newApp.utility' to the dependencies of the 'newApp.newController' module. 

或者,你可以创建两个控制器和同一模块中的效用工厂:绕角模块系统

// Create the module once 
angular.module('newApp', ['angularSpinner', 'ui.bootstrap']); 

// Reference it by invoking it with just one parameter 
angular.module('newApp').controller('newController', ...); 

angular.module('newApp').factory('utilityFactory', ...); 

用法和最佳实践能在这里找到: https://github.com/johnpapa/angular-styleguide/blob/master/a1/README.md#modules

相关问题