嵌套的承诺在单击按钮之前无法解析

时间:2013-08-22 07:37:41

标签: angularjs promise

我正在构建一个AngularJS应用程序(使用1.2.0 RC1),它使用REST API来保存用户输入的数据。用户将输入有关公司的数据,它包含两个我需要保存的核心组件:

  • 公司详情
  • 该公司内的联系人

我有一个REST方法来调用以保存公司,然后我有另一个我要求保存每个联系人的方法。

我计划使用$q跟踪所有请求,并在完成后解决这些请求。

我遇到的问题是,虽然REST API被成功调用并且我的所有数据都被保存但是当所有承诺都已解决时我的控制器没有得到通知,直到我点击了屏幕上的按钮,例如add contact按钮。我已经尝试消除REST API作为问题的根源,但在使用setTimeout模拟延迟作业时仍然会发生这种情况。

这是我的service模块:

angular.module('myApp', ['restService'])
    .factory('service', ['client', '$q', function (client, $q) {
        return {
            create: function (name, people) {
                var deferred = $q.defer();

                setTimeout(function () {
                    console.log('saved company ' + name);

                    var promises = people.map(function (person) {
                        var dfd = $q.defer();

                        setTimeout(function () {
                            console.dir(person);
                            console.log('Person saved');
                            dfd.resolve();
                        }, 100);

                        return dfd.promise;
                    });

                    $q.all(promises).then(deferred.resolve);
                }, 100);

                return deferred.promise;
            }
        };
    }]);

这是我的控制器:

angular.module('club', ['myApp'])
    .controller('RegisterCtrl', ['$scope', 'service', function ($scope, service) {
        $scope.addPerson = function () {
            $scope.company.people.push({
                firstName: '',
                lastName: '',
                email: ''
            });
        };

        $scope.removePerson = function (person, index) {
            $scope.company.people.splice(index, 1);
        };

        $scope.save = function (company) {
            service.createClub(company.name, company.people).then(function () {
                console.log('And we\'re done');
            });
        };

        $scope.company = {
            name: '',
            people: []
        };
    }]);

因此,当我调用save方法(ng-click绑定到一个按钮)时,我将设置一个如下的console.log:

saved company
Object
Person saved
Object
Person saved

注意没有And we're done输出。只要我点击addPerson ng-click绑定的按钮,我就会得到最终的消息输出。

3 个答案:

答案 0 :(得分:2)

Angular不会神奇地观看所有超时。它需要一个钩子来告诉某些东西可能已经改变,这会触发$digest循环。

您可以通过调用$scope.$apply()或使用内置处理程序(如ng-click)手动执行此操作。但对于此用例,您还可以使用$timeout服务代替setTimeout

请参阅API Reference/ng/$timeout

答案 1 :(得分:0)

强烈建议使用AngularJS $http服务与REST API进行通信,因为它负责为您更新模型。

它会返回一个可以链接successerror处理程序的承诺,因此它非常通用。

转换为您的代码,您可以拥有这样的服务:

angular.module('myApp', [])
    .factory('person', ['$http', function ($http) {
        return {
            create: function (data) {

                // Return promise
                return $http.post('/yourApiUrl', data);
            }
        };
    }]);

和这样的控制器:

angular.module('myApp', [])
    .controller('yourController', ['person', function (person) {

        $scope.company = {
            name: '',
            people: []
        };

        $scope.addPerson = function(data){

            //Save person to API
            person.create(data)

                // Success handler
                .success(function(data, status, headers, config) {

                    // Parse person from response (e.g. if your API 
                    // returns the created object after a POST)
                    var person = data;

                    // Add person to company
                    $scope.company.people.push(person)
                })

                // Error handler
                .error(function(data, status, headers, config) {

                    // Handle error
                });

        };

    }]);

通过从服务中返回承诺,您可以自定义控制器中的successerror处理程序,从而提供非常灵活的解决方案。

而且你不必创造自己的承诺。 AngularJS将为您处理这一切。

我希望有所帮助!

答案 2 :(得分:0)

您总是希望在范围上下文中调用deferred.resolve()和deferred.reject()。在您的情况下,您正在从角度框架外部调用代码(setTimeout,并且实际使用您在注释中提到的Azure移动服务的JS客户端API),因此您应该将dfd.resolve()包装在$ rootScope中。 $ apply()(不要忘记将$ rootScope注入你的服务)。

http://plnkr.co/edit/3sCHEjrtMdqCvFQ7ILTD

这个Plunkr展示了一个工作示例。如果你将$ rootScope。$ apply(dfd.resolve())更改回dfd.resolve(),它就会出现你在问题中描述的问题。

请注意,使用$ rootScope可能也更好。$ apply(function(){dfd.resolve()}),原因如下:

http://jimhoskins.com/2012/12/17/angularjs-and-apply.html