TypeError:无法调用未定义Angularjs的方法'then'

时间:2016-03-02 03:24:20

标签: javascript angularjs promise

我对Angular很新,并且在进行同步操作时遇到问题。我已经解决了角度控制器出现的一些问题,我从newController文件中得到错误'无法调用方法然后未定义'。

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

4 个答案:

答案 0 :(得分:5)

了解到:

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

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

因此,要使用promises,window.plugin.launcher.getDevice()需要" promisified",涉及明确创建new Promise()及其解析/拒绝.getDevice' s回调。 (简单地包装在$ 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);
    });
});

答案 1 :(得分: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);
    };

答案 2 :(得分: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返回一个尚未在任何地方声明的变量'device'

4)如果不在getData函数中返回promise,则不能使用'then'。然后是一个在Javascript promises中实现的方法,因此您不能在代码中的任何位置使用它。 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

答案 3 :(得分:0)

正如提到的其他一些答案,您需要从Promise函数返回utils.getData。 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中的promises和异步编程的更多信息,请参阅一些资源:

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

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

作为AngularJS最佳实践的一般指南,另一件事是John Papa的角度风格指南:https://github.com/johnpapa/angular-styleguide

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

在你的情况下,你创建了一个名为newApp.newController的模块,但是你拥有它,它将无法工作,因为它试图引用utilityFactory,它在一个名为{{1的单独模块中定义} {},但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.

角度模块系统的使用和最佳实践可以在这里找到: https://github.com/johnpapa/angular-styleguide/blob/master/a1/README.md#modules