Angular JS:如何绑定到Promise


67

我试图将诺言与观点捆绑在一起。我不知道您是否可以直接执行此操作,但这就是我要尝试执行的操作。有什么想法我做错了吗?

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

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

编辑:解决方案:事实证明您可以直接绑定诺言。我的原始代码有两个问题:

  1. 使用setTimeout()代替angular的$ timeout是一个问题。Angular不知道触发超时时需要刷新UI(您可以在setTimeout内部使用$ scope。$ apply解决此问题,也可以只使用$ timeout)
  2. 绑定到返回诺言的函数是一个问题。如果第二次被召唤,它将带来另一个承诺。更好的方法是为承诺设置一个范围变量,并仅根据需要创建一个新的承诺。(就我而言,这是在国家/地区代码上调用$ 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
hugo der hungrige

2
有一点警告,您应该意识到:不仅UI仅在$scope.$apply被调用时才会更新,而且如果在$ apply之外解决了延迟的
请求,则不会

请注意,已接受的答案和/或问题中的解决方案不再起作用-请在下面查看我的答案。
本杰明·

Answers:


29

警告:此答案在编写时是准确的,但是从1.2版本开始,Angular模板引擎无法透明地处理承诺!- @马尔沃里奥

是的,模板引擎(和表达式)透明地处理了promise,但是我会将promise分配给控制器中的作用域属性,而不是每次都返回一个返回新promise的函数时调用(我认为这是您的问题,因为新的每次都会返回诺言)。

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字段。(当您更改国家/地区时,您的州列表也会更改)
Adam Tegen 2012年

我知道了,它能正常工作:jsfiddle.net/YQwaf/31更新您的答案,我会给你功劳:)
Adam

谢谢,我在小提琴中做了一些小修改(您可以像最初建议的那样直接使用Promise)
Guillaume86

嘿Guillaume86,我想您可以在此方面为我提供帮助。我的诺言解析为一个数组,并且更新该数组中的单个项目不会更新我的范围。 stackoverflow.com/questions/14313573/...
winduptoy

24
警告:此答案在编写时是准确的,但从1.2版本开始,Angular模板引擎无法透明地处理承诺
马尔沃里奥

70

从Angular 1.2开始,您不能再在模板中直接使用Promise
相反,您需要像往常一样将结果放入到$scope内部then-没有魔术。

作为解决旧问题的临时解决方法,您可以致电

$parseProvider.unwrapPromises(true)

但是此功能将在以后删除,因此请不要依赖它。


5
及时而有用的答案!在Angular的较旧版本的教程中,我只是勉强结束。
内森·泰勒

27

从Angular 1.3开始-$parseProvider.unwrapPromises(true) 将不再起作用

相反,您应该直接取消承诺:

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

请注意,promise解包仍将照常与ngResource一起使用。


有没有一种方法可以从promise设置范围值之后无需调用$ scope。$ apply()?(使用Angular 1.3)
Ryan Weiss

您不需要调用处理程序$scope.$apply()为您自动完成的操作then(如果没有摘要,则计划摘要)。
Benjamin Gruenbaum 2014年

3
好吧,不完全是,但是摘要仅在使用$ http方法(它具有特殊的处理程序)时才自动应用于IF。我最初使用的是jQuery的$ .ajax方法,这就是为什么在诺言返回后更改范围时它不起作用的原因。更改它以使用Angular的$ http解决了该问题。谢谢本杰明。
Ryan Weiss 2014年

8
@RyanWeiss并不特定于$http使用Angular promises($ q)创建的promise。如果您希望$ .ajax这样做,可以将它转换为by $q.when(yourPromise)。无论如何,最好将$ http用于角度的http请求。
本杰明·格林巴姆2014年

0

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

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;
    };
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.