【问题标题】:Angular JS: how to bind to promisesAngular JS:如何绑定到 Promise
【发布时间】:2012-10-13 13:01:50
【问题描述】:

我正在尝试将承诺绑定到视图。我不知道你是否可以直接这样做,但这就是我正在尝试做的。任何想法我做错了什么?

注意:源有点做作超时并使用静态数据,但这是为了使代码更易于诊断。

编辑: JSFiddle 页面:http://jsfiddle.net/YQwaf/27/

编辑:解决方案:原来你可以直接绑定承诺。我的原始代码有两个问题:

  1. 使用 setTimeout() 代替 Angular 的 $timeout 是个问题。 Angular 不知道它需要在触发超时时刷新 UI(你可以在 setTimeout 中使用 $scope.$apply 来解决这个问题,或者你可以只使用 $timeout)
  2. 绑定到返回承诺的函数是一个问题。如果它被第二次调用,它会做出另一个承诺。更好的是为 promise 设置一个范围变量,并且只根据需要创建一个新的 promise。 (就我而言,这是在国家代码上调用 $scope.$watch)

HTML:

<div ng:controller="addressValidationController">
    Region Code <select ng:model="regionCode" ng:options="r.code as r.name for r in getRegions()"/>
    Country Code<select ng:model="countryCode"><option value="US">United States</option><option value="CA">Canada</option></select>
</div>

JS:

function addressValidationController($scope, $q) {
    var regions = {
        US: [{code: 'WI',name: 'Wisconsin'}, {code: 'MN',name: 'Minnesota'}], 
        CA: [{code: 'ON',name: 'Ontario'}]
    };
    $scope.getRegions = function () {
        var deferred = $q.defer();
        setTimeout(function () {
            var countryRegions = regions[$scope.countryCode];
            console.log(countryRegions);
            if(countryRegions === undefined) {
                deferred.resolve([]);
            } else {
                deferred.resolve(countryRegions);
            }
        }, 1000);
        return deferred.promise;
    };
}

【问题讨论】:

  • 你能做一个工作小提琴吗?那太好了!
  • 其实文档里有:docs.angularjs.org/api/ng.$q
  • 您应该注意一点:不仅 UI 仅在调用 $scope.$apply 时更新,而且如果延迟在 $ 之外解决,则不会调用在 promise 上注册的每个回调申请:)
  • 请注意,接受的答案和/或问题中的解决方案不再有效 - 请参阅下面的答案。

标签: angularjs promise


【解决方案1】:

警告:这个答案在编写时是准确的,但是从 1.2 开始,Angular 模板引擎不能透明地处理承诺! -- @Malvolio

是的,模板引擎(和表达式)透明地处理承诺,但我会将承诺分配给控制器中的范围属性,而不是每次都调用返回新承诺的函数(我认为这是你的问题,已解决的承诺丢失了因为每次都会返回一个新的承诺)。

JSFiddle:http://jsfiddle.net/YQwaf/36/

HTML:

<div ng:controller="addressValidationController">
    Region Code <select ng:model="regionCode" ng:options="r.code as r.name for r in regions"/>
    Country Code<select ng:model="countryCode"><option value="US">United States</option><option value="CA">Canada</option></select>
</div>

JS:

function addressValidationController($scope, $q, $timeout) {
    var regions = {
        US: [{
            code: 'WI',
            name: 'Wisconsin'},
        {
            code: 'MN',
            name: 'Minnesota'}],
        CA: [{
            code: 'ON',
            name: 'Ontario'}]
    };

    function getRegions(countryCode) {
        console.log('getRegions: ' + countryCode);
        var deferred = $q.defer();
        $timeout(function() {
            var countryRegions = regions[countryCode];
            if (countryRegions === undefined) {
                console.log('resolve empty');
                deferred.resolve([]);
            } else {
                console.log('resolve');
                deferred.resolve(countryRegions);
            }
        }, 1000);
        return deferred.promise;
    };

    $scope.regions = [];

    // Manage country changes:
    $scope.$watch('countryCode', function(countryCode) {
        if (angular.isDefined(countryCode)) {
            $scope.regions = getRegions(countryCode);
        }
        else {
            $scope.regions = [];
        }
    });
}​

【讨论】:

  • 问题是区域依赖于 countryCode 字段。 (当您更改国家/地区时,您的州列表会更改)
  • 我得到了它的工作:jsfiddle.net/YQwaf/31 更新你的答案,我会给你信用:)
  • 谢谢,我在小提琴中做了一些小修改(你可以像最初建议的那样直接使用promise)
  • 嘿 Guillaume86,我想你可以帮我解决这个问题。我的承诺解析为一个数组,更新该数组中的单个项目不会更新我的范围。 stackoverflow.com/questions/14313573/…
  • 警告:这个答案在编写时是准确的,但截至 1.2 the Angular template engine does not handle promises transparently!
【解决方案2】:

从 Angular 1.2 开始,you can't use promises in templates directly anymore.
相反,您需要将结果放入 then 内的 $scope 中,就像您通常所做的那样 - 没有魔法。

作为获得旧行为的临时解决方法,您可以调用

$parseProvider.unwrapPromises(true)

但是这个功能稍后会被移除,所以不要依赖它。

【讨论】:

  • 及时而有帮助的答案!在旧版本 Angular 的教程中,我几乎没有死机。
【解决方案3】:

从 Angular 1.3 开始 - $parseProvider.unwrapPromises(true) will no longer work

相反,您应该直接解开承诺:

myApiMethod().then(function(value){
    $scope.item = value; 
});

注意,promise unwrapping 仍然可以像往常一样使用 ngResource。

【讨论】:

  • 有没有办法做到这一点,而无需在从承诺设置范围值后调用 $scope.$apply() ? (使用 Angular 1.3)
  • 您无需调用$scope.$apply(),它由then 处理程序自动为您完成(如果没有正在进行的摘要,则安排摘要)。
  • 嗯,不完全是,但是如果并且仅当您使用 $http 方法(它有一个特殊的处理程序来执行此操作)时,才会自动应用摘要。我最初使用的是 jQuery 的 $.ajax 方法,这就是为什么当我在 promise 返回后更改范围时它不起作用的原因。将其更改为使用 Angular 的 $http 解决了该问题。谢谢本杰明。
  • @RyanWeiss 它不是特定于 $http 它特定于使用 Angular 承诺 ($q) 创建的承诺。如果您希望 $.ajax 这样做,您可以通过$q.when(yourPromise) 将其转换为 1。无论如何,最好将 $http 用于 Angular 的 http 请求。
【解决方案4】:

返回对保存列表的范围变量的引用就足够了。

function addressValidationController($scope,$timeout) {
    var regions = {
        US: [{code: 'WI',name: 'Wisconsin'}, {code: 'MN',name: 'Minnesota'}], 
        CA: [{code: 'ON',name: 'Ontario'}]
    };

    $scope._regions = [];

    $scope.getRegions = function () {

        $timeout(function () {
            var countryRegions = regions[$scope.countryCode];
            console.log(countryRegions);
            if(countryRegions === undefined) {
                $scope._regions = []
            } else {
                $scope._regions = countryRegions
            }
        }, 1000);

        return $scope._regions;
    };
}

【讨论】:

    猜你喜欢
    • 2014-12-02
    • 2018-04-18
    • 1970-01-01
    • 2017-05-24
    • 2012-09-07
    • 1970-01-01
    • 1970-01-01
    • 2015-07-26
    相关资源
    最近更新 更多