AngularJSフォームにカスタム検証を追加するにはどうすればよいですか?


278

入力フィールドを備えたフォームと、required属性などを追加することによる検証設定があります。しかし、一部のフィールドでは、追加の検証を行う必要があります。FormController制御を検証する「タップ」方法を教えてください。

カスタム検証は、「これらの3つのフィールドが入力されている場合、このフィールドは必須であり、特定の方法でフォーマットする必要がある」のようになります。

メソッドFormController.$setValidityはありますが、それはパブリックAPIのようには見えないので、使用しません。カスタムディレクティブを作成して使用する方法NgModelControllerは別のオプションのように見えますが、基本的には、カスタム検証ルールごとにディレクティブを作成する必要がありますが、これは不要です。

実際、コントローラーからのフィールドを無効としてマークする(同時にFormController同期を維持する)ことは、ジョブを完了するための最も単純なシナリオで必要なことかもしれませんが、その方法がわかりません。


4
Angular JSでカスタム検証を処理するためのモンスターのコーディングに関する素晴らしい記事があります。これをチェックてください
安州

カスタムディレクティブが必要なため、私が探しているものとは異なりますが、とにかく良い記事なので、あなたの答えを受け入れます。
botteaap 2012

同じことを考えているのですが、FormControllerのレベルでいくつかのコントロールが欲しいです。たとえば、特定のカスタムディレクティブでFormControllerインスタンスにのようなフラグを付ける必要がありますformName.$warning
Adam Waselnuk、2014年

2
私はそれ$$が非公開APIに先行し、公開されると信じてい$ます。stackoverflow.com/questions/19338493/…を
ダニエルF

回答:


370

編集:以下にngMessages(> = 1.3.X)に関する情報を追加しました。

標準フォーム検証メッセージ(1.0.X以降)

これは、Googleの「Angular Form Validation」の場合の上位の結果の1つなので、現在、そこから入ってくる人のために別の回答を追加したいと思います。

FormController。$ setValidityにはメソッドがありますが、それはパブリックAPIのように見えないため、使用しません。

それは「公開」であり、心配する必要はありません。これを使って。それが目的です。使用するつもりがなかった場合、Angular開発者はクロージャーでそれを民営化したでしょう。

カスタム検証を行うために、Angular-UIを他の回答の提案のように使用したくない場合は、独自の検証ディレクティブをロールするだけで済みます。

app.directive('blacklist', function (){ 
   return {
      require: 'ngModel',
      link: function(scope, elem, attr, ngModel) {
          var blacklist = attr.blacklist.split(',');

          //For DOM -> model validation
          ngModel.$parsers.unshift(function(value) {
             var valid = blacklist.indexOf(value) === -1;
             ngModel.$setValidity('blacklist', valid);
             return valid ? value : undefined;
          });

          //For model -> DOM validation
          ngModel.$formatters.unshift(function(value) {
             ngModel.$setValidity('blacklist', blacklist.indexOf(value) === -1);
             return value;
          });
      }
   };
});

そして、ここにいくつかの使用例があります:

<form name="myForm" ng-submit="doSomething()">
   <input type="text" name="fruitName" ng-model="data.fruitName" blacklist="coconuts,bananas,pears" required/>
   <span ng-show="myForm.fruitName.$error.blacklist">
      The phrase "{{data.fruitName}}" is blacklisted</span>
   <span ng-show="myForm.fruitName.$error.required">required</span>
   <button type="submit" ng-disabled="myForm.$invalid">Submit</button>
</form>

注:1.2.Xではng-ifng-show上記の代わりに使用することをお勧めします

これは必須のプランカーリンクです

また、私はこの件について、もう少し詳しく説明するブログエントリをいくつか書きました。

角度形式の検証

カスタム検証ディレクティブ

編集:1.3.XでのngMessagesの使用

ngShowの代わりにngMessagesモジュールを使用して、エラーメッセージを表示できるようになりました。実際には何でも機能しますが、エラーメッセージである必要はありませんが、基本は次のとおりです。

  1. 含む <script src="angular-messages.js"></script>
  2. ngMessagesモジュール宣言での参照:

    var app = angular.module('myApp', ['ngMessages']);
  3. 適切なマークアップを追加します。

    <form name="personForm">
      <input type="email" name="email" ng-model="person.email" required/>
    
      <div ng-messages="personForm.email.$error">
        <div ng-message="required">required</div>
        <div ng-message="email">invalid email</div>
      </div>
    </form>

上記のマークアップでは、ng-message="personForm.email.$error"基本的にはng-message子ディレクティブのコンテキストを指定します。次に ng-message="required"ng-message="email"監視するコンテキストのプロパティを指定します。最も重要なのは、それらをチェックインする順序も指定することです。リストで最初に見つかった「真実」なものが勝ち、そのメッセージだけが表示され、他のメッセージは表示されません。

そして、ngMessagesサンプルのプランカー


6
$ parsers.unshiftに渡す関数の値を返す場合、誤った値もモデルに保存されます-値が無効な場合は、未定義を返す方がよいと思います。
georgiosd 2013年

5
+1 @georgiosd ... 100%正解です。Angularが何をするかを見ると、彼らは未定義を返しています。(うまくいけば)無効なフォームからのモデルは送信されないので、値を返すことはおそらく大したことではありません...しかし、申し訳ありませんがより安全だと思います。
Ben Lesh 2013年

2
素晴らしいもの!Angularでのカスタム検証に関する適切な記事を探してここで
maaachine

AngularJSとフィルターを使用した高度なフォーム検証を確認しましたか?フィルター検証を一般的に解決します。
Benny Bottema、2014年

1
私はあなたがreturn value ? valid : undefined上記を行うつもりだったと思う。
GChorn、2015

92

Angular-UIのプロジェクトには、おそらくこれに役立つui-validateディレクティブが含まれています。検証を行うために呼び出す関数を指定しましょう。

デモページをご覧ください:http : //angular-ui.github.com/、Validate見出しまで検索します。

デモページから:

<input ng-model="email" ui-validate='{blacklist : notBlackListed}'>
<span ng-show='form.email.$error.blacklist'>This e-mail is black-listed!</span>

次に、コントローラで:

function ValidateCtrl($scope) {
  $scope.blackList = ['bad@domain.com','verybad@domain.com'];
  $scope.notBlackListed = function(value) {
    return $scope.blackList.indexOf(value) === -1;
  };
}

Angular 1.4を使用している私にとってこれがうまくいかないのは奇妙なことです
Nick

46

検証シナリオにng-requiredを使用できます(「これらの3つのフィールドに入力した場合、このフィールドは必須です」:

<div ng-app>
    <input type="text" ng-model="field1" placeholder="Field1">
    <input type="text" ng-model="field2" placeholder="Field2">
    <input type="text" ng-model="field3" placeholder="Field3">
    <input type="text" ng-model="dependentField" placeholder="Custom validation"
        ng-required="field1 && field2 && field3">
</div>

2
これでうまくいきました。他のフィールドの値に依存する単純な検証の場合、これは複雑な検証ルールを記述する代わりに実行する方法です
VimalKumar

28

Angular-Validatorを使用できます。

例:関数を使用してフィールドを検証する

<input  type = "text"
    name = "firstName"
    ng-model = "person.firstName"
    validator = "myCustomValidationFunction(form.firstName)">

それからあなたのコントローラーには次のようなものがあります

$scope.myCustomValidationFunction = function(firstName){ 
   if ( firstName === "John") {
       return true;
    }

次のようなこともできます:

<input  type = "text"
        name = "firstName"
        ng-model = "person.firstName"
        validator = "'!(field1 && field2 && field3)'"
        invalid-message = "'This field is required'">

(ここで、field1、field2、およびfield3はスコープ変数です。フィールドが空の文字列と等しくないかどうかを確認することもできます)

フィールドが通過しない場合、フィールドはvalidator無効としてマークされ、ユーザーはフォームを送信できなくなります。

その他の使用例と例については、https//github.com/turinggroup/angular-validatorをご覧ください。

免責事項:私はAngular-Validatorの作者です


13

私は最近、角形入力の式ベースの無効化を可能にするディレクティブを作成しました。任意の有効な角度式を使用でき、オブジェクト表記を使用したカスタム検証キーをサポートしています。角度v1.3.8でテスト済み

        .directive('invalidIf', [function () {
        return {
            require: 'ngModel',
            link: function (scope, elm, attrs, ctrl) {

                var argsObject = scope.$eval(attrs.invalidIf);

                if (!angular.isObject(argsObject)) {
                    argsObject = { invalidIf: attrs.invalidIf };
                }

                for (var validationKey in argsObject) {
                    scope.$watch(argsObject[validationKey], function (newVal) {
                        ctrl.$setValidity(validationKey, !newVal);
                    });
                }
            }
        };
    }]);

次のように使用できます。

<input ng-model="foo" invalid-if="{fooIsGreaterThanBar: 'foo > bar',
                                   fooEqualsSomeFuncResult: 'foo == someFuncResult()'}/>

または、式を渡すだけで(「invalidIf」のデフォルトのvalidationKeyが与えられます)

<input ng-model="foo" invalid-if="foo > bar"/>

13

フォームでカスタムワイルドカード式の検証を行うためのクールな方法を次に示します(from:AngularJSとフィルターを使用した高度なフォーム検証)。

<form novalidate="">  
   <input type="text" id="name" name="name" ng-model="newPerson.name"
      ensure-expression="(persons | filter:{name: newPerson.name}:true).length !== 1">
   <!-- or in your case:-->
   <input type="text" id="fruitName" name="fruitName" ng-model="data.fruitName"
      ensure-expression="(blacklist | filter:{fruitName: data.fruitName}:true).length !== 1">
</form>
app.directive('ensureExpression', ['$http', '$parse', function($http, $parse) {
    return {
        require: 'ngModel',
        link: function(scope, ele, attrs, ngModelController) {
            scope.$watch(attrs.ngModel, function(value) {
                var booleanResult = $parse(attrs.ensureExpression)(scope);
                ngModelController.$setValidity('expression', booleanResult);
            });
        }
    };
}]);

jsFiddleデモ(式の命名と複数の式をサポート)

これはに似てui-validateいますが、スコープ固有の検証関数は必要ありません(これは一般的に機能します)。もちろん、この方法ではui.utilsは必要ありません。


ありがとう。とてもかっこいい。動的フォームに検証ルールを適用すると特に便利です。ただし、無効でもモデル値を設定します。とにかくそれが無効な場合にmodelValueを設定しないようにしますか?
YuMei

5

更新:

同じ機能を備えた、以前のディレクティブの改良版と簡略版(2つではなく1つ):

.directive('myTestExpression', ['$parse', function ($parse) {
    return {
        restrict: 'A',
        require: 'ngModel',
        link: function (scope, element, attrs, ctrl) {
            var expr = attrs.myTestExpression;
            var watches = attrs.myTestExpressionWatch;

            ctrl.$validators.mytestexpression = function (modelValue, viewValue) {
                return expr == undefined || (angular.isString(expr) && expr.length < 1) || $parse(expr)(scope, { $model: modelValue, $view: viewValue }) === true;
            };

            if (angular.isString(watches)) {
                angular.forEach(watches.split(",").filter(function (n) { return !!n; }), function (n) {
                    scope.$watch(n, function () {
                        ctrl.$validate();
                    });
                });
            }
        }
    };
}])

使用例:

<input ng-model="price1" 
       my-test-expression="$model > 0" 
       my-test-expression-watch="price2,someOtherWatchedPrice" />
<input ng-model="price2" 
       my-test-expression="$model > 10" 
       my-test-expression-watch="price1" 
       required />

結果:他のディレクティブモデルと現在のモデルの変更時にバリデーターが実行される相互依存テスト式。

テスト式にはローカル$model変数があり、他の変数と比較するために使用する必要があります。

以前は:

余分なディレクティブを追加して@Plantfaceコードを改善しようとしました。この追加のディレクティブは、複数のngModel変数で変更が行われたときに式を実行する必要がある場合に非常に役立ちます。

.directive('ensureExpression', ['$parse', function($parse) {
    return {
        restrict: 'A',
        require: 'ngModel',
        controller: function () { },
        scope: true,
        link: function (scope, element, attrs, ngModelCtrl) {
            scope.validate = function () {
                var booleanResult = $parse(attrs.ensureExpression)(scope);
                ngModelCtrl.$setValidity('expression', booleanResult);
            };

            scope.$watch(attrs.ngModel, function(value) {
                scope.validate();
            });
        }
    };
}])

.directive('ensureWatch', ['$parse', function ($parse) {
    return {
        restrict: 'A',
        require: 'ensureExpression',
        link: function (scope, element, attrs, ctrl) {
            angular.forEach(attrs.ensureWatch.split(",").filter(function (n) { return !!n; }), function (n) {
                scope.$watch(n, function () {
                    scope.validate();
                });
            });
        }
    };
}])

これを使用して相互検証フィールドを作成する方法の例:

<input name="price1"
       ng-model="price1" 
       ensure-expression="price1 > price2" 
       ensure-watch="price2" />
<input name="price2" 
       ng-model="price2" 
       ensure-expression="price2 > price3" 
       ensure-watch="price3" />
<input name="price3" 
       ng-model="price3" 
       ensure-expression="price3 > price1 && price3 > price2" 
       ensure-watch="price1,price2" />

ensure-expressionng-modelまたはensure-watch変数が変更されたときにモデルを検証するために実行されます。


4

@synergetic @bleshは関数validateを以下のように仮定すると思います

function validate(value) {
    var valid = blacklist.indexOf(value) === -1;
    ngModel.$setValidity('blacklist', valid);
    return valid ? value : undefined;
}

ngModel.$formatters.unshift(validate);
ngModel.$parsers.unshift(validate);

4

サーバーを呼び出すカスタム検証

バックエンドへのリクエストなど、非同期検証を処理するngModelController $asyncValidatorsAPIを使用します$http。オブジェクトに追加された関数は、有効なときに解決するか、無効なときに拒否するプロミスを返す必要があります。進行中の非同期検証は、キーに格納されますngModelController.$pending。詳細については、AngularJS開発者ガイド-フォーム(カスタム検証)を参照してください。

ngModel.$asyncValidators.uniqueUsername = function(modelValue, viewValue) {
  var value = modelValue || viewValue;

  // Lookup user by username
  return $http.get('/api/users/' + value).
     then(function resolved() {
       //username exists, this means validation fails
       return $q.reject('exists');
     }, function rejected() {
       //username does not exist, therefore this validation passes
       return true;
     });
};

詳細については、


を使用して $validators APIを

受け入れられた回答は、$parsersおよび$formattersパイプラインを使用して、カスタム同期バリデーターを追加します。AngularJS 1.3+は$validatorsAPIを追加したため$parsers$formattersパイプラインとパイプラインにバリデーターを配置する必要はありません。

app.directive('blacklist', function (){ 
   return {
      require: 'ngModel',
      link: function(scope, elem, attr, ngModel) {           
          ngModel.$validators.blacklist = function(modelValue, viewValue) {
              var blacklist = attr.blacklist.split(',');
              var value = modelValue || viewValue;
              var valid = blacklist.indexOf(value) === -1;
              return valid;
          });    
      }
   };
});

詳細については、AngularJS ngModelController APIリファレンス-$ validatorsを参照してください。


3

AngularJSでは、カスタム検証を定義するのに最適な場所はCutsomディレクティブです。AngularJSはngMessagesモジュールを提供します。

ngMessagesは、リッスンするキー/値オブジェクトの状態に基づいてメッセージを表示および非表示にするように設計されたディレクティブです。ディレクティブ自体は、ngModel $ errorオブジェクト(検証エラーのキー/値の状態を格納)でエラーメッセージレポートを補完します。

カスタムフォームの検証の場合、ngMessagesモジュールとカスタムディレクティブを使用する必要があります。ここでは、番号の長さが6未満かどうかを確認する簡単な検証を行います。画面にエラーが表示されます。

 <form name="myform" novalidate>
                <table>
                    <tr>
                        <td><input name='test' type='text' required  ng-model='test' custom-validation></td>
                        <td ng-messages="myform.test.$error"><span ng-message="invalidshrt">Too Short</span></td>
                    </tr>
                </table>
            </form>

カスタム検証ディレクティブを作成する方法は次のとおりです

angular.module('myApp',['ngMessages']);
        angular.module('myApp',['ngMessages']).directive('customValidation',function(){
            return{
            restrict:'A',
            require: 'ngModel',
            link:function (scope, element, attr, ctrl) {// 4th argument contain model information 

            function validationError(value) // you can use any function and parameter name 
                {
                 if (value.length > 6) // if model length is greater then 6 it is valide state
                 {
                 ctrl.$setValidity('invalidshrt',true);
                 }
                 else
                 {
                 ctrl.$setValidity('invalidshrt',false) //if less then 6 is invalide
                 }

                 return value; //return to display  error 
                }
                ctrl.$parsers.push(validationError); //parsers change how view values will be saved in the model
            }
            };
        });

$setValidity モデルの状態を有効/無効に設定する組み込み関数です


1

検証で大文字と小文字を区別するかどうかを指定する機能で@Ben Leshの回答を拡張しました(デフォルト)

使用する:

<input type="text" name="fruitName" ng-model="data.fruitName" blacklist="Coconuts,Bananas,Pears" caseSensitive="true" required/>

コード:

angular.module('crm.directives', []).
directive('blacklist', [
    function () {
        return {
            restrict: 'A',
            require: 'ngModel',
            scope: {
                'blacklist': '=',
            },
            link: function ($scope, $elem, $attrs, modelCtrl) {

                var check = function (value) {
                    if (!$attrs.casesensitive) {
                        value = (value && value.toUpperCase) ? value.toUpperCase() : value;

                        $scope.blacklist = _.map($scope.blacklist, function (item) {
                            return (item.toUpperCase) ? item.toUpperCase() : item
                        })
                    }

                    return !_.isArray($scope.blacklist) || $scope.blacklist.indexOf(value) === -1;
                }

                //For DOM -> model validation
                modelCtrl.$parsers.unshift(function (value) {
                    var valid = check(value);
                    modelCtrl.$setValidity('blacklist', valid);

                    return value;
                });
                //For model -> DOM validation
                modelCtrl.$formatters.unshift(function (value) {
                    modelCtrl.$setValidity('blacklist', check(value));
                    return value;
                });
            }
        };
    }
]);

0

このスレッドで提供されているいくつかの素晴らしい例とライブラリーですが、それらには私が探していたものがまったくありませんでした。私のアプローチ:angular-validity-非同期検証用のプロミスベースの検証ライブラリで、オプションのBootstrapスタイリングが組み込まれています。

OPのユースケースの角度有効性ソリューションは、次のようになります。

<input  type="text" name="field4" ng-model="field4"
        validity="eval"
        validity-eval="!(field1 && field2 && field3 && !field4)"
        validity-message-eval="This field is required">

これを試してみたい場合は、ここにフィドルがあります。libはGitHubで入手でき、詳細なドキュメントとたくさんのライブデモがあります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.