Предотвращать появление нескольких форм с помощью angular.js - отключить кнопку формы

Я хочу предотвратить появление нескольких форм с помощью angular.js. Вопрос связан с этим question.

Когда пользователь нажимает кнопку отправки формы, значение/метка кнопки отправки должно быть изменено на "загрузка..", статус кнопки будет отключен, а событие отправки должно запускаться обычным способом, что приведет к отправке вызова на сервер. Таким образом, пользователь увидит следующий эффект:

  • Сразу: значение кнопки отправки изменяется на "загрузка.." и отключается

  • Как только сервер отвечает: пользователь получает результат запроса сервера (тогда как ответ сервера обрабатывается без вмешательства angular)

Я создал этот plunk, чтобы показать, что я имею в виду. Моя проблема относится к этой строке: elm.attr('disabled',true);. Это не только отключает кнопку, но также предотвращает распространение события отправки. Таким образом, я получаю отключенную кнопку (желаемый результат), но форма не представляется (нежелательный результат).

Вы можете увидеть изменение поведения, если вы прокомментируете/раскомментируете эту строку: elm.attr('disabled',true);

Любая идея, как изменить это?

Ответ 1

Попробуйте $timeout (функция angularjs)

$timeout(function(){
    elm.attr('disabled',true);
}, 0)

Ответ 2

У меня стандартная форма и просто использую angular в интерфейсе, поэтому, если вам просто нужно дважды нажать кнопку, пока сервер отвечает, тогда вы можете использовать эту простую директиву, которая повторно используется и не требует контроллера или ngModel.

http://plnkr.co/edit/2aZWQSLS8s6EhO5rKnRh?p=preview

app.directive('clickOnce', function($timeout) {
    return {
        restrict: 'A',
        link: function(scope, element, attrs) {
            var replacementText = attrs.clickOnce;

            element.bind('click', function() {
                $timeout(function() {
                    if (replacementText) {
                        element.html(replacementText);
                    }
                    element.attr('disabled', true);
                }, 0);
            });
        }
    };
});

Он отключит кнопку и, возможно, изменит текст кнопки. Используйте так:

<button click-once>Button just disables</button>
<button click-once="Loading...">Text changes and button disables</button>

В текущей форме это будет работать только в том случае, если вы делаете стандартные формы и не представляете ajax.

Ответ 3

Просто добавьте новое свойство в свой контроллер

$scope.processing = false;

В вашем методе

$scope.processData = function(){
    $scope.processing = true;
    $http.post('').then(function(){
        $scope.processing = false;
    });
});

В свой html bind ng-disabled атрибут свойства $scope.processing отключить кнопку и показать текст во время обработки метода.

Ответ 4

Альтернативное (гибкое и простое) решение (вдохновение): оберточная функция вокруг кода отправки, который устанавливает переменную области видимости. См. живой пример.

Использование в контроллере:

$scope.submit = mutexAction($scope, 'sending', submit);

в поле зрения:

<form ng-submit="submit()">
  ...
  <button ng-disabled="sending">
    {{sending ? "Sending..." : "Send"}}
  </button>
</form>

Функция (поместите ее в службу):

function mutexAction(scope, semaphoreName, action) {
  return function() {
    if (scope[semaphoreName]) {
      // not queuing action, forget it!
      return;
    }
    scope[semaphoreName] = true;
    action()['finally'](function () {
      scope[semaphoreName] = false;
    });
  };
}

Ответ 5

В дополнение к spenthil ответ, вариант в кофе script +, вы можете включить кнопку, если вам нужно (например, когда валидация формы не удалась и вы хотите попробовать еще раз)

class ClickOnceDirective

    constructor: (@$timeout) ->
        link = (scope, element, attrs) =>

            originalText = element.html()
            replacementText = attrs.clickOnce

            element.bind('click', =>
                @$timeout ->

                    if (replacementText)
                        element.html(replacementText)

                    element.attr('disabled', true)

                    # enable back
                    @$timeout ->
                         element.attr('disabled', false)
                        if (replacementText)
                             element.html(originalText)
                    , 500

                , 0)

         return {
         link
         restrict: 'A'
        }

directivesModule.directive 'clickOnce', ['$timeout', ClickOnceDirective]

Ответ 6

В конце концов я тоже пошел по директивному маршруту. Вместо ng-click используется следующее, и ожидается, что переданная функция вернет обещание (что делает ретунгал). Когда обещание будет разрешено (ответ возвращается), он будет давать последующие сообщения. Также можно настроить это, чтобы добавить/удалить ng-disabled.

// copied from ngClick and modified to provide single click only behavior
// expects function to return promise
app.directive('srMutexClick', function ($parse) {
  return {
    compile: function ($element, attr) {
      var fn = $parse(attr['srMutexClick']);
      return function srEventHandler(scope, element) {
        var submitting = false;
        element.on('click', function (event) {
          scope.$apply(function () {
            if (submitting) {
              return
            }
            submitting = true;
            // `submitting` is reset when promise is resolved
            fn(scope, {$event: event}).finally(function() { submitting = false });
          });
        });
      };
    }
  };
});

Ответ 7

вот простой способ сделать подобный материал с помощью простых логических проверок, изменение текста также можно сделать аналогичным образом.

<button type="submit" class="btn btn-success btn-lg btn-block"  ng-disabled="!userForm.$valid || isValid">Submit!</button>


 $scope.isValid = false;
    $scope.submitForm = function () {
        $scope.isValid = true;
        console.log('submit');
    }

Ответ 8

Самый простой и наиболее распространенный способ сделать это - полагаться на свойство $submitted объекта формы следующим образом:

<form name="formName" ng-submit="submit()">
  ...
  <button type="submit" ng-disabled="formName.$submitted">Submit</button>
</form>

Ответ 9

Вот общий способ сделать это для всех запросов AJAX с использованием перехватчиков $http. Если у вас есть все маршруты REST, начиная с /api/then:

     angular.module('yourapp').factory('loadingInterceptor',['$q','$rootScope',function($q,$rootScope) {
         var apiRe = /^\/api\//;
    return {
        request: function(config) {
            if (config.url.match(apiRe)) {
                $rootScope.loading = true;
            }
            config.headers = config.headers || {};

            return config;
        },
        response: function(res) {
            $rootScope.loading = false;
            return $q.resolve(res);
        },

        'responseError': function(rejection) {
            $rootScope.loading = false;
            return $q.reject(rejection);
        }
    };
}]);


angular.module('yourapp').config(['$httpProvider', function($httpProvider) {
    $httpProvider.interceptors.push('loadingInterceptor');
}]);

С помощью перехватчика вам не придется помещать $scope.isLoading в каждый контроллер. Недостатком является то, что любая кнопка с ng-disabled = "loading" будет заблокирована во время запроса.