web-dev-qa-db-ja.com

AngularJSを使用したフォーム検証での2つの入力値の比較

AngularJSを使用してフォームの検証をしようとしています。特に2つの値を比較することに興味があります。ユーザーが続行する前に、入力したデータを確認してほしい。以下のコードがあるとしましょう:

<p>
    Email:<input type="email" name="email1" ng-model="emailReg">
    Repeat Email:<input type="email" name="email2" ng-model="emailReg2">
<p>

そして、私は次のもので検証を使用できます:

<span ng-show="registerForm.email1.$error.required">Required!</span>
<span ng-show="registerForm.email1.$error.email">Not valid email!</span>
<span ng-show="emailReg !== emailReg2">Emails have to match!</span>  <-- see this line

registerForm。$ validは、ユーザーがフォームを送信する前にこの検証内で比較を使用して電子メールを強制的に同じにする方法がわからない場合を除き、入力のテキストに対して正しく反応します。

カスタムディレクティブのないソリューションが欲しいのですが、それなしでは解決できない場合は対処します。 ここ は、カスタムディレクティブに関する同様の問題に対処する回答です。

助けてくれてありがとう、ありがとう

47
trainoasis

これを実現する1つの方法は、カスタムディレクティブを使用することです。カスタムディレクティブ(この場合はng-match)を使用した例を次に示します。

<p>Email:<input type="email" name="email1" ng-model="emailReg">
Repeat Email:<input type="email" name="email2" ng-model="emailReg2" ng-match="emailReg"></p>

<span data-ng-show="myForm.emailReg2.$error.match">Emails have to match!</span>

注:ng-をカスタムディレクティブのプレフィックスとして使用することは、公式のAngularJSディレクティブと競合する可能性があるため、一般的にお勧めしません。

更新

また、カスタムディレクティブを使用せずにこの機能を取得することもできます。

HTML

<button ng-click="add()></button>
<span ng-show="IsMatch">Emails have to match!</span>

Controller

$scope.add = function() {
  if ($scope.emailReg != $scope.emailReg2) {
    $scope.IsMatch=true;
    return false;
  }
  $scope.IsMatch=false;
}
37

2つの入力値を比較するためにng-pattern/regexを使用できるはずです

Email:<input type="email" name="email1" ng-model="emailReg">
Repeat Email:<input type="email" name="email2" ng-model="emailReg2" ng-pattern="emailReg">

および検証:

<span ng-show="registerForm.email2.$error.pattern">Repeat Email should have the same value with email!</span>
46
Henry Neo

trainosais-あなたは正しい、検証は指令レベルで行われるべきです。クリーンでモジュール式で、コードの再利用が可能です。コントローラでそのような基本的な検証を行う場合、さまざまなフォームに対して何度も何度もそれを書きます。それは超乾燥です。

最近、同様の問題が発生し、パーサーパイプラインにプラグインする単純なディレクティブで分類したため、Angularアーキテクチャとの一貫性が保たれています。バリデーターを連鎖させると、再利用が非常に簡単になります。これが、私の見解では唯一のソリューションと見なされるべきです。

苦労せずに、簡単なマークアップを次に示します。

<form novalidate="novalidate">
    <label>email</label>
    <input type="text"
        ng-model="email"
        name="email" />
    <label>email repeated</label>
    <input ng-model="emailRepeated"
        same-as="email"
        name="emailRepeated" />
</form>

そして、JSコード:

angular.module('app', [])
    .directive('sameAs', function() {
        return {
            require: 'ngModel',
            link: function(scope, elem, attrs, ngModel) {
                ngModel.$parsers.unshift(validate);

                // Force-trigger the parsing pipeline.
                scope.$watch(attrs.sameAs, function() {
                    ngModel.$setViewValue(ngModel.$viewValue);
                });

                function validate(value) {
                    var isValid = scope.$eval(attrs.sameAs) == value;

                    ngModel.$setValidity('same-as', isValid);

                    return isValid ? value : undefined;
                }
            }
        };
    });

ディレクティブはパーサーパイプラインにフックして、ビュー値の変更を通知し、新しいビュー値と参照フィールドの値の比較に基づいて有効性を設定します。そのビットは簡単です。トリッキーなビットは、参照フィールドの変更を探ります。そのため、ディレクティブは参照値にウォッチャーを設定し、解析パイプラインを強制トリガーして、すべてのバリデーターを再度実行します。

あなたがそれで遊びたいなら、ここに私のペンがあります: http://codepen.io/jciolek/pen/kaKEn

ジェイセクに役立つと思います

29
Jacek Ciolek

私は最近、検証を行うのに十分な汎用性のあるカスタムディレクティブを作成しました。現在のスコープから検証関数を取得します

module.directive('customValidator', [function () {
        return {
            restrict: 'A',
            require: 'ngModel',
            scope: { validateFunction: '&' },
            link: function (scope, Elm, attr, ngModelCtrl) {
                ngModelCtrl.$parsers.Push(function (value) {
                    var result = scope.validateFunction({ 'value': value });
                    if (result || result === false) {
                        if (result.then) {
                            result.then(function (data) {           //For promise type result object
                                ngModelCtrl.$setValidity(attr.customValidator, data);
                            }, function (error) {
                                ngModelCtrl.$setValidity(attr.customValidator, false);
                            });
                        }
                        else {
                            ngModelCtrl.$setValidity(attr.customValidator, result);
                            return result ? value : undefined;      //For boolean result return based on boolean value
                        }
                    }
                    return value;
                });
            }
        };
    }]);

それを使用するには

<input type="email" name="email2" ng-model="emailReg2" custom-validator='emailMatch' data-validate-function='checkEmailMatch(value)'>
<span ng-show="registerForm.email2.$error.emailMatch">Emails have to match!</span>

コントローラーでメソッドを実装できます。trueまたはfalseを返す必要があります

$scope.checkEmailMatch=function(value) {
    return value===$scope.emailReg;
}

利点は、カスタム検証ごとにカスタムディレクティブを記述する必要がないことです。

12
Chandermani

angularを1.3以上にアップグレードすると、 Jacek Ciolekの素晴らしい答え :を使用して問題が見つかりました。

  • 参照フィールドにデータを追加します
  • ディレクティブを含むフィールドに同じデータを追加します(このフィールドは現在有効です)
  • 参照フィールドに戻ってデータを変更します(ディレクティブフィールドは有効なままです)

rdukeshier's answer (参照モデルを正しく取得するためにvar modelToMatch = element.attr('sameAs')var modelToMatch = attrs.sameAsに更新)をテストしましたが、同じ問題が発生しました。

これを修正するために(angular 1.3および1.4でテスト済み)、参照フィールドが変更されたときにすべての検証を実行するために、rdukeshierのコードを適応し、参照フィールドにウォッチャーを追加しました。ディレクティブは次のようになります。

angular.module('app', [])
  .directive('sameAs', function () {
    return {
      require: 'ngModel',
      link: function(scope, element, attrs, ctrl) {
        var modelToMatch = attrs.sameAs;      

        scope.$watch(attrs.sameAs, function() {
          ctrl.$validate();          
        })

        ctrl.$validators.match = function(modelValue, viewValue) {
          return viewValue === scope.$eval(modelToMatch);
        };
      }
   };
});

更新されたコードペン

8
br3w5

ng-patternを使用して、ng-validとng-dirtyが正しく機能するようにします

Email:<input type="email" name="email1" ng-model="emailReg">
Repeat Email:<input type="email" name="email2" ng-model="emailReg2" ng-pattern="emailReg">

<span ng-show="registerForm.email2.$error.pattern">Emails have to match!</span>
5
Cheng Li

関数やディレクティブは必要ありません。ビューから$ modelValueを比較するだけです:

ng-show="formName.email.$modelValue !== formName.confirmEmail.$modelValue"

より詳細な例:

<span ng-show="(formName.email.$modelValue !== formName.confirmEmail.$modelValue) 
                && formName.confirmEmail.$touched
                && !formName.confirmEmail.$error.required">Email does not match.</span>

注意 ConfirmEmailがViewModelの外にあること。それは$ scopeのプロパティです。提出する必要はありません。

カスタムバリデータディレクティブの単純なバージョンを次に示します。

angular.module('app')
  .directive('equalsTo', function () {
    return {
      require: 'ngModel',
      link:    function (scope, Elm, attrs, ngModel) {
        scope.$watchGroup([attrs.equalsTo, () => ngModel.$modelValue], newVal => {
          ngModel.$setValidity('equalsTo', newVal[0] === newVal[1]);
        });
      }
    };
  })
1
Yura Fedoriv

このモジュールは、2つのフィールドの比較に適しています。 Angular 1.3+で最適です。使いやすい https://www.npmjs.com/package/angular-password

また、モジュールを汎用として保存することもできます。それらをモジュールのパッケージリストに含めるだけです。

1
ravi punjwani

@ Henry-Neoのメソッドは近く、より厳密な正規表現ルールが必要でした。

<form name="emailForm">
    Email: <input type="email" name="email1" ng-model="emailReg">
    Repeat Email: <input type="email" name="email2" ng-model="emailReg2" ng-pattern="(emailReg)">
</form>

括弧内に正規表現ルールを含めると、emailRegからemailReg2の文字列全体に一致し、一致しないためフォーム検証が失敗します。

その後、要素にドリルダウンして、どの部品が故障しているかを見つけることができます。

 <p ng-show="emailForm.$valid">Form Valid</p>
 <p ng-show="emailForm.email1.$error">Email not valid</p>
 <p ng-show="emailForm.email1.$valid && emailForm.email1.$error.pattern">
     Emails Do Not Match
 </p>
1
dmo

以下に、sameAsディレクティブのangular 1.3バージョンを示します。

angular.module('app').directive('sameAs', [function() {
  'use strict';

  return {
    require: 'ngModel',
    restrict: 'A',
    link: function(scope, element, attrs, ctrl) {
      var modelToMatch = element.attr('sameAs');      
      ctrl.$validators.match = function(modelValue, viewValue) {
        return viewValue === scope.$eval(modelToMatch);
      };
    }
  };
}]);
0
rdukeshier

もちろん、非常に単純な比較のために、常にngMin/ngMaxを使用できます。

それ以外の場合は、カスタムディレクティブを使用できます。また、$watchまたは$observeまたは$evalまたはこの豪華な$setValidityを実行する必要なしがあります。行ったり来たり。また、postLink関数にフックする必要はまったくありません。 angular精神に反するため、できるだけDOMから離れるようにしてください。

フレームワークが提供するライフサイクルフックを使用するだけです。変更ごとにvalidator$validateを追加します。そのような単純な。

app.directive('sameAs', function() {
  return {
    restrict: 'A',
    require: {
      ngModelCtrl: 'ngModel'
    },
    scope: {
      reference: '<sameAs'
    },
    bindToController: true,
    controller: function($scope) {
      var $ctrl = $scope.$ctrl;

      //add the validator to the ngModelController
      $ctrl.$onInit = function() {
        function sameAsReference (modelValue, viewValue) {
          if (!$ctrl.reference || !modelValue) { //nothing to compare
            return true;
          }
          return modelValue === $ctrl.reference;
        }
        $ctrl.ngModelCtrl.$validators.sameas = sameAsReference;
      };

      //do the check at each change
      $ctrl.$onChanges = function(changesObj) {
        $ctrl.ngModelCtrl.$validate();
      };
    },
    controllerAs: '$ctrl'
  };
});

plunker

0
Daniele Repici

素晴らしい例に感謝します @ Jacek Ciolek 。 angular 1.3.xの場合、参照入力値が更新されると、このソリューションは中断します。 angular 1.3.xのこの例に基づいて、このソリューションはAngular 1.3.xと同様に機能します。バインドし、参照値の変更を監視します。

angular.module('app', []).directive('sameAs', function() {
  return {
    restrict: 'A',
    require: 'ngModel',
    scope: {
      sameAs: '='
    },
    link: function(scope, Elm, attr, ngModel) {
      if (!ngModel) return;

      attr.$observe('ngModel', function(value) {
        // observes changes to this ngModel
        ngModel.$validate();
      });

      scope.$watch('sameAs', function(sameAs) {
        // watches for changes from sameAs binding
        ngModel.$validate();
      });

      ngModel.$validators.sameAs = function(value) {
        return scope.sameAs == value;
      };
    }
  };
});

ここに私のペンがあります: http://codepen.io/kvangrae/pen/BjxMWR

0

私はこれをアプリケーション全体で1つの形式で行う必要があり、私の場合は非常に複雑なディレクティブがありますので、いくつかのポイントがあるようにng-patterを使用しますが、文字列に特別な.[\などの文字が壊れたため、特殊文字をエスケープするための関数を作成します。

$scope.escapeRegExp(str) {
  return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
}

そしてビューで

<form name="ExampleForm">
  <label>Password</label>
  <input ng-model="password" required />
  <br>
   <label>Confirm password</label>
  <input ng-model="confirmPassword" required ng-pattern="escapeRegExp(password)"/>  
</form>
0
stalin

より大きな問題を見る必要があります。 1つの問題を解決するディレクティブの作成方法。ディレクティブ se-form-error を試してください。この問題や他の多くの問題を解決するのに役立つでしょうか。

    <form name="ExampleForm">
  <label>Password</label>
  <input ng-model="password" required />
  <br>
   <label>Confirm password</label>
  <input ng-model="confirmPassword" required />
  <div use-form-error="isSame" use-error-expression="password && confirmPassword && password!=confirmPassword" ng-show="ExampleForm.$error.isSame">Passwords Do Not Match!</div>
</form>

ライブの例 jsfiddle

0

私のものはあなたのソリューションに似ていますが、私はそれを機能させました。唯一の違いは私のモデルです。 html入力に次のモデルがあります。

ng-model="new.Participant.email"
ng-model="new.Participant.confirmEmail"

私のコントローラーでは、これを$ scopeに入れています:

 $scope.new = {
        Participant: {}
    };

そして、この検証行は機能しました:

<label class="help-block" ng-show="new.Participant.email !== new.Participant.confirmEmail">Emails must match! </label>
0
r.sangria

Chandermani のメソッドを変更して、Angularjs 1.3以降と互換性を持たせました。 $ parsersから$ asyncValidatorsに移行しました。

module.directive('customValidator', [function () {
    return {
        restrict: 'A',
        require: 'ngModel',
        scope: { validateFunction: '&' },
        link: function (scope, Elm, attr, ngModelCtrl) {
            ngModelCtrl.$asyncValidators[attr.customValidator] = function (modelValue, viewValue) {
                return new Promise(function (resolve, reject) {
                    var result = scope.validateFunction({ 'value': viewValue });
                    if (result || result === false) {
                        if (result.then) {
                            result.then(function (data) {           //For promise type result object
                                if (data)
                                    resolve();
                                else
                                    reject();
                            }, function (error) {
                                reject();
                            });
                        }
                        else {
                            if (result)
                                resolve();
                            else
                                reject();
                            return;
                        }
                    }
                    reject();
                });
            }

        }
    };
}]);

使い方は同じです

0
Proggear