Angular JS: как привязываться к promises

Я пытаюсь связать обещание с представлением. Я не знаю, можете ли вы сделать это напрямую, но это то, что я пытаюсь сделать. Любые идеи, что я делаю неправильно?

Примечание: источник немного надуман с таймаутом и использует статические данные, но это облегчает диагностику кода.

EDIT: Страница JSFiddle: http://jsfiddle.net/YQwaf/27/

РЕДАКТИРОВАТЬ: РЕШЕНИЕ: Оказалось, что может напрямую связывать promises. У меня было две проблемы с моим исходным кодом:

  • Использование setTimeout() вместо angular $timeout было проблемой. angular не знает, что ему нужно обновить пользовательский интерфейс при запуске таймаута (вы можете решить это с помощью $scope. $apply внутри setTimeout или просто использовать $timeout)
  • Привязка к функции, которая вернула обещание, была проблемой. Если он будет вызван во второй раз, это еще одно обещание. Лучше всего установить переменную области видимости и при необходимости создать новое обещание. (В моем случае это вызывало $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;
    };
}

Ответ 1

ПРЕДУПРЕЖДЕНИЕ: этот ответ был точным, когда он был написан, но с 1.2 механизм шаблона Angular не обрабатывает прозрачно promises! - @Malvolio

Да, механизм шаблонов (и выражений) обрабатывает прозрачно, но я бы назначил обещание свойства scope в контроллере и не вызывал каждый раз функцию, которая возвращает новое обещание (я думаю, это ваша проблема, потеряно, потому что новое обещание возвращается каждый раз).

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 = [];
        }
    });
}​

Ответ 2

Начиная с Angular 1.2, вы больше не можете использовать promises в шаблонах.
Вместо этого вам нужно поместить результат в $scope внутри then, как обычно, не было бы волшебства.

В качестве временного обходного пути для получения старого поведения вы можете позвонить

$parseProvider.unwrapPromises(true)

но эта функция будет удалена позже, поэтому не зависеть от нее.

Ответ 3

От Angular 1.3 - $parseProvider.unwrapPromises(true) больше не будет работать.

Вместо этого вы должны развернуть promises напрямую:

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

Обратите внимание, что обезвреживание обещаний по-прежнему будет работать с ngResource, как обычно.