Каков наилучший способ дождаться, когда обещание, инициированное директивой, будет разрешено?

Это простая форма, которая содержит одно поле ввода с директивой. Если ввод действителен, директива вызовет удаленный сервер. Ответ заполнит модель формы некоторыми дополнительными данными.

Как дождаться выполнения обещания при отправке формы?

Вот пример кода, если это поможет:

app.directive('getSecretStuff', function($q, $http) {

    function get_some_secret_stuff( value ) {

        var deferred = $q.defer();

        $http.get(url)
        .success(function (response) {
            deferred.resolve(response.secret_stuff);
        });

        return deferred.promise;

    };

    return {
        restrict: 'A',
        require: '^form',
        scope: {
            secret_stuff: '=stuff',
        },
        link: function(scope, element, attrs, ctrl) {
            scope.$watch(
                function() { return ctrl[ element.attr('name') ].$valid; },
                function (validity) {
                    if (validity) {
                        get_some_secret_stuff( element.val() ).then(function( stuff ) {
                            scope.secret_stuff = stuff;
                        });
                    }
                }
            );

        },
    };
});

person Mauro    schedule 28.11.2015    source источник


Ответы (1)


(Неправильное) использование асинхронных валидаторов для отключения отправки формы до тех пор, пока обещание не будет разрешено

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

HTML

<div ng-app="myApp">
    <div ng-controller="ctrlr">
        <form name="form" novalidate ng-submit="!form.$pending && submit()">
            <!-- our validaty check is just a sample - check for numbers -->
            <input ng-model="publicStuff" name="publicStuff" get-secret-stuff stuff="secretStuff" ng-pattern="/^[0-9]+$/" />
            <input ng-model="secretStuff" />
            <button type="submit">Go</button>
        </form>
    </div>
</div>

Скрипт

angular.module('myApp', [])
    .controller('ctrlr', function ($scope) {
    })
    .directive('getSecretStuff', function ($q, $timeout) {
        function get_some_secret_stuff(value) {
            var deferred = $q.defer();
            // for illustration, we use the $timeout instead of $http 
            // swap this out with the actual $http call in your code
            $timeout(function () {
                deferred.resolve('secret value for ' + value);
            }, 2000)
            return deferred.promise;
        };

        // a promise that resolves immediately
        function get_dummy_promise() {
            var deferred = $q.defer();
            console.log('asd')
            deferred.resolve('1');
            return deferred.promise;
        };

        return {
            restrict: 'A',
            require: ['^form', 'ngModel'],
            scope: {
                secret_stuff: '=stuff',
            },
            link: function (scope, element, attrs, ctrlrs) {
                // we misuse the async validator to... 
                ctrlrs[1].$asyncValidators.secretStuff = function (modelValue, viewValue) {
                    if (viewValue)
                        return get_some_secret_stuff(viewValue).then(function (response) {
                            // ...do work other than validation
                            scope.secret_stuff = response;
                            return true;
                        });
                    else
                        // this is just for the first run when the validator gets called even if $valid is not set
                        return get_dummy_promise().then(function (response) {
                            scope.secret_stuff = undefined;
                            return true;
                        });
                };

                // we probably want to clear out the secret stuff for invalid values
                scope.$watch(
                    function () { return ctrlrs[0][element.attr('name')].$valid; },
                    function (validity) {
                        if (!validity) {
                            scope.secret_stuff = undefined;
                        }
                    }
                );
            },
        };
    });
person potatopeelings    schedule 28.11.2015
comment
Это очень хороший подход к ответу. Однако я хочу, чтобы кнопка отправки оставалась активной до выполнения запроса. Если это действительно произойдет, приложение должно дождаться завершения запроса директивы (!form.$pending в вашем коде), а затем запустить запрос POST (я сделаю это с бесконечным циклом с $timeout в нем. Но мой вопрос на самом деле был больше о канал, чтобы уведомить контроллер о выполнении запроса, и form.$pending выглядит достаточно подходящим Пожалуйста, обновите свой ответ моим предложением выше, поэтому я принимаю его! - person Mauro; 30.11.2015
comment
Удален ng-disabled на кнопке отправки! - person potatopeelings; 01.12.2015