Bagaimana cara menambahkan validasi khusus ke formulir AngularJS?

278

Saya memiliki formulir dengan bidang input dan pengaturan validasi dengan menambahkan requiredatribut dan semacamnya. Tetapi untuk beberapa bidang saya perlu melakukan beberapa validasi tambahan. Bagaimana saya "mengetuk" ke validasi yang FormControllermengontrol?

Validasi khusus dapat berupa "jika 3 bidang ini diisi, maka bidang ini diperlukan dan perlu diformat dengan cara tertentu".

Ada metode di FormController.$setValiditytapi itu tidak terlihat seperti API publik jadi saya lebih suka tidak menggunakannya. Membuat arahan khusus dan menggunakan NgModelControllertampak seperti opsi lain, tetapi pada dasarnya akan mengharuskan saya untuk membuat arahan untuk setiap aturan validasi khusus, yang tidak saya inginkan.

Sebenarnya, menandai bidang dari controller sebagai tidak valid (sambil tetap FormControllermenyinkronkan) mungkin adalah hal yang saya butuhkan dalam skenario paling sederhana untuk menyelesaikan pekerjaan, tetapi saya tidak tahu bagaimana melakukannya.

botteaap
sumber
4
Ada artikel bagus tentang pengkodean monster untuk menangani validasi khusus di JS sudut. Periksa ini keluar
Anshu
Bukan apa yang saya cari, karena memerlukan arahan khusus, tetapi saya akan menerima jawaban Anda karena itu artikel yang bagus.
botteaap
Saya bertanya-tanya hal yang sama, saya ingin kontrol di level FormController. Sebagai contoh, saya ingin arahan khusus tertentu untuk menandai contoh FormController sebagai sesuatu seperti formName.$warning.
Adam Waselnuk
2
Saya percaya bahwa $$mendahului apis tanpa-publik, dengan $menjadi publik. Lihat stackoverflow.com/questions/19338493/...
Daniel F

Jawaban:

370

Edit: menambahkan informasi tentang ngMessages (> = 1.3.X) di bawah ini.

Pesan validasi form standar (1.0.X ke atas)

Karena ini adalah salah satu hasil teratas jika Anda Google "Validasi Formulir Sudut", saat ini, saya ingin menambahkan jawaban lain untuk ini bagi siapa pun yang datang dari sana.

Ada metode di FormController. $ SetValidity tapi itu tidak terlihat seperti API publik jadi saya lebih suka tidak menggunakannya.

Ini "publik", jangan khawatir. Gunakan. Itu untuk apa. Jika itu tidak dimaksudkan untuk digunakan, para dev Angular akan memprivatisasinya dalam penutupan.

Untuk melakukan validasi khusus, jika Anda tidak ingin menggunakan Angular-UI seperti jawaban lain yang disarankan, Anda cukup menggulirkan arahan validasi Anda sendiri.

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

Dan inilah beberapa contoh penggunaan:

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

Catatan: di 1.2.x itu mungkin preferrable untuk pengganti ng-ifuntuk ng-showdi atas

Berikut ini adalah tautan plunker wajib

Juga, saya telah menulis beberapa entri blog tentang hal ini yang lebih detail:

Validasi Formulir Sudut

Arahan Validasi Kustom

Edit: menggunakan ngMessages di 1.3.X

Anda sekarang dapat menggunakan modul ngMessages alih-alih ngShow untuk menampilkan pesan kesalahan Anda. Ini benar-benar akan bekerja dengan apa pun, tidak harus menjadi pesan kesalahan, tapi inilah dasarnya:

  1. Termasuk <script src="angular-messages.js"></script>
  2. Referensi ngMessagesdalam deklarasi modul Anda:

    var app = angular.module('myApp', ['ngMessages']);
  3. Tambahkan markup yang sesuai:

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

Pada markup di atas, ng-message="personForm.email.$error"pada dasarnya menentukan konteks untuk ng-messagearahan anak. Kemudian ng-message="required"dan ng-message="email"tentukan properti pada konteks itu untuk ditonton. Yang paling penting, mereka juga menentukan perintah untuk memeriksanya . Yang pertama ditemukan dalam daftar yang "benar" menang, dan itu akan menunjukkan pesan itu dan tidak ada yang lain.

Dan seorang plunker untuk contoh ngMessages

Ben Lesh
sumber
6
Jika Anda mengembalikan nilai pada fungsi yang Anda berikan ke $ parsers.unshift, nilai-nilai yang salah akan disimpan ke model juga - akan lebih baik untuk mengembalikan undefined Saya percaya (ketika nilainya tidak valid).
georgios
5
+1 @georgiosd ... 100% benar. Melihat melalui apa yang dilakukan Angular, mereka kembali tanpa definisi. Mungkin bukan masalah besar untuk mengembalikan nilai, karena (semoga) model dari formulir yang tidak valid tidak dikirimkan ... tapi lebih baik aman daripada menyesal, saya kira.
Ben Lesh
2
Barang bagus! Jika Anda mencari Google di sini untuk mencari artikel bagus tentang validasi khusus di Angular, lihat apa yang ditulis
@blesh
Apakah Anda memeriksa validasi formulir Lanjutan dengan AngularJS dan filter ? Ini memecahkan validasi filter secara umum.
Benny Bottema
1
Saya pikir Anda mungkin bermaksud melakukan di return value ? valid : undefinedatas.
GChorn
92

Proyek Angular-UI mencakup arahan validasi ui, yang mungkin akan membantu Anda dengan ini. Ini memungkinkan Anda menentukan fungsi untuk memanggil untuk melakukan validasi.

Lihat halaman demo: http://angular-ui.github.com/ , cari ke bawah ke arah Validasi.

Dari halaman demo:

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

kemudian di controller Anda:

function ValidateCtrl($scope) {
  $scope.blackList = ['[email protected]','[email protected]'];
  $scope.notBlackListed = function(value) {
    return $scope.blackList.indexOf(value) === -1;
  };
}
Pete BD
sumber
Betapa anehnya bahwa ini tidak berhasil bagi saya menggunakan Angular 1.4
Nick
46

Anda dapat menggunakan ng-diperlukan untuk skenario validasi Anda ("jika 3 bidang ini diisi, maka bidang ini wajib diisi":

<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>
Mario G.
sumber
2
Ini berhasil untuk saya. Untuk validasi sederhana yang bergantung pada nilai bidang lain, ini adalah cara untuk menggantikan penulisan aturan validasi kompleks
VimalKumar
28

Anda dapat menggunakan Angular-Validator .

Contoh: menggunakan fungsi untuk memvalidasi bidang

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

Kemudian di controller Anda, Anda akan memiliki sesuatu seperti

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

Anda juga dapat melakukan sesuatu seperti ini:

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

(di mana field1 field2, dan field3 adalah variabel lingkup. Anda mungkin juga ingin memeriksa apakah bidang tidak sama dengan string kosong)

Jika bidang tidak lulus, validatormaka bidang tersebut akan ditandai sebagai tidak valid dan pengguna tidak akan dapat mengirimkan formulir.

Untuk kasus dan contoh penggunaan lebih lanjut, lihat: https://github.com/turinggroup/angular-validator

Penafian: Saya penulis Angular-Validator

pengguna3920706
sumber
13

Baru-baru ini saya membuat arahan untuk memungkinkan validasi berbasis input input bentuk sudut. Ekspresi sudut yang valid dapat digunakan, dan mendukung kunci validasi khusus menggunakan notasi objek. Diuji dengan v1.3.8 sudut

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

Anda bisa menggunakannya seperti ini:

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

Atau dengan hanya memberikan ekspresi (itu akan diberikan validasi defaultKey dari "invalidIf")

<input ng-model="foo" invalid-if="foo > bar"/>
Alex Schwartz
sumber
13

Berikut cara keren untuk melakukan validasi ekspresi wildcard khusus dalam formulir (dari: Validasi formulir lanjutan dengan AngularJS dan filter ):

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

Demo jsFiddle (mendukung penamaan ekspresi dan banyak ekspresi)

Ini mirip dengan ui-validate, tetapi Anda tidak memerlukan fungsi validasi spesifik lingkup (ini berfungsi secara umum) dan tentu saja Anda tidak perlu ui.utils dengan cara ini.

Benny Bottema
sumber
Terima kasih. Sangat keren. Sangat berguna untuk menerapkan aturan validasi untuk formulir dinamis. Namun, masih menetapkan nilai model meskipun tidak valid. Pokoknya untuk mencegahnya mengatur modelValue jika tidak valid?
YuMei
5

Memperbarui:

Versi yang ditingkatkan dan disederhanakan dari arahan sebelumnya (satu bukannya dua) dengan fungsi yang sama:

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

Contoh penggunaan:

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

Hasil: Ekspresi pengujian yang saling bergantung di mana validator dieksekusi pada perubahan model direktif dan model saat ini lainnya.

Ekspresi tes memiliki $modelvariabel lokal yang harus Anda gunakan untuk membandingkannya dengan variabel lain.

Sebelumnya:

Saya telah berupaya meningkatkan kode @Plantface dengan menambahkan arahan ekstra. Arahan ekstra ini sangat berguna jika ekspresi kita perlu dieksekusi ketika perubahan dibuat di lebih dari satu variabel Model.

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

Contoh cara menggunakannya untuk membuat bidang yang divalidasi silang:

<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-expressiondieksekusi untuk memvalidasi model ketika ng-modelatau salah satu ensure-watchvariabel diubah.

knr
sumber
4

@synergetic Saya pikir @blesh kira menempatkan fungsi memvalidasi seperti di bawah ini

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);
Atul Chaudhary
sumber
4

Validasi Kustom yang memanggil Server

Gunakan API ngModelController$asyncValidators yang menangani validasi asinkron, seperti membuat $httppermintaan ke backend. Fungsi yang ditambahkan ke objek harus mengembalikan janji yang harus diselesaikan saat valid atau ditolak saat tidak valid. Validasi async yang sedang berlangsung disimpan dengan kunci ngModelController.$pending. Untuk informasi lebih lanjut, lihat Panduan Pengembang AngularJS - Formulir (Validasi Kustom) .

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

Untuk informasi lebih lanjut, lihat


Menggunakan $validatorsAPI

Jawaban yang diterima menggunakan $parsersdan $formattersjalur pipa untuk menambahkan validator sinkron khusus. AngularJS 1.3+ menambahkan $validatorsAPI sehingga tidak perlu memasukkan validator ke dalam $parsersdan $formattersjalur pipa:

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

Untuk informasi lebih lanjut, lihat Referensi API AngularJS ngModelController - $ validator .

georgeawg
sumber
3

Di AngularJS tempat terbaik untuk menentukan Validasi Kustom adalah direktif Cutsom. AngularJS menyediakan modul ngMessages.

ngMessages adalah arahan yang dirancang untuk menampilkan dan menyembunyikan pesan berdasarkan keadaan objek kunci / nilai yang didengarkannya. Arahan itu sendiri melengkapi pelaporan pesan kesalahan dengan objek ngModel $ error (yang menyimpan status kunci / nilai kesalahan validasi).

Untuk validasi formulir kustom. Seseorang harus menggunakan Modul ngMessages dengan direktif khusus. Di sini saya memiliki validasi sederhana yang akan memeriksa apakah panjang angka kurang dari 6 menampilkan kesalahan pada layar.

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

Berikut adalah cara membuat arahan validasi khusus

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 adalah fungsi bawaan untuk mengatur status model ke valid / tidak valid

Muhammad Nasir
sumber
1

Saya memperluas jawaban @Ben Lesh dengan kemampuan untuk menentukan apakah validasi peka huruf besar-kecil atau tidak (default)

menggunakan:

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

kode:

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;
                });
            }
        };
    }
]);
Liran Brimer
sumber
0

Beberapa contoh dan lib yang bagus disajikan di utas ini, tetapi mereka tidak memiliki apa yang saya cari. Pendekatan saya: angular-validity - lib validasi berbasis janji untuk validasi asinkron, dengan styling Bootstrap opsional yang dipanggang.

Solusi validitas sudut untuk kasus penggunaan OP mungkin terlihat seperti ini:

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

Ini Fiddle , jika Anda ingin mencobanya. Lib tersedia di GitHub , memiliki dokumentasi terperinci, dan banyak demo langsung.

2Muat
sumber