13

angular.js を使用して複数のフォーム送信を防止したい。質問はこの質問に関連しています。

ユーザーがフォーム送信ボタンをクリックすると、送信ボタンの値/ラベルが「読み込み中..」に変わり、ボタンのステータスが無効に設定され、送信イベントが通常の方法でトリガーされ、サーバーに呼び出しを送信します。このようにして、ユーザーは次の効果を確認できます。

  1. すぐに: 送信ボタンの値が「loading..」に変わり、無効になります

  2. サーバーが応答するとすぐに: ユーザーはサーバー要求の結果を提示されます (一方、サーバーの応答は角度の介入なしで処理されます)。

私が何を意味するかを示すために、このプランクを作成しました。私の問題は次の行に関連しています: elm.attr('disabled',true);. これにより、ボタンが無効になるだけでなく、submit イベントの伝播も妨げられます。したがって、無効なボタン (望ましい結果) が表示されますが、フォームは送信されません (望ましくない結果)。

この行をコメント化/コメント解除すると、動作の変化を確認できます。elm.attr('disabled',true);

これを変更する方法はありますか?

4

9 に答える 9

21

私は標準的なフォームを持っており、フロントエンドでAngularを使用しているだけなので、サーバーが応答している間にボタンが2回クリックされるのを防ぐ必要がある場合は、再利用可能でコントローラーや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 送信ではなく標準フォーム送信を行っている場合にのみ機能します。

于 2013-11-07T00:12:53.753 に答える
15

コントローラーに新しいプロパティを追加するだけです

$scope.processing = false;

あなたの方法では

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

HTML で ng-disabled 属性を $scope.processing プロパティにバインドして、ボタンを無効にし、メソッドの処理中にテキストを表示します。

于 2014-07-13T21:29:35.290 に答える
11

$timeout (angularjs 関数) を試してください

$timeout(function(){
    elm.attr('disabled',true);
}, 0)
于 2013-04-04T09:45:27.677 に答える
6

代替(柔軟でシンプルな)ソリューション(インスピレーション):スコープ変数を設定する送信コードのラッパー関数。実際の例を参照してください。

コントローラーでの使用法:

$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;
    });
  };
}
于 2014-07-13T20:11:24.497 に答える
1

私も最終的に指令ルートに行きました。以下は ng-click の代わりに使用され、渡された関数が promise を返すことを期待します (restangular はそうします)。promise が解決される (応答が返される) と、その後の提出が許可されます。これを微調整して、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 });
          });
        });
      };
    }
  };
});
于 2014-06-24T17:45:24.063 に答える
1

これは、単純な論理チェックで同様のことを行う簡単な方法です。テキストの変更も同様の方法で行うことができます。

<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');
    }
于 2015-12-22T17:46:57.390 に答える
0

$http インターセプターを使用してすべての AJAX リクエストに対して行う一般的な方法を次に示します。/api/ から始まるすべての REST ルートがある場合:

     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" を含むボタンがブロックされることです。

于 2016-11-29T15:58:29.000 に答える