如何使用工厂函数 return $http.post() 对象?

How to return $http.post() object with factory function?

当我尝试使用 Angular 从工厂函数 return 一个 $http.post() 对象时,它抛出了这个错误:

下面是我的代码:

AngularUtils.js

Fenrir.factory('AngularUtils', function ($http, $log, $window) {

var AngularUtils = {
    save: function(url, obj, errors, not_show_error_toast) {            
        not_show_error_toast = typeof not_show_error_toast !== 'undefined' ? not_show_error_toast : false;
        loaderShow();
        if (angular.isDefined(obj.id)) {
            return $http.put(url + obj.id + '/', obj).
                then(function onSuccess(response) {
                    loaderHide();
                    angular.extend(obj, response);
                    errors = {};
                }), function onError(response) {
                    loaderHide();
                    handleErrors(response, status, errors, not_show_error_toast);
                };
        } else {
            return this.create(url, obj, errors, not_show_error_toast);
        }
    },
    create: function(url, obj, errors, not_show_error_toast) {            
        not_show_error_toast = typeof not_show_error_toast !== 'undefined' ? not_show_error_toast : false;
        return $http.post(url, obj).
            then(function onSuccess(response) {
                loaderHide();
                angular.extend(obj, response);
                errors = {};
            }), function onError(response) {
                loaderHide();
                handleErrors(response, status, errors, not_show_error_toast);
            };
    },
}

   return AngularUtils
})

Admin.js

var Fenrir = angular.module('Fenrir', []);

Fenrir.controller('AdminController', function ($scope, $http, AngularUtils) {
    $scope.createScreen = function () {
        AngularUtils.save('/admin/saveScreen', $scope.record, '', '').then(function (hubs) {
            $scope.hubs = hubs;
            console.log('In ctrl ' + $scope.hubs);
        });
    }
})

如何正确 return $http.post() 对象?

仔细查看错误信息:

TypeError: AngularUtils.save(...).then is not a function

这表示没有名为 then() 的函数是 return 从 AngularUtils.save(...) 编辑而来的。这表明 save() 函数确实执行了,但它没有像预期的那样 return promise

所以你的工厂方法确实执行了,但因为你没有 return 承诺,我们只能推测执行最终导致了哪条路径。

更新:原脚本语法错误

在尝试为您证明我的解决方案时,我发现您的 $put().then() 响应处理程序存在语法问题,错误的地方有一个括号,onError 函数应该是then 函数的第二个参数,而不是在它之后。您的代码应如下所示:

return $http.put(url + obj.id + '/', obj).
            then(function onSuccess(response) {
                loaderHide();
                angular.extend(obj, response);
                errors = {};
            }, function onError(response) {
                loaderHide();
                handleErrors(response, status, errors, not_show_error_toast);
            });

微妙,但很重要,如果你没有严格编译,那么工厂函数根本就没有被正确编译,这可能会导致你的错误。


onesome 路径的工厂方法中 return async promise deferrals (futures) 然后是默认模式,虽然冗长,但 return 一个单一的延迟包裹了整个方法执行,然后对于每个响应路径,您可以选择通过 return 编辑的原始承诺来解决或拒绝响应函数。

可以使用不同的库和技术来执行此操作,see AngularJs and Promises with the $http Service by Rick Strahl 了解常见实现的演练。

save: function(url, obj, errors, not_show_error_toast) {            
    var defer = $.Deferred();

    not_show_error_toast = typeof not_show_error_toast !== 'undefined' ? not_show_error_toast : false;

    if (angular.isDefined(obj.id)) {
        loaderShow(); // NOTE: let create manage the loader start if it needs to
        $http.put(url + obj.id + '/', obj).
            then(function onSuccess(response) {
                loaderHide();
                // HACK: because .then is used instead of .success, you probably want the response.data, please review this
                angular.extend(obj, response.data);

                defer.resolve(obj);
            }, function onError(response) {
                loaderHide();
                handleErrors(response, status, errors, not_show_error_toast);

                defer.reject(response);
            });
    } else {
        // If create is a deferral, it should be safe to return it directly
        return this.create(url, obj, errors, not_show_error_toast);
    }

    return defer.promise();
},

Note: Only call a start or show utility or state operation in the same scope that also calls the reciprocal end/hide/stop/close. The code can work if you do not follow this rule but it gets harder to maintain as your project evolves, for that reason I've moved the loaderShow(); call into the branch that we know will always call loaderHide(). This functional scope can not make the assumption that the create() function will eventually call loaderHide() for us.

This is a common, but bad habbit we all need to grow out of ;)