Dynamiczna walidacja wzorców ng w Angularjs

84

Mam formularz, który jeśli pole wyboru jest fałszywe, wymusza walidację danych wejściowych za pomocą dyrektywy ng-required. Jeśli pole wyboru ma wartość true, pole jest ukryte, a wartość ng-required ma wartość false.

Problem polega na tym, że mam również wyrażenie regularne do walidacji określone na wejściu, a także wykorzystuję dyrektywę kątową ng-pattern. Problem, z którym się spotykam, polega na tym, że jeśli użytkownik poda nieprawidłowy numer telefonu, zaznacza pole, aby dezaktywować dane wejściowe (i nie wymaga dalszej weryfikacji), formularz nie pozwoli na przesłanie, ponieważ jest nieprawidłowy w oparciu o wzorzec ng.

Próbowałem rozwiązać ten problem, dodając funkcję ng-change, aby ustawić model wejściowy na null, jednak wzorzec ng, a tym samym pole, jest nadal ustawiony jako nieprawidłowy w początkowym zestawie pola wyboru na wartość false. Jeśli jednak odznaczę to pole, ustawiając wszystko z powrotem na początkowe ładowanie formularza, a następnie zaznaczam to pole ponownie, formularz jest ważny i można go przesłać. Nie jestem pewien, czego mi brakuje. Oto kod zmiany ng, który mam do tej pory:

    var phoneNumberRegex = /^\(?(\d{3})\)?[ .-]?(\d{3})[ .-]?(\d{4})$/;
    $scope.phoneNumberPattern = phoneNumberRegex;
    $scope.removeValidation = function() {
        if ($scope.cell._newUser === false) {
            $scope.request._number = '';
            $scope.phoneNumberPattern = /[0-9a-zA-Z]?/;
        } else {
            $scope.phoneNumberPattern = phoneNumberRegex;
        }
    };
Brian
źródło

Odpowiedzi:

175

To ciekawy problem, złożona walidacja Angular. Następujące skrzypce realizują to, co chcesz:

http://jsfiddle.net/2G8gA/1/

Detale

Stworzyłem nową dyrektywę, rpatternczyli mieszankę Angulara ng-requiredi ng-patternkodu z input[type=text]. To, co robi, to obserwowanie requiredatrybutu pola i uwzględnienie tego podczas walidacji za pomocą wyrażenia regularnego, tj. Jeśli nie jest wymagane, zaznacz pole jako valid-pattern.

Uwagi

  • Większość kodu pochodzi z Angulara i jest dostosowana do potrzeb.
  • Gdy pole wyboru jest zaznaczone, pole jest wymagane.
  • Pole nie jest ukryte, jeśli wymagane pole wyboru jest fałszywe.
  • Wyrażenie regularne jest uproszczone dla wersji demonstracyjnej (ważne są 3 cyfry).

Brudne (ale mniejszy) rozwiązanie, jeśli nie chcą nowej dyrektywy, byłoby coś takiego:

$scope.phoneNumberPattern = (function() {
    var regexp = /^\(?(\d{3})\)?[ .-]?(\d{3})[ .-]?(\d{4})$/;
    return {
        test: function(value) {
            if( $scope.requireTel === false ) {
                return true;
            }
            return regexp.test(value);
        }
    };
})();

A w HTML nie byłyby wymagane żadne zmiany:

<input type="text" ng-model="..." ng-required="requireTel"
    ng-pattern="phoneNumberPattern" />

To faktycznie oszukuje Angulara do wywołania naszej test() metody, zamiast tego RegExp.test(), że bierze to requiredpod uwagę.

Nikos Paraskevopoulos
źródło
2
Byłbym bardzo zainteresowany, aby wiedzieć, co robią okrągłe nawiasy otaczające i po funkcji. Bez nich to nie działa
NeedHack
5
Wywołują funkcję bezpośrednio, skutecznie umieszczając jej wartość zwracaną w zmiennej. Jest to bardzo powszechne w Javascript, aby nie zaśmiecać zewnętrznego zakresu szczegółami implementacji konkretnego komponentu, np. Spojrzeć na wzorzec modułu .
Nikos Paraskevopoulos
6
Brudne rozwiązanie polegające na zrobieniu figla na AngularJS w oparciu o "ducktyping" JS jest najbardziej niesamowitym kodem, jaki dzisiaj widziałem. Naprawdę sprytne rozwiązanie!
Florian Loch,
Dziękuję za niesamowity kod, ale wciąż niezbyt jasny, 1) zgodnie z docs.angularjs.org/api/ng/directive/input , „Jeśli wynikiem wyrażenia jest obiekt RegExp, jest on używany bezpośrednio. Jeśli wyrażenie szacuje do ciągu, a następnie zostanie przekonwertowany na RegExp ”, ale przypuszczam, że phoneNumberPattern jest obiektem po zwróceniu? Czy to ma być Regxp Obj? 2) A w funkcji (value) {} store in test, skąd pochodzi parametr „value”? Wdzięczni za wszelkie odpowiedzi!
user2499325
1
1) Klauzula „Jeśli wyrażenie wylicza na RegExp” jest implementowana przez Angular poprzez sprawdzenie, czy otrzymana rzecz ma testmetodę. Dlatego nadajemy mu obiekt z naszym własnym niestandardowym test(), zasadniczo emulującym natywny JS RegExp. 2) valueJest oczywiście przekazywany przez Angular podczas wykonywania faktycznej kontroli ważności.
Nikos Paraskevopoulos
25

Nie zabierając niczego ze wspaniałej odpowiedzi Nikosa, być może możesz to zrobić prościej:

<form name="telForm">
  <input name="cb" type='checkbox' data-ng-modal='requireTel'>
  <input name="tel" type="text" ng-model="..." ng-if='requireTel' ng-pattern="phoneNumberPattern" required/>
  <button type="submit" ng-disabled="telForm.$invalid || telForm.$pristine">Submit</button>
</form>

Zwróć uwagę na drugie wejście: możemy użyć ng-ifdo sterowania renderowaniem i walidacją w formularzach. Jeśli requireTelzmienna nie jest ustawiona, drugie wejście byłoby nie tylko ukryte, ale w ogóle nie renderowane, więc formularz przejdzie walidację, a przycisk zostanie włączony, a otrzymasz to, czego potrzebujesz.

kumarharsh
źródło
1
Zaleta jest prosta. Wadą jest to, że wszelkie dane wejściowe, które zostaną usunięte, gdy wyrażenie ng-if ma wartość false, spowoduje usunięcie tekstu w nich, jeśli wartość ng-if zostanie później oceniona jako true. Mimo to +1 za prostotę.
Brad
Chciałem dodać, że jeśli pójdziesz tą drogą, upewnij się, że rozumiesz, że wszystko w ng-if (jeśli umieścisz to na przykład na div) będzie teraz miało swój własny zakres.
Brad
5

Zastosowany wzór:

 ng-pattern="/^\d{0,9}(\.\d{1,9})?$/"

Używany plik referencyjny:

 '<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.6/angular.js"></script>'

Przykład wprowadzenia:

 <input type="number" require ng-pattern="/^\d{0,9}(\.\d{1,9})?$/"><input type="submit">
Anil Singh
źródło
2

 <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.6/angular.js"></script>
<input type="number" require ng-pattern="/^\d{0,9}(\.\d{1,9})?$/"><input type="submit">

Jijo Paulose
źródło
2

Niedawno wpadłem na to.

To, co zrobiłem, co wydaje się łatwiejsze niż powyższe, to ustawienie wzorca na zmiennej w zakresie i odniesienie się do niego we wzorze ng w widoku.

Kiedy „pole wyboru nie jest zaznaczone”, po prostu ustawiam wartość wyrażenia regularnego na /.*/ w wywołaniu zwrotnym onChanged (jeśli nie jest zaznaczone). ng-pattern wybiera zmianę i mówi „OK, wartość jest dobra”. Formularz jest teraz ważny. Usunąłbym również złe dane z pola, abyś nie miał tam widocznie złego telefonu #.

Miałem dodatkowe problemy związane z ng-required i zrobiłem to samo. Działał jak urok.

jessewolfe
źródło
0

Ustawia klucz błędu walidacji wzorca, jeśli ngModel $ viewValue nie jest zgodny z wyrażeniem RegExp znalezionym przez oszacowanie wyrażenia kątowego podanego w wartości atrybutu. Jeśli wynikiem wyrażenia jest obiekt RegExp, jest on używany bezpośrednio. Jeśli wynikiem wyrażenia jest ciąg znaków, zostanie on przekonwertowany na wyrażenie RegExp po zawinięciu go w znaki ^ i $.

Wygląda na to, że najczęściej głosowana odpowiedź w tym pytaniu powinna zostać zaktualizowana, ponieważ kiedy ją spróbuję, nie stosuje ona testfunkcji, a walidacja nie działa.

Przykład z dokumentów Angular działa dobrze dla mnie:

Modyfikowanie wbudowanych walidatorów

html

<form name="form" class="css-form" novalidate>
  <div>
   Overwritten Email:
   <input type="email" ng-model="myEmail" overwrite-email name="overwrittenEmail" />
   <span ng-show="form.overwrittenEmail.$error.email">This email format is invalid!</span><br>
   Model: {{myEmail}}
  </div>
</form>

js

var app = angular.module('form-example-modify-validators', []);

app.directive('overwriteEmail', function() {
    var EMAIL_REGEXP = /^[a-z0-9!#$%&'*+/=?^_`{|}~.-]+@example\.com$/i;

    return {
        require: 'ngModel',
        restrict: '',
        link: function(scope, elm, attrs, ctrl) {
            // only apply the validator if ngModel is present and Angular has added the email validator
            if (ctrl && ctrl.$validators.email) {

                // this will overwrite the default Angular email validator
                ctrl.$validators.email = function(modelValue) {
                    return ctrl.$isEmpty(modelValue) || EMAIL_REGEXP.test(modelValue);
                };
             }
         }
     };
 });

Plunker

Theodor
źródło
-1

Możesz skorzystać ze strony https://regex101.com/, aby stworzyć własny wzorzec dla danego kraju:

Na przykład Polska:

-pattern = xxxxxxxxx OR xxx-xxx-xxx OR xxx xxx xxx 
-regexp ="^\d{9}|^\d{3}-\d{3}-\d{3}|^\d{3}\s\d{3}\s\d{3}"
Lepton
źródło