35

升级到 1.2 后,我的服务返回的承诺表现不同......简单的服务 myDates:

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

            $http.get(aGoodURL).
                 success(function (data, status, headers, config) {
                     deferred.resolve(data);  // we get to here fine.
            })......

在早期版本中,我可以在我的控制器中执行以下操作:

$scope.theDates = myDates.getDates();

从 getDates 返回的承诺可以直接绑定到 Select 元素。现在这不起作用,我不得不在我的控制器中对 promise 提供回调,否则数据不会绑定:

$scope.theDates = matchDates.getDates();
$scope.theDates.then(function (data) {
      $scope.theDates = data;  // this wasn't necessary in the past

文档仍然说:

$q promises 被 Angular 模板引擎识别,这意味着在模板中,您可以将附加到范围的 Promise 视为结果值。

他们(承诺)在旧版本的 Angular 中工作,但在 1.2 RC3 中,自动绑定在我所有的简单服务中都失败了......关于我可能做错了什么的任何想法。

4

4 回答 4

40

1.2.0-rc3 有一些变化,包括你提到的一个:

AngularJS 1.2.0-rc3 ferocious-twitch修复了 $compile 和 $animate 中的一些高优先级问题,并为 1.2 铺平了道路。此版本还引入了一些重要的重大更改,在某些情况下可能会破坏您的指令和模板。请务必阅读更改日志以了解这些更改并了解如何在需要时迁移您的代码。有关此版本的完整详细信息,请参阅更改日志

更改日志中有描述:

$解析:

  • 由于5dc35b52, $parse 和 templates 一般将不再自动解包 promises。此功能已被弃用,如果绝对需要,可以在过渡期间通过$parseProvider.unwrapPromises(true)api 重新启用它。
  • 由于b6a37d11,在 rc.2 中添加的功能,如果值是承诺,则从函数中解包返回值(如果启用了承诺解包 - 请参阅前一点),由于打破了流行的使用模式,该功能被恢复。
于 2013-10-19T22:58:50.433 回答
22

正如@Nenad 所指出的,承诺不再自动取消引用。这是我见过的最奇怪的决定之一,因为它默默地删除了我所依赖的功能(这对我来说是 angular 的独特卖点之一,少即是多)。所以我花了相当多的时间来弄清楚这一点。特别是因为 $resource 框架似乎仍然可以正常工作。最重要的是,这也是一个候选版本。如果他们真的不得不反对这一点(这些论点听起来很微弱),他们至少可以给出一个宽限期,在他们默默地关闭它之前有警告。虽然通常对角度印象深刻,但这是一个很大的缺点。如果这实际上会被恢复,我不会感到惊讶,尽管到目前为止似乎相对较少的抗议。

反正。有哪些解决方案?

  • 始终使用 then(),并在 then 方法中分配 $scope

    function Ctrl($scope) {
       foo().then( function(d) { $scope.d = d; });
    )
    
  • 通过 unwrap 函数调用该值。该函数返回 promise 中的一个字段,并通过 then 方法设置该字段。因此,只要未解决承诺,它将是未定义的。

    $rootScope.unwrap = function (v) {
      if (v && v.then) {
        var p = v;
        if (!('$$v' in v)) {
          p.$$v = undefined;
          p.then(function(val) { p.$$v = val; });
        }
        v = v.$$v;
      }
      return v;
    };
    

    您现在可以调用它:

    Hello {{ unwrap(world) }}.
    

    这是来自http://plnkr.co/edit/Fn7z3g?p=preview没有与之关联的名称。

  • 设置$parseProvider.unwrapPromises(true)并使用这些消息,您可以将其关闭,$parseProvider.logPromiseWarnings(false)但最好注意它们可能会在下一个版本中删除该功能。

叹息,40 年 Smalltalk 的become消息允许您切换对象引用。承诺,因为他们本来可以......

更新:

更改我的应用程序后,我发现了一个运行良好的通用模式。

假设我需要对象'x'并且有一些方法可以远程获取这个对象。然后,我将首先检查缓存中的“x”。如果有一个对象,我会返回它。如果不存在这样的对象,我将创建一个实际的空对象。不幸的是,这需要您知道这是一个数组还是一个哈希/对象。我将此对象放在缓存中,以便将来的调用可以使用它。然后我开始远程调用,并在回调中将从远程系统获得的数据复制到创建的对象中。缓存确保对 get 方法的重复调用不会为同一对象创建大量远程调用。

 function getX() {
   var x = cache.get('x');
   if ( x == undefined) {
      cache.put('x', x={});
      remote.getX().then( function(d) { angular.copy(d,x); } );
   }
   return x;
 }

另一种选择是为 get 方法提供对象的目的地:

 function getX(scope,name) {
   remote.getX().then( function(d) { 
       scope[name] = d;
   } );
 }       
于 2014-01-14T15:36:47.453 回答
3

你总是可以创建一个 Common Angular 服务并在其中放置一个 unwrap 方法,以重新创建旧承诺的工作方式。这是一个示例方法:

var shared = angular.module("shared");

shared.service("Common", [
    function () {

        // [Unwrap] will return a value to the scope which is automatially updated. For example,
        //      you can pass the second argument an ng-resource call or promise, and when the result comes back
        //      it will update the first argument. You can also pass a function that returns an ng-resource or
        //      promise and it will extend the first argument to contain a new "load()" method which can make the
        //      call again. The first argument should either be an object (like {}) or an array (like []) based on
        //      the expected return value of the promise.
        // Usage: $scope.reminders = Common.unwrap([], Reminders.query().$promise);
        // Usage: $scope.reminders = Common.unwrap([], Reminders.query());
        // Usage: $scope.reminders = Common.unwrap([], function() { return Reminders.query(); });
        // Usage: $scope.reminders.load();
        this.unwrap = function(result, func) {
            if (!result || !func) return result;

            var then = function(promise) {
                //see if they sent a resource
                if ('$promise' in promise) {
                    promise.$promise.then(update);
                }
                //see if they sent a promise directly
                else if ('then' in promise) {
                    promise.then(update);
                }
            };

            var update = function(data) {
                if ($.isArray(result)) {
                    //clear result list
                    result.length = 0;
                    //populate result list with data
                    $.each(data, function(i, item) {
                        result.push(item);
                    });
                } else {
                    //clear result object
                    for (var prop in result) {
                        if (prop !== 'load') delete result[prop];
                    }
                    //deep populate result object from data
                    $.extend(true, result, data);
                }
            };

            //see if they sent a function that returns a promise, or a promise itself
            if ($.isFunction(func)) {
                // create load event for reuse
                result.load = function() {
                    then(func());
                };
                result.load();
            } else {
                then(func);
            }

            return result;
        };
    }
]);

这基本上适用于旧承诺的执行方式和自动解决方式。但是,如果第二个参数是一个函数,它还有一个额外的好处,那就是添加一个“.load()”方法,该方法可以将值重新加载到作用域中。

angular.module('site').controller("homeController", function(Common) {
    $scope.reminders = Common.unwrap([], Reminders.query().$promise);
    $scope.reminders = Common.unwrap([], Reminders.query());
    $scope.reminders = Common.unwrap([], function() { return Reminders.query(); });
    function refresh() {
        $scope.reminders.load();
    }
});
于 2014-01-28T19:41:24.747 回答
0

这些是一些很好的答案,当我升级 Angular 并且我的承诺自动解包停止工作时,它们帮助我找到了我的问题。

冒着被 Peter Kriens 多余的风险,我发现这种模式对我有用(这是一个简单的例子,将一些名人的名言简单地放在一个页面上)。

我的控制器:

angular.module('myModuleName').controller('welcomeController',
function ($scope, myDataServiceUsingResourceOrHttp) {

    myDataServiceUsingResourceOrHttp.getQuotes(3).then(function (quotes) { $scope.quotes = quotes; });
}
);

我的页面:

...
<div class="main-content" ng-controller="welcomeController">
...
<div class="widget-main">
    <div class="row" ng-repeat="quote in quotes">
        <div class="col-xs-12">
            <blockquote class="pull-right">
                <p>{{quote.text}}</p>
                <small>{{quote.source}}</small>
            </blockquote>
                </div>
    </div>
</div>
...
于 2014-05-30T16:56:51.547 回答