7

メールフィールドのフォーム検証を実装しようとしています。検証は次のように行う必要があります。

  • 必要な属性にメールが入力されているかどうかを確認し、メールが入力されていない場合はメッセージを表示します
  • 電子メールの形式が有効かどうかを確認し(属性を指定せずに自動的に実行されるようです)、形式が間違っている場合はメッセージを表示します
  • $ http.get呼び出しで電子メールが一意であるかどうかを確認し、電子メールが見つかったために使用できない場合にメッセージを表示します

最初のメッセージが表示され、フィールドが空の場合、2番目のメッセージが表示され、電子メールが無効である場合、3番目のメッセージが表示されます。電子メールアドレスが見つかったため、一度に1つずつ一意ではありません。

これは、「required」属性のみで試してみると機能しますが、email-availableディレクティブ属性を追加するとすぐに、電子メールの形式がチェックされなくなり、email-availableディレクティブがrequired属性と一緒に実行されます。両方のメッセージがポップアップ表示されますが、ユーザーに一度に1つのメッセージだけが表示されるようにしたいと思います。

私はangularjs1.1.3を使用しています。

誰かが私が間違っているかもしれないことを教えてもらえますか?

HTML

<div id="user_mod" class="mod_form" ng-show="userModScreenIsVisible">
<form name="mod_user" novalidate>
    <input type="email" name="email" ng-model="user.email" placeholder="E-Mail" required email-available/>
    <div class="user-help" ng-show="mod_user.email.$dirty && mod_user.email.$invalid">Invalid:
        <span ng-show="mod_user.email.$error.required">Please enter your email.</span>
        <span ng-show="mod_user.email.$error.email">This is not a valid email.</span>
        <span ng-show="mod_user.email.$error.emailAvailable">This email address is already taken.</span>
    </div>
</form>

指令

directive('emailAvailable', function($http) { // available
    return {
        require: 'ngModel',
        link: function(scope, elem, attr, ctrl) {
            ctrl.$parsers.unshift(function(viewValue) {
                ctrl.$setValidity('emailAvailable', false);
                if(viewValue !== "" && typeof viewValue !== "undefined") {
                    console.log("variable is defined");

                    $http.get('/api/user/email/' + viewValue + '/available')
                        .success(function(data, status, headers, config) {
                            console.log(status);
                            ctrl.$setValidity('emailAvailable', true);
                            return viewValue;
                        })
                        .error(function(data, status, headers, config) {
                            console.log("error");
                            ctrl.$setValidity('emailAvailable', false);
                            return undefined;
                        });
                } else {
                    console.log("variable is undefined");
                    ctrl.setValidity('emailAvailable', false);
                    return undefined;
                }
            });
        }
    };
});
4

3 に答える 3

19

あなたはすでに自分の問題を解決しているようですが、ここでいくつかのヒント/アドバイスを提供できると思います (願っています):

1)組み込みの angular バリデーターがそれではなくに実行されたにバリデーターが実行されたことを確認するために必要なことはすべて。push()ctrl.$parsersunshift()

2) 以前に実行したバリデーターが無効であることを示しているため、バリデーターを実行しないようにするため (つまり、フィールドが既に無効化されている場合に Ajax 呼び出しを行いたくない場合)。バリデータ内のステートメントをチェックctrl.$invalidインするだけです。if

$setValidity()3) ajax 呼び出しを開始する前と受信後に、別の呼び出しでフォームを無効にする必要があります。このように、AJAX が返されて有効かどうかが通知されるまで、フォームは無効です。

4) これはおそらくマイナーですが、a も追加しない限りctrl.$formatter、最初に $scope でオブジェクトに割り当てられた値は、画面に書き込まれる前に検証されません。データが事前入力されたルーティング、ng-repeat、または ng-include を介してフォームが画面に動的に追加される場合、これは問題になる可能性があります。通常、すべてのバリデーターには $parser コンポーネント (ビュー -> モデル) と $formatter コンポーネント (モデル -> ビュー) が必要です。

5) 注意事項です。ほとんどすべてのバリデーターは、値が無効な場合、モデルから値を完全に削除します。非同期呼び出しを行っているため、パーサー関数ですぐに viewValue を返す必要があります。通常、パーサー関数はundefined、フィールドが無効化されている場合に戻ります。これにより、無効なデータがモデルに含まれるのを防ぐことができます。

6) バリデーターには $error オブジェクトで維持される状態があるため、この非同期バリデーターが最初にヒットしたときにそれをクリアする必要があります。下記参照。

7) 補足: あなたの回答で、あなたが ajax 応答ハンドラーで値を返していることに気付きました... それはあなたのために何もしません。呼び出しは非同期であるため、事実上、そのパーサーからは常に undefined が返されます。モデルを更新しますか?もしそうなら、私は驚くだろう。

元のディレクティブを変更して、おそらく希望どおりに機能させる方法を次に示します。

app.directive('emailAvailable', function($http, $timeout) { // available
    return {
        require: 'ngModel',
        link: function(scope, elem, attr, ctrl) {
          console.log(ctrl);
            // push the validator on so it runs last.
            ctrl.$parsers.push(function(viewValue) {
                // set it to true here, otherwise it will not 
                // clear out when previous validators fail.
                ctrl.$setValidity('emailAvailable', true);
                if(ctrl.$valid) {
                  // set it to false here, because if we need to check 
                  // the validity of the email, it's invalid until the 
                  // AJAX responds.
                  ctrl.$setValidity('checkingEmail', false);

                  // now do your thing, chicken wing.
                  if(viewValue !== "" && typeof viewValue !== "undefined") {
                      $http.get('/api/user/email/' + viewValue + '/available')
                          .success(function(data, status, headers, config) {
                              ctrl.$setValidity('emailAvailable', true);
                              ctrl.$setValidity('checkingEmail', true);
                          })
                          .error(function(data, status, headers, config) {
                              ctrl.$setValidity('emailAvailable', false);
                              ctrl.$setValidity('checkingEmail', true);
                          });
                  } else {
                      ctrl.$setValidity('emailAvailable', false);
                      ctrl.$setValidity('checkingEmail', true);
                  }
                }
                return viewValue;
            });

        }
    };
});

そして...もちろん、ここにすべてを示すプランカーがあります

于 2013-03-24T01:52:23.037 に答える
2

required 属性を削除し、email-valid という 2 番目のディレクティブを追加することで解決しました。また、emailAvailable ディレクティブの else 句で setValidity メソッドを削除しました。

HTML

<form name="mod_user" novalidate>
    <input type="email" name="email" ng-model="user.email" placeholder="E-Mail" email-valid email-available/>
    <div class="user-help" ng-show="mod_user.email.$dirty && mod_user.email.$invalid">Invalid:
        <span ng-show="mod_user.email.$error.emailValid">Please enter a valid email address.</span>
        <span ng-show="mod_user.email.$error.emailAvailable">This email address is already taken.</span>
    </div>
    <br/>
    <button class="button" ng-click="hideUserModScreen()">Close</button>
    <button class="button" ng-click="updateUserDetails()" ng-disabled="mod_user.$invalid" ng-show="updateUserDetailsButtonIsVisible">Update</button>
    <button class="button" ng-click="saveUserDetails()" ng-disabled="mod_user.$invalid" ng-show="saveUserDetailsButtonIsVisible">Save</button>
</form>

ディレクティブ

angular.module('myApp.directives', []).
directive('appVersion', ['version', function (version) {
    return function (scope, elm, attrs) {
        elm.text(version);
    };
}]).

directive('emailAvailable', function($http) { // available
    return {
        require: 'ngModel',
        link: function(scope, elem, attr, ctrl) {
            ctrl.$parsers.unshift(function(viewValue) {
                if(viewValue && viewValue.match(/[a-z0-9\-_]+@[a-z0-9\-_]+\.[a-z0-9\-_]{2,}/)) {
                    console.log("variable is defined");

                    $http.get('/api/user/email/' + viewValue + '/available')
                        .success(function(data, status, headers, config) {
                            console.log(status);
                            ctrl.$setValidity('emailAvailable', true);
                            return viewValue;
                        })
                        .error(function(data, status, headers, config) {
                            console.log("error");
                            ctrl.$setValidity('emailAvailable', false);
                            return undefined;
                        });
                } else {
                    console.log("variable is undefined");
                    return undefined;
                }
            });
        }
    };
}).

directive('emailValid', function() {
    return {
        require: 'ngModel',
        link: function(scope, elm, attrs, ctrl) {
            ctrl.$parsers.unshift(function(viewValue) {
                if (viewValue && viewValue.match(/[a-z0-9\-_]+@[a-z0-9\-_]+\.[a-z0-9\-_]{2,}/)) {
                    // it is valid
                    ctrl.$setValidity('emailValid', true);
                    return viewValue;
                } else {
                    // it is invalid, return undefined (no model update)
                    ctrl.$setValidity('emailValid', false);
                    return undefined;
                }
            });
        }
    };
});
于 2013-03-23T21:50:40.887 に答える
0

すべてのキーストロークをチェックしないようにタイムアウトを追加することで、Ben Leshの回答を少し拡張しました

app.directive "uniqueEmail", ($http, $timeout) ->
    restrict: "A" 
    require: "ngModel" 
    link: (scope, elem, attrs, ctrl) ->
        return unless ctrl 
        q = null
        ctrl.$parsers.push (viewValue) ->
            ctrl.$setValidity 'unique-email', true
            if ctrl.$valid
                if viewValue?
                    $timeout.cancel(q) if q?
                    q = $timeout (->
                        ctrl.$setValidity 'checking-unique-email', false
                        $http.get("#{endpoint}/emails/#{viewValue}/exists").success (exists) ->
                            ctrl.$setValidity 'checking-unique-email', true
                            ctrl.$setValidity 'unique-email', exists is 'false'
                        q = null
                        ) , attrs.checkDelay ? 1000

            viewValue
于 2014-05-22T21:04:09.827 に答える