wake-up-neo.net

Vergleichen von zwei Eingabewerten in einer Formularvalidierung mit AngularJS

Ich versuche eine Formularvalidierung mit AngularJS durchzuführen. Ich interessiere mich besonders für den Vergleich zweier Werte. Ich möchte, dass der Benutzer einige eingegebene Daten bestätigt, bevor er fortfährt. Sagen wir, ich habe den folgenden Code:

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

und dann kann ich die Validierung mit verwenden:

<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 reagiert korrekt auf den Text in Eingaben, es sei denn, ich weiß nicht, wie der Vergleich innerhalb dieser Überprüfung zu verwenden ist, um die E-Mails vor dem Senden des Formulars zu zwingen.

Ich hätte gerne eine Lösung ohne benutzerdefinierte Anweisungen, aber wenn dies ohne sie nicht möglich ist, werde ich damit umgehen. Hier ist eine Antwort, die ein ähnliches Problem mit einer benutzerdefinierten Anweisung anspricht.

Jede Hilfe danke, danke

47
trainoasis

Eine Möglichkeit, dies zu erreichen, besteht in einer benutzerdefinierten Anweisung. Hier ein Beispiel mit einer benutzerdefinierten Direktive (in diesem Fall 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>

HINWEIS: Es wird generell nicht empfohlen, ng- als Präfix für eine benutzerdefinierte Anweisung zu verwenden, da dies zu Konflikten mit einer offiziellen AngularJS-Anweisung führen kann. 

Aktualisieren

Es ist auch möglich, diese Funktionalität zu erhalten, ohne eine benutzerdefinierte Anweisung zu verwenden:

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

Sie sollten ng-pattern/regex für den Vergleich von 2 Eingabewerten verwenden können

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

und Validierung mit:

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

trainosais - Sie haben Recht, die Validierung sollte auf Direktivebene erfolgen. Es ist sauber, modular und ermöglicht die Wiederverwendbarkeit von Code. Wenn Sie eine solche Basisprüfung in einem Controller haben, müssen Sie sie immer wieder für verschiedene Formulare schreiben. Das ist super Anti-Dry.

Ich hatte kürzlich ein ähnliches Problem und habe es mit einer einfachen Direktive gelöst, die in die Parser-Pipeline eingefügt wird und daher mit der Angular-Architektur konsistent bleibt. Durch die Verkettung von Validatoren ist die Wiederverwendung sehr einfach, und das sollte meiner Meinung nach die einzige Lösung sein.

Hier ist die vereinfachte Auszeichnung:

<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>

Und der JS-Code:

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;
                }
            }
        };
    });

Die Direktive hakt sich in die Parser-Pipeline ein, um über Änderungen des Ansichtswerts benachrichtigt zu werden und die Gültigkeit basierend auf dem Vergleich des neuen Ansichtswerts und des Werts des Referenzfelds festzulegen. Das ist einfach. Das knifflige Bit schnüffelt nach Änderungen im Referenzfeld. Dazu setzt die Direktive einen Beobachter auf den Referenzwert und triggert die Parsing-Pipeline mit Gewalt, um alle Validatoren erneut zum Laufen zu bringen.

Wenn du damit spielen willst, hier mein Stift: http://codepen.io/jciolek/pen/kaKEn

Ich hoffe es hilft. Jacek

29
Jacek Ciolek

Ich habe kürzlich eine benutzerdefinierte Direktive geschrieben, die generisch genug ist, um Validierungen durchzuführen. Es nimmt eine Validierungsfunktion aus dem aktuellen Geltungsbereich 

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;
                });
            }
        };
    }]);

Um es zu benutzen, machst du es

<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>

In Ihrem Controller können Sie dann die Methode implementieren, die true oder false zurückgeben soll

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

Der Vorteil ist, dass Sie nicht für jede benutzerdefinierte Überprüfung eine benutzerdefinierte Anweisung schreiben müssen.

12
Chandermani

Bei der Aktualisierung von Winkel auf 1.3 und höher fand ich ein Problem mit Jacek Cioleks großartige Antwort :

  • Fügen Sie dem Referenzfeld Daten hinzu
  • Fügen Sie dem Feld mit der Direktive dieselben Daten hinzu (dieses Feld ist jetzt gültig)
  • Gehen Sie zurück zum Referenzfeld und ändern Sie die Daten (Direktive-Feld bleibt gültig) 

Ich habe die Antwort von rdukeshier getestet (Aktualisierung von var modelToMatch = element.attr('sameAs') auf var modelToMatch = attrs.sameAs, um das Referenzmodell korrekt abzurufen), aber dasselbe Problem trat auf.

Um dies zu beheben (getestet in Winkel 1.3 und 1.4), habe ich den Code von rdukeshier angepasst und einen Beobachter in das Referenzfeld eingefügt, um alle Validierungen auszuführen, wenn das Referenzfeld geändert wird. Die Direktive sieht jetzt so aus:

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);
        };
      }
   };
});

Aktualisierte Codepen

7
br3w5

verwenden Sie ng-pattern, damit ng-valid und ng-dirty korrekt funktionieren können

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

Keine Funktion oder Direktive. Vergleichen Sie einfach ihren $ modelValue aus der Sicht: 

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

Detaillierteres Beispiel: 

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

Bitte beachten Sie , dass die ConfirmEmail außerhalb des ViewModel liegt; Es ist das Eigentum des $ -Ocopes. Es muss nicht eingereicht werden.

Hier ist meine einfache Version der benutzerdefinierten Validator-Direktive:

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

Dieses Modul eignet sich gut zum Vergleich zweier Felder. Funktioniert hervorragend mit Angular 1.3+. Einfach zu verwenden https://www.npmjs.com/package/angular-password

Es ermöglicht auch das Speichern des Moduls als generisch. Fügen Sie sie einfach in die Paketliste Ihres Moduls ein.

1
ravi punjwani

@ Henry-Neos Methode war nahe, es brauchte nur strengere Regex-Regeln.

<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>

Durch Einfügen der Regex-Regel in Klammern wird die gesamte Zeichenfolge von emailReg mit emailReg2 abgeglichen und die Formularüberprüfung schlägt fehl, da sie nicht übereinstimmt.

Sie können dann die Elemente untersuchen, um herauszufinden, welcher Teil fehlerhaft ist.

 <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

Hier ist eine eckige 1.3-Version der selbenAs-Direktive:

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

Für sehr einfache Vergleiche können Sie natürlich immer ngMinngMax verwenden.

Ansonsten können Sie mit einer benutzerdefinierten Direktive gehen, und es besteht keine Notwendigkeit, $watch oder $observe oder $eval oder diesen ausgefallenen $setValidity hin und her zu tun. Es ist auch nicht nötig, sich in die Funktion postLink einzuhaken. Versuchen Sie, sich so weit wie möglich aus dem DOM herauszuhalten, da dies gegen den eckigen Geist ist.

Verwenden Sie einfach die Lebenszyklus-Hooks, die Ihnen das Framework bietet. Fügen Sie bei jeder Änderung ein Validator und $validate hinzu. So einfach ist das.

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'
  };
});

Ihr Plunker .

0
Daniele Repici

Danke für das tolle Beispiel @Jacek Ciolek . Für Winkel 1.3.x wird diese Lösung unterbrochen, wenn der Referenzeingangswert aktualisiert wird. Aufbauend auf diesem Beispiel für Winkel 1.3.x funktioniert diese Lösung genauso gut mit Angular 1.3.x. Es bindet und überwacht den Referenzwert.

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;
      };
    }
  };
});

Hier ist mein Stift: http://codepen.io/kvangrae/pen/BjxMWR

0

Ich muss dies nur in einer Form in meiner gesamten Anwendung tun, und ich sehe eine Direktive wie eine Superkomplikation für meinen Fall, daher verwende ich den ng-patter wie manche haben einen Punkt, habe aber einige Probleme, wenn der String Sonderzeichen wie .[\ enthält pleite, also erstelle ich eine Funktion für scape-Sonderzeichen.

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

und in der Ansicht

<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

Sie müssen das größere Problem betrachten. Wie man die Anweisungen schreibt, die ein Problem lösen. Sie sollten die Direktive use-form-error versuchen. Würde es helfen, dieses Problem und viele andere zu lösen.

    <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>

Live-Beispiel jsfiddle

0

Meine ist ähnlich wie Ihre Lösung, aber ich habe sie zum Laufen gebracht. Der einzige Unterschied ist mein Modell. Ich habe die folgenden Modelle in meiner HTML-Eingabe:

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

und in meinem Controller habe ich das in $ scope:

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

und diese Validierungslinie funktionierte:

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

Ich habe die Methode von Chandermani geändert, um mit Angularjs 1.3 und höher kompatibel zu sein. Von $ parsers zu $ ​​asyncValidators migriert.

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();
                });
            }

        }
    };
}]);

Die Nutzung ist gleich

0
Proggear