【问题标题】:AngularJS Scopes and DeferredsAngularJS 范围和延迟
【发布时间】:2013-04-14 17:01:30
【问题描述】:

我有一个服务,它做一些困难的事情并返回一个承诺:

.factory('myService', function($q) {

    return {
        doSomethingHard: function() {
            var deferred = $q.defer();

            setTimeout(function() {
                deferred.resolve("I'm done!");
            }, 1000);

            return deferred.promise;
        }
    };
})

我有一个控制器,它使用该服务将功能添加到范围:

.controller('MyCtrl', function($scope, myService) {

    $scope.doSomething = function() {
        var promise = myService.doSomethingHard();

        promise.then(function(result) {
            alert(result);
        });
    };

})

我使用指令通过解析属性来调用该控制器函数:

.directive('myDirective', function($parse) {
    return {
        link: function(scope, el, attr) {

            var myParsedFunction = $parse(attr.myDirective);

            el.bind('click', function() {
                myParsedFunction(scope);
            });
        }
    };
})

使用模板

<div ng-controller="MyCtrl">
    <button my-directive="doSomething()">The Button</button>
</div>

点击按钮触发事件监听器,它调用控制器函数doSomething,它调用服务函数doSomethingHard,它返回一个promise,THAT IS NEVER RESOLVED。

一切都在这里:

http://jsfiddle.net/nicholasstephan/RgKaT/

什么给了?

谢谢。

编辑:感谢 Maksym H.,看起来在 $scope.$apply() 中包装承诺解析会使其在控制器中触发。我有一个工作小提琴http://jsfiddle.net/RgKaT/4/。但我真的很想把范围排除在我的服务之外。

我也很想知道为什么会这样。或者更好的是,为什么它在包裹在范围内时如果不解决承诺就不起作用。当考虑属性的变化需要消化时,整个 Angular 世界与常规 Javascript 世界的类比是有意义的,但这是一个承诺……带有回调函数。 $q 是否只是将承诺标记为已解决并等待范围消化该属性更新并触发其已解决的处理程序函数?

【问题讨论】:

    标签: angularjs angularjs-directive promise angularjs-scope


    【解决方案1】:

    这是另一种方式:尝试在指令中定义作用域并将此属性绑定到预期的父作用域。

    .directive('myDirective', function() {
      return {
        scope: { myDirective: "=" }, // or { myParsedFunction: "=myDirective" },
        link: function(scope, el, attr) {
    
            el.bind('click', function() {
                scope.myDirecive(scope); // or scope.myParsedFunction(scope)
            });
        }
      };
    })
    

    但主要是在一段时间后解决它时运行摘要:

    .factory('myService', function($q, $timeout) {
    
        return {
            doSomethingHard: function() {
                alert('3. doing something hard');
    
                var deferred = $q.defer();
    
                // using $timeout as it's working better with promises
                $timeout(function() {
                    alert('4. resolving deferred');
                    deferred.resolve('Hello World!'); // Here...
                }, 1000);
    
                return deferred.promise;
            }
        };
    })
    

    jsFiddle

    附:确保您将方法作为父范围的模型传递,而不是通过 HTML 中的“()”应用它

    &lt;button my-directive="doSomething"&gt;Button&lt;/button&gt;

    【讨论】:

    • @nicholas,我已经更新了 Plunker 上的代码。我希望它对你有用。
    • 很棒的答案。谢谢。我已经按照您的建议将 $rootScope 添加到服务中。谢谢。但我仍然对那个解决方案不太满意,也不明白(见我的编辑)。
    • @nicholas,你不用担心,因为这是原生 setTimeout 的错。通过使用它,agnular 似乎不再关注解析。在实际示例中,您可能会使用 XHR erquest 之类的东西并解决成功,对吗?但是现在您可以使用 Angular 的 $timeout 提供程序对其进行测试,该提供程序可以更好地使用 Angular Promise。您也可以尝试使用 XHR 请求来解决,它应该可以工作。这是更新的小提琴(jsfiddle.net/RgKaT/6)
    • 啊。这有点道理。解析一个属性,这样我就不必为指令jsfiddle.net/jYZR9/1 创建一个独立的范围,也可以。在我的生产代码中,除了使用 $http 而不是 $timeout 之外,它是相同的,我仍然必须编写 $scope.$apply(fn) 而不仅仅是 fn(),这有点奇怪,但问题一定源于某个地方别的。我会尝试为自己的教育而复制小提琴,但现在它正在工作,这对我来说已经足够了。谢谢。
    • 即使 promise 没有解决,这个返回是否会在 1 秒后完成?
    【解决方案2】:

    只需将setTimeout 替换为$timeout(并记住将$timeout 注入您的服务)。这是updated jsFiddle。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-07-01
      • 2015-03-08
      • 1970-01-01
      • 2018-01-09
      • 1970-01-01
      • 2023-03-03
      • 2017-03-02
      相关资源
      最近更新 更多