Validasi bidang setelah pengguna meninggalkan bidang

92

Dengan AngularJS, saya dapat menggunakan ng-pristineatau ng-dirtyuntuk mendeteksi jika pengguna telah memasuki lapangan. Namun, saya ingin melakukan validasi sisi klien hanya setelah pengguna meninggalkan area lapangan. Ini karena ketika pengguna memasukkan bidang seperti email atau telepon, mereka akan selalu mendapatkan kesalahan sampai mereka selesai mengetik email lengkap mereka, dan ini bukan pengalaman pengguna yang optimal.

Contoh


MEMPERBARUI:

Angular sekarang dikirimkan dengan acara blur khusus: https://docs.angularjs.org/api/ng/directive/ngBlur

mcranston18.dll
sumber
3
Saya rasa ini adalah sesuatu yang seharusnya didukung oleh
angularjs
Mungkin. Validasi formulir bawaan bekerja cukup baik sehingga saya tidak tahu apakah saya ingin ini dibangun. Lagi pula, sebagian besar kasus penggunaan saya ingin Angular segera memvalidasi. yaitu dalam bidang angka, saya ingin formulir segera dibatalkan jika pengguna mulai mengetik huruf.
mcranston18
11
validasi pada blur adalah umum sejak 15 tahun ...
Olivvv

Jawaban:

75

Angular 1.3 sekarang memiliki ng-model-options, dan Anda dapat menyetel opsi ke { 'updateOn': 'blur'}misalnya, dan Anda bahkan dapat menolak, ketika penggunaan terlalu cepat, atau Anda ingin menyimpan beberapa operasi DOM yang mahal (seperti penulisan model ke beberapa tempat DOM dan Anda tidak ingin siklus $ digest terjadi pada setiap tombol mati)

https://docs.angularjs.org/guide/forms#custom-triggers dan https://docs.angularjs.org/guide/forms#non-immediate-debounce-model-updates

Secara default, setiap perubahan pada konten akan memicu pembaruan model dan validasi formulir. Anda bisa mengganti perilaku ini menggunakan direktif ngModelOptions untuk mengikat hanya ke daftar kejadian yang ditentukan. Ie ng-model-options = "{updateOn: 'blur'}" akan memperbarui dan memvalidasi hanya setelah kontrol kehilangan fokus. Anda dapat menyetel beberapa acara menggunakan daftar yang dipisahkan spasi. Yaitu ng-model-options = "{updateOn: 'mousedown blur'}"

Dan lepaskan

Anda bisa menunda pembaruan / validasi model dengan menggunakan kunci debounce dengan direktif ngModelOptions. Penundaan ini juga berlaku untuk parser, validator, dan flag model seperti $ dirty atau $ pristine.

Ie ng-model-options = "{debounce: 500}" akan menunggu setengah detik sejak konten terakhir berubah sebelum memicu pembaruan model dan validasi formulir.

pocesar
sumber
2
Dalam kebanyakan kasus, ini tidak menyelesaikan masalah, karena Anda mungkin masih ingin menerima ng-changeacara sebelum pengguna meninggalkan lapangan. Misalnya, dalam kasus saya, saya meminta data baru dari API saya segera setelah bidang input valid, tetapi saya tidak ingin menampilkan pesan kesalahan segera karena tidak valid - saya hanya ingin melakukan ini jika tidak valid dan peristiwa blur terjadi.
Tom
@Tom saya sedang menguji 1.3.0 rc-3dan tidak menyala changesampai blur, periksa ini: plnkr.co/edit/RivVU3r7xfHO1hUybqMu?p=preview
Gill Bates
Ini harus menjadi jawaban teratas yang diterima. Mengapa menambahkan arahan, jika Anda dapat menambahkan satu properti?
Dan Hulton
92

Dari versi 1.3.0 ini dapat dengan mudah dilakukan $touched, yang benar setelah pengguna meninggalkan lapangan.

<p ng-show="form.field.$touched && form.field.$invalid">Error</p>

https://docs.angularjs.org/api/ng/type/ngModel.NgModelController

pengguna1506145
sumber
1
Bukankah itu masih mengeksekusi validasi setelah setiap key-up? OP menyatakan dia ingin melakukan validasi hanya setelah pengguna meninggalkan lapangan.
comecme
@comecme ya itu akan, default ng-model-options updateOn: 'default'yang artinya, untuk masukan, tombol atas, dan untuk pilihan, pada perubahan
pocesar
5
Namun, kelemahan besar di sini adalah Anda tidak akan memiliki pengalaman validasi yang sama jika kembali ke lapangan. Anda kembali ke titik awal, di mana itu memvalidasi pada setiap penekanan tombol, karena sudah diatur ke disentuh. Saya terkejut ini mendapat begitu banyak suara positif.
dudewad
3
@dudewad itu benar, tapi menurut saya itu perilaku yang diinginkan UX bijaksana - jika Anda kembali ke bidang yang tidak valid, pesan kesalahan harus tetap ada sampai nilainya valid, tidak sampai Anda mengetik huruf pertama.
danbars
@dudewad Bukankah akan membantu pengguna untuk memvalidasi pada setiap pers setelah mereka mengetahui bahwa bidang tidak valid setelah pertama kali mereka mengisinya? Setelah mereka mengetahui bahwa field tersebut tidak valid, kemungkinan besar mereka ingin segera mengetahui kapan field tersebut valid dan memvalidasi pada setiap penekanan tombol akan mempercepat proses itu, mungkin?
Alan Dunning
32

Saya menyelesaikan ini dengan memperluas apa yang disarankan @jlmcdonald. Saya membuat arahan yang secara otomatis akan diterapkan ke semua elemen input dan pilih:

var blurFocusDirective = function () {
    return {
        restrict: 'E',
        require: '?ngModel',
        link: function (scope, elm, attr, ctrl) {
            if (!ctrl) {
                return;
            }

            elm.on('focus', function () {
                elm.addClass('has-focus');

                scope.$apply(function () {
                    ctrl.hasFocus = true;
                });
            });

            elm.on('blur', function () {
                elm.removeClass('has-focus');
                elm.addClass('has-visited');

                scope.$apply(function () {
                    ctrl.hasFocus = false;
                    ctrl.hasVisited = true;
                });
            });

            elm.closest('form').on('submit', function () {
                elm.addClass('has-visited');

                scope.$apply(function () {
                    ctrl.hasFocus = false;
                    ctrl.hasVisited = true;
                });
            });

        }
    };
};

app.directive('input', blurFocusDirective);
app.directive('select', blurFocusDirective);

Ini akan menambah has-focusdan has-visitedmengklasifikasikan berbagai elemen saat pengguna memfokuskan / mengunjungi elemen. Anda kemudian dapat menambahkan kelas ini ke aturan CSS Anda untuk menunjukkan kesalahan validasi:

input.has-visited.ng-invalid:not(.has-focus) {
    background-color: #ffeeee;   
}

Ini berfungsi dengan baik karena elemen masih mendapatkan $ properti tidak valid dll, tetapi CSS dapat digunakan untuk memberikan pengalaman yang lebih baik kepada pengguna.

lambinator
sumber
2
Mengapa '? NgModel' diperlukan, bukan hanya 'ngModel'? Saya mengerti bahwa ngModel harus ada di sana, tetapi mengapa ada tanda tanya?
Noremac
2
Terima kasih untuk idenya. Harus menunjukkan bahwa itu membutuhkan jQuery (saya pikir - tidak bisa melihat .closest () di jql).
iandotkelly
1
Tidak semua elemen <input> didukung oleh ngModel (mis. Input type = submit). Itu? hanya membutuhkan ngModel pada elemen yang mendukungnya.
chmanie
5
Sejujurnya, Anda tidak boleh menyimpang dari cara sudut pengaturan status bidang masukan seperti formName.inputName.$dirty. Saya sarankan mengedit direktif untuk menulis boolean serupa formName.inputName.$focused, daripada menggunakan nama kelas untuk blur dan boolean untuk validasi.
Tom
17

Saya berhasil melakukan ini dengan sedikit CSS yang cukup sederhana. Hal ini mengharuskan pesan kesalahan menjadi saudara dari input yang terkait dengannya, dan memiliki kelas error.

:focus ~ .error {
    display:none;
}

Setelah memenuhi kedua persyaratan tersebut, ini akan menyembunyikan pesan kesalahan apa pun yang terkait dengan bidang input terfokus, sesuatu yang menurut saya harus dilakukan angularjs. Sepertinya pengawasan.

nicholas
sumber
2
Ahh. Ini adalah cara cerdas untuk pergi. Saya lebih suka ini daripada menulis javascript untuk melakukannya.
dek
5
Kelemahan dari pendekatan ini adalah ketika seseorang mengoreksi kesalahan, kesalahan tidak lagi terlihat yang tidak ideal. Idealnya adalah kesalahan tidak muncul sampai buram tetapi tidak hilang sampai diperbaiki.
Chris Nicola
4

Ini tampaknya diterapkan sebagai standar dalam versi sudut yang lebih baru.

Kelas ng-untouched dan ng-touch ditetapkan masing-masing sebelum dan setelah pengguna memiliki fokus pada elemen yang divalidasi.

CSS

input.ng-touched.ng-invalid {
   border-color: red;
}
Arg0n
sumber
4

Mengenai solusi @ lambinator ... Saya mendapatkan kesalahan berikut di angular.js 1.2.4:

Kesalahan: [$ rootScope: inprog] $ digest sedang berlangsung

Saya tidak yakin apakah saya melakukan sesuatu yang salah atau jika ini adalah perubahan di Angular, tetapi menghapus scope.$applypernyataan menyelesaikan masalah dan kelas / negara bagian masih diperbarui.

Jika Anda juga melihat kesalahan ini, cobalah yang berikut ini:

var blurFocusDirective = function () {
  return {
    restrict: 'E',
    require: '?ngModel',
    link: function (scope, elm, attr, ctrl) {
      if (!ctrl) {
        return;
      }
      elm.on('focus', function () {
        elm.addClass('has-focus');
        ctrl.$hasFocus = true;
      });

      elm.on('blur', function () {
        elm.removeClass('has-focus');
        elm.addClass('has-visited');
        ctrl.$hasFocus = false;
        ctrl.$hasVisited = true;
      });

      elm.closest('form').on('submit', function () {
        elm.addClass('has-visited');

        scope.$apply(function () {
          ctrl.hasFocus = false;
          ctrl.hasVisited = true;
        });
      });
    }
  };
};
app.directive('input', blurFocusDirective);
app.directive('select', blurFocusDirective);
melcher
sumber
2

Mungkin berhasil bagi Anda untuk menulis perintah khusus yang membungkus metode javascript blur () (dan menjalankan fungsi validasi saat dipicu); ada masalah Angular yang memiliki sampel (serta arahan umum yang dapat mengikat ke acara lain yang tidak didukung oleh Angular):

https://github.com/angular/angular.js/issues/1277

Jika Anda tidak ingin pergi ke rute itu, pilihan Anda yang lain adalah mengatur $ watch di lapangan, lagi-lagi memicu validasi saat bidang diisi.

jlmcdonald.dll
sumber
2

Untuk mempelajari lebih lanjut jawaban yang diberikan, Anda dapat menyederhanakan pemberian tag input dengan menggunakan pseudo-class CSS3 dan hanya menandai bidang yang dikunjungi dengan kelas untuk menunda menampilkan kesalahan validasi hingga pengguna kehilangan fokus pada bidang:

(Contoh membutuhkan jQuery)

JavaScript

module = angular.module('app.directives', []);
module.directive('lateValidateForm', function () {
    return {
        restrict: 'AC',
        link: function (scope, element, attrs) {
            $inputs = element.find('input, select, textarea');

            $inputs.on('blur', function () {
                $(this).addClass('has-visited');
            });

            element.on('submit', function () {
                $inputs.addClass('has-visited');
            });
        }
    };
});

CSS

input.has-visited:not(:focus):required:invalid,
textarea.has-visited:not(:focus):required:invalid,
select.has-visited:not(:focus):required:invalid {
  color: #b94a48;
  border-color: #ee5f5b;
}

HTML

<form late-validate-form name="userForm">
  <input type="email" name="email" required />
</form>
Patrick Glandien
sumber
2

berdasarkan jawaban @nicolas .. Pure CSS harus triknya, itu hanya akan menampilkan pesan kesalahan pada blur

<input type="email" id="input-email" required
               placeholder="Email address" class="form-control" name="email"
               ng-model="userData.email">
        <p ng-show="form.email.$error.email" class="bg-danger">This is not a valid email.</p>

CSS

.ng-invalid:focus ~ .bg-danger {
     display:none;
}
keithics
sumber
Ini bagus, tetapi pesan kesalahan akan hilang ketika pengguna mengklik kembali di lapangan, yang tidak ideal.
Bennett McElwee
2

Berikut adalah contoh penggunaan ng-messages (tersedia dalam angular 1.3) dan custom directive.

Pesan validasi ditampilkan dalam mode blur untuk pertama kalinya pengguna meninggalkan kolom input, tetapi ketika dia mengoreksi nilainya, pesan validasi segera dihapus (tidak buram lagi).

JavaScript

myApp.directive("validateOnBlur", [function() {
    var ddo = {
        restrict: "A",
        require: "ngModel",
        scope: {},
        link: function(scope, element, attrs, modelCtrl) {
            element.on('blur', function () {
                modelCtrl.$showValidationMessage = modelCtrl.$dirty;
                scope.$apply();
            });
        }
    };
    return ddo;
}]);

HTML

<form name="person">
    <input type="text" ng-model="item.firstName" name="firstName" 
        ng-minlength="3" ng-maxlength="20" validate-on-blur required />
    <div ng-show="person.firstName.$showValidationMessage" ng-messages="person.firstName.$error">
        <span ng-message="required">name is required</span>
        <span ng-message="minlength">name is too short</span>
        <span ng-message="maxlength">name is too long</span>
    </div>
</form>

PS. Jangan lupa untuk mendownload dan menyertakan ngMessages dalam modul Anda:

var myApp = angular.module('myApp', ['ngMessages']);
jurgemar.dll
sumber
1

ng-model-options di AngularJS 1.3 (versi beta saat tulisan ini dibuat) didokumentasikan untuk mendukung {updateOn: 'blur'}. Untuk versi sebelumnya, sesuatu seperti berikut ini berhasil untuk saya:

myApp.directive('myForm', function() {
  return {
    require: 'form',
    link: function(scope, element, attrs, formController) {
      scope.validate = function(name) {
        formController[name].isInvalid
            = formController[name].$invalid;
      };
    }
  };
});

Dengan template seperti ini:

<form name="myForm" novalidate="novalidate" data-my-form="">
<input type="email" name="eMail" required="required" ng-blur="validate('eMail')" />
<span ng-show="myForm.eMail.isInvalid">Please enter a valid e-mail address.</span>
<button type="submit">Submit Form</button>
</form>
Terence Bandoian
sumber
1

Gunakan status bidang $ tersentuh Bidang telah disentuh untuk ini seperti yang ditunjukkan pada contoh di bawah ini.

<div ng-show="formName.firstName.$touched && formName.firstName.$error.required">
    You must enter a value
</div>
Kushal Sharma
sumber
0

Anda dapat secara dinamis menyetel kelas has-error css (dengan asumsi Anda menggunakan bootstrap) menggunakan ng-class dan properti pada lingkup pengontrol terkait:

plunkr: http://plnkr.co/edit/HYDlaTNThZE02VqXrUCH?p=info

HTML:

<div ng-class="{'has-error': badEmailAddress}">
    <input type="email" class="form-control" id="email" name="email"
        ng-model="email" 
        ng-blur="emailBlurred(email.$valid)">
</div>

Pengontrol:

$scope.badEmailAddress = false;

$scope.emailBlurred = function (isValid) {
    $scope.badEmailAddress = !isValid;
};
Mike
sumber
0

Jika Anda menggunakan bootstrap 3 dan lesscss, Anda dapat mengaktifkan validasi blur dengan sedikit cuplikan berikut:

:focus ~ .form-control-feedback.glyphicon-ok {
  display:none;
}

:focus ~ .form-control-feedback.glyphicon-remove {
  display:none;
}

.has-feedback > :focus {
  & {
    .form-control-focus();
  }
}
Stefan
sumber
0

outI menggunakan arahan. Ini kodenya:

app.directive('onBlurVal', function () {
    return {
        restrict: 'A',
        link: function (scope, element, attrs, controller) {

            element.on('focus', function () {
                element.next().removeClass('has-visited');
                element.next().addClass('has-focus');
            });

            element.on('blur', function () {

                element.next().removeClass('has-focus');
                element.next().addClass('has-visited');
            });
        }
    }
})

Semua kontrol input saya memiliki elemen span sebagai elemen berikutnya, di mana pesan validasi saya ditampilkan dan direktif sebagai atribut ditambahkan ke setiap kontrol input.

Saya juga memiliki kelas css (opsional) .has-focus dan has-visiting di file css saya yang Anda lihat direferensikan dalam direktif.

CATATAN: ingatlah untuk menambahkan 'on-blur-val' persis seperti ini ke kontrol masukan Anda tanpa apostrof

pengguna3676608
sumber
0

Dengan menggunakan ng-focus Anda dapat mencapai tujuan Anda. Anda perlu memberikan fokus ng di bidang masukan Anda. Dan saat menulis turunan ng-show Anda, Anda harus menulis logika yang tidak sama juga. Seperti kode di bawah ini:

<input type="text" class="form-control" name="inputPhone" ng-model="demo.phoneNumber" required ng-focus> <div ng-show="demoForm.inputPhone.$dirty && demoForm.inputPhone.$invalid && !demoForm.inputPhone.$focused"></div>

sinar
sumber
0

Kita bisa menggunakan fungsi onfocus dan onblur. Sederhana dan terbaik.

<body ng-app="formExample">
  <div ng-controller="ExampleController">
  <form novalidate class="css-form">
    Name: <input type="text" ng-model="user.name" ng-focus="onFocusName='focusOn'" ng-blur="onFocusName=''" ng-class="onFocusName" required /><br />
    E-mail: <input type="email" ng-model="user.email" ng-focus="onFocusEmail='focusOn'" ng-blur="onFocusEmail=''" ng-class="onFocusEmail" required /><br />
  </form>
</div>

<style type="text/css">
 .css-form input.ng-invalid.ng-touched {
    border: 1px solid #FF0000;
    background:#FF0000;
   }
 .css-form input.focusOn.ng-invalid {
    border: 1px solid #000000;
    background:#FFFFFF;
 }
</style>

Coba di sini:

http://plnkr.co/edit/NKCmyru3knQiShFZ96tp?p=preview

Naveen Kumar
sumber