jQuery Validate - membutuhkan setidaknya satu bidang dalam grup untuk diisi

98

Saya menggunakan Plugin jQuery Validate yang sangat baik untuk memvalidasi beberapa formulir. Pada satu formulir, saya perlu memastikan bahwa pengguna mengisi setidaknya satu dari sekelompok bidang. Saya rasa saya punya solusi yang cukup bagus, dan ingin membagikannya. Harap sarankan perbaikan apa pun yang dapat Anda pikirkan.

Karena tidak menemukan cara bawaan untuk melakukan ini, saya mencari dan menemukan metode validasi khusus Rebecca Murphey , yang sangat membantu.

Saya meningkatkan ini dengan tiga cara:

  1. Untuk membiarkan Anda meneruskan pemilih untuk grup bidang
  2. Untuk membiarkan Anda menentukan berapa banyak dari grup itu yang harus diisi agar validasi lulus
  3. Untuk menampilkan semua input dalam grup sebagai lolos validasi segera setelah salah satunya lolos validasi. (Lihat teriakan untuk Nick Craver di bagian akhir.)

Jadi bisa dibilang "setidaknya X masukan yang cocok dengan pemilih Y harus diisi."

Hasil akhirnya, dengan markup seperti ini:

<input class="productinfo" name="partnumber">
<input class="productinfo" name="description">

... adalah sekelompok aturan seperti ini:

// Both these inputs input will validate if 
// at least 1 input with class 'productinfo' is filled
partnumber: {
   require_from_group: [1,".productinfo"]
  }
description: {
   require_from_group: [1,".productinfo"]
}

Item # 3 mengasumsikan bahwa Anda menambahkan kelas .checkedke pesan kesalahan Anda setelah validasi berhasil. Anda dapat melakukan ini sebagai berikut, seperti yang ditunjukkan di sini .

success: function(label) {  
        label.html(" ").addClass("checked"); 
}

Seperti pada demo yang ditautkan di atas, saya menggunakan CSS untuk memberikan setiap span.errorgambar X sebagai latar belakangnya, kecuali jika ia memiliki kelasnya.checked , dalam hal ini gambar itu mendapat gambar tanda centang.

Inilah kode saya sejauh ini:

jQuery.validator.addMethod("require_from_group", function(value, element, options) {
    var numberRequired = options[0];
    var selector = options[1];
    //Look for our selector within the parent form
    var validOrNot = $(selector, element.form).filter(function() {
         // Each field is kept if it has a value
         return $(this).val();
         // Set to true if there are enough, else to false
      }).length >= numberRequired;

    // The elegent part - this element needs to check the others that match the
    // selector, but we don't want to set off a feedback loop where each element
    // has to check each other element. It would be like:
    // Element 1: "I might be valid if you're valid. Are you?"
    // Element 2: "Let's see. I might be valid if YOU'RE valid. Are you?"
    // Element 1: "Let's see. I might be valid if YOU'RE valid. Are you?"
    // ...etc, until we get a "too much recursion" error.
    //
    // So instead we
    //  1) Flag all matching elements as 'currently being validated'
    //  using jQuery's .data()
    //  2) Re-run validation on each of them. Since the others are now
    //     flagged as being in the process, they will skip this section,
    //     and therefore won't turn around and validate everything else
    //  3) Once that's done, we remove the 'currently being validated' flag
    //     from all the elements
    if(!$(element).data('being_validated')) {
    var fields = $(selector, element.form);
    fields.data('being_validated', true);
    // .valid() means "validate using all applicable rules" (which 
    // includes this one)
    fields.valid();
    fields.data('being_validated', false);
    }
    return validOrNot;
    // {0} below is the 0th item in the options field
    }, jQuery.format("Please fill out at least {0} of these fields."));

Hore!

Teriak

Sekarang untuk teriakan itu - awalnya, kode saya hanya secara membabi buta menyembunyikan pesan kesalahan di bidang lain yang cocok alih-alih memvalidasinya ulang, yang berarti bahwa jika ada masalah lain (seperti 'hanya angka yang diizinkan dan Anda memasukkan huruf') , itu disembunyikan sampai pengguna mencoba mengirim. Ini karena saya tidak tahu bagaimana menghindari loop umpan balik yang disebutkan dalam komentar di atas. Saya tahu pasti ada jalan, jadi saya mengajukan pertanyaan , dan Nick Craver mencerahkan saya. Terima kasih, Nick!

Pertanyaan Terselesaikan

Ini awalnya adalah pertanyaan semacam "biarkan saya berbagi ini dan melihat apakah ada yang bisa menyarankan perbaikan". Meskipun saya masih menerima umpan balik, saya pikir itu cukup lengkap untuk saat ini. (Bisa lebih pendek, tapi saya ingin mudah dibaca dan belum tentu ringkas.) Jadi nikmatilah!

Perbarui - sekarang menjadi bagian dari jQuery Validation

Ini secara resmi ditambahkan ke jQuery Validation pada 4/3/2012.

Nathan Long
sumber
Selain itu, lihat aturan yang terkait erat - "Lewati bidang ini, atau isi setidaknya X" - stackoverflow.com/questions/1888976/…
Nathan Long
Mengapa satu masukan sewenang-wenang bertanggung jawab untuk memeriksa apakah masukan lain sudah diisi? Ini tidak masuk akal. Mungkin Anda bisa memasukkan sedikit markup dengan elemen yang terlibat?
montrealist
@dalbaeb - Saya mengklarifikasi sedikit contoh. Ini tidak berarti bahwa satu masukan yang sewenang-wenang bertanggung jawab untuk memeriksa masukan lainnya; setiap masukan dalam kelompok bertanggung jawab untuk memeriksa yang lainnya.
Nathan Long
Itulah yang saya pikirkan, terima kasih banyak!
montrealist
3
Terima kasih, ini berfungsi untuk saya, tetapi bidang wajib lainnya dalam formulir sekarang tidak merespons lagi kecuali mereka mendapatkan dan kehilangan fokus setelah pemeriksaan. (Seseorang menambahkan ini sebagai jawaban atas pertanyaan Anda yang lain, tetapi harus ditandai karena ini bukan jawaban).
mydoghasworms

Jawaban:

21

Itu solusi yang sangat baik, Nathan. Terima kasih banyak.

Berikut cara membuat kode di atas berfungsi, jika seseorang mengalami masalah dalam mengintegrasikannya, seperti yang saya lakukan:

Kode di dalam file tambahan-metode.js :

jQuery.validator.addMethod("require_from_group", function(value, element, options) {
...// Nathan's code without any changes
}, jQuery.format("Please fill out at least {0} of these fields."));

// "filone" is the class we will use for the input elements at this example
jQuery.validator.addClassRules("fillone", {
    require_from_group: [1,".fillone"]
});

Kode di dalam file html :

<input id="field1" class="fillone" type="text" value="" name="field1" />
<input id="field2" class="fillone" type="text" value="" name="field2" />
<input id="field3" class="fillone" type="text" value="" name="field3" />
<input id="field4" class="fillone" type="text" value="" name="field4" />

Jangan lupa untuk menyertakan file tambahan-metode.js!


sumber
Senang ini membantu Anda, dan terima kasih telah memberikan informasi. Namun, alih-alih melakukan metode addClassRules, saya lebih suka menggunakan berbagai aturan pada setiap formulir individu. Jika Anda membuka halaman ini ( jquery.bassistance.de/validate/demo/milk ) dan mengklik "tampilkan skrip yang digunakan pada halaman ini", Anda akan melihat sebuah contoh. Saya mengambil satu langkah lebih jauh: Saya mendeklarasikan sebuah array yang disebut "rules", lalu secara terpisah, saya menggunakannya dengan var validator = $ ('# formtovalidate'). Validate (rules);
Nathan Long
Pemikiran lain: kelas 'fillone' yang Anda tunjukkan di sini bisa jadi bermasalah. Bagaimana jika, pada formulir yang sama, Anda perlu membutuhkan setidaknya satu nomor bagian, DAN setidaknya satu nama kontak? Aturan Anda akan mengizinkan 0 nama kontak selama setidaknya ada satu nomor bagian. Saya pikir lebih baik untuk menetapkan aturan seperti require_from_group: [1,".partnumber"]dan ...[1,".contactname"]untuk memastikan Anda memvalidasi hal yang benar.
Nathan Long
6

Solusi bagus. Namun, saya memiliki masalah aturan lain yang diperlukan tidak berfungsi. Menjalankan .valid () terhadap formulir memperbaiki masalah ini untuk saya.

if(!$(element).data('being_validated')) {
  var fields = $(selector, element.form);
  fields.data('being_validated', true); 
  $(element.form).valid();
  fields.data('being_validated', false);
}
sean
sumber
1
Terima kasih Sean, saya juga mengalami masalah ini. Namun, ada satu masalah dengan solusi ini, ketika pengguna membuka formulir untuk pertama kalinya - segera setelah ia mengisi bidang pertama yang diminta dari grup, semua bidang formulir lainnya akan divalidasi dan dengan demikian ditandai sebagai salah. Cara saya menyelesaikan ini adalah dengan menambahkan penangan form.submit () sebelum membuat instance validator, di mana saya menetapkan bendera validator.formSubmit = true. Dalam metode require-from-group saya kemudian memeriksa flag tersebut; jika itu ada saya lakukan $(element.form).valid();, kalau tidak saya lakukan fields.valid();.
Christof
Adakah yang bisa menjelaskan apa yang sebenarnya terjadi di sini? Saya memiliki aturan yang cukup mirip, yang berhasil tetapi kami belum mengatasi masalah validasi ulang (bidang lain dalam grup masih ditandai sebagai tidak valid). Tapi sekarang saya mengalami bahwa formulir dikirimkan meskipun tidak valid. Jika kolom yang dikelompokkan valid maka tidak masuk ke submithandler dan jika tidak valid memasukkan invalidHandler tapi tetap mengirimkan! Saya akan mengatakan ini adalah bug yang cukup serius di plugin validasi? Bahwa aturan mengembalikan valid hanya berlaku untuk aturan itu (bahkan tidak seluruh bidang) jadi mengapa pengiriman formulir tidak valid?
Adam
Saya telah menyelidiki lebih lanjut dan itu adalah bidang di depan grup yang tidak memvalidasi dengan benar. Saya telah menanyakan ini sebagai pertanyaan terpisah (dengan solusi parsial yang telah saya temukan): stackoverflow.com/questions/12690898/…
Adam
4

Terima kasih sean. Itu memperbaiki masalah yang saya alami dengan kode yang mengabaikan aturan lain.

Saya juga membuat beberapa perubahan sehingga pesan 'Harap isi setidaknya 1 bidang ..' muncul di div terpisah, bukan setelah semua bidang.

masukkan ke dalam bentuk skrip validasi

showErrors: function(errorMap, errorList){
            $("#form_error").html("Please fill out at least 1 field before submitting.");
            this.defaultShowErrors();
        },

tambahkan ini di suatu tempat di halaman

<div class="error" id="form_error"></div>

tambahkan ke fungsi addMethod metode require_from_group

 if(validOrNot){
    $("#form_error").hide();
}else{
    $("#form_error").show();
}
......
}, jQuery.format(" &nbsp;(!)"));
Walter Kelly
sumber
4

Saya telah mengirimkan tambalan yang tidak mengalami masalah yang terjadi pada versi saat ini (di mana opsi "diperlukan" berhenti berfungsi dengan baik di bidang lain, diskusi tentang masalah dengan versi saat ini ada di github .

Contoh di http://jsfiddle.net/f887W/10/

jQuery.validator.addMethod("require_from_group", function (value, element, options) {
var validator = this;
var minRequired = options[0];
var selector = options[1];
var validOrNot = jQuery(selector, element.form).filter(function () {
    return validator.elementValue(this);
}).length >= minRequired;

// remove all events in namespace require_from_group
jQuery(selector, element.form).off('.require_from_group');

//add the required events to trigger revalidation if setting is enabled in the validator
if (this.settings.onkeyup) {
    jQuery(selector, element.form).on({
        'keyup.require_from_group': function (e) {
            jQuery(selector, element.form).valid();
        }
    });
}

if (this.settings.onfocusin) {
    jQuery(selector, element.form).on({
        'focusin.require_from_group': function (e) {
            jQuery(selector, element.form).valid();
        }
    });
}

if (this.settings.click) {
    jQuery(selector, element.form).on({
        'click.require_from_group': function (e) {
            jQuery(selector, element.form).valid();
        }
    });
}

if (this.settings.onfocusout) {
    jQuery(selector, element.form).on({
        'focusout.require_from_group': function (e) {
            jQuery(selector, element.form).valid();
        }
    });
}

return validOrNot;
}, jQuery.format("Please fill at least {0} of these fields."));
docflabby
sumber
3

Memulai nama variabel dengan $ diperlukan dalam PHP, tetapi cukup aneh (IMHO) dalam Javascript. Juga, saya yakin Anda menyebutnya sebagai "$ module" dua kali dan "module" sekali, bukan? Tampaknya kode ini seharusnya tidak berfungsi.

Juga, saya tidak yakin apakah itu sintaks plugin jQuery normal, tetapi saya mungkin menambahkan komentar di atas panggilan addMethod Anda, menjelaskan apa yang Anda capai. Bahkan dengan deskripsi teks Anda di atas, sulit untuk mengikuti kode, karena saya tidak paham dengan apa yang dimaksud fieldset,: terisi, nilai, elemen, atau selektor. Mungkin sebagian besar dari ini jelas bagi seseorang yang akrab dengan plugin Validasi, jadi gunakan penilaian tentang berapa jumlah penjelasan yang tepat.

Mungkin Anda bisa membuat beberapa vars untuk mendokumentasikan sendiri kode tersebut; Suka,

var atLeastOneFilled = module.find(...).length > 0;
if (atLeastOneFilled) {
  var stillMarkedWithErrors = module.find(...).next(...).not(...);
  stillMarkedWithErrors.text("").addClass(...)

(dengan asumsi saya benar-benar mengerti arti dari potongan kode Anda ini! :))

Saya tidak begitu yakin apa artinya "modul", sebenarnya - apakah ada nama yang lebih spesifik yang dapat Anda berikan untuk variabel ini?

Kode yang bagus, secara keseluruhan!

Michael Gundlach
sumber
Terima kasih atas sarannya - Saya telah mengklarifikasi nama variabel dan memecah kode agar sedikit lebih mudah dibaca.
Nathan Long
2

Karena formulir yang saya kerjakan memiliki beberapa daerah kloning dengan masukan yang dikelompokkan seperti ini, saya memberikan argumen tambahan ke konstruktor require_from_group, mengubah tepat satu baris fungsi addMethod Anda:

var commonParent = $(element).parents(options[2]);

dan dengan cara ini pemilih, ID, atau nama elemen dapat diberikan satu kali:

jQuery.validator.addClassRules("reqgrp", {require_from_group: [1, ".reqgrp", 'fieldset']});

dan validator akan membatasi validasi ke elemen dengan kelas itu hanya di dalam setiap fieldset, daripada mencoba menghitung semua elemen yang digolongkan .reqgrp di formulir.

Andrew Roazen
sumber
2

Inilah retakan saya pada jawaban Rocket Hazmat, mencoba menyelesaikan masalah bidang lain yang ditentukan juga perlu divalidasi, tetapi menandai semua bidang sebagai valid pada pengisian yang berhasil.

jQuery.validator.addMethod("require_from_group", function(value, element, options){
    var numberRequired = options[0],
    selector = options[1],
    $fields = $(selector, element.form),
    validOrNot = $fields.filter(function() {
        return $(this).val();
    }).length >= numberRequired,
    validator = this;
    if(!$(element).data('being_validated')) {
        $fields.data('being_validated', true).each(function(){
            validator.valid(this);
        }).data('being_validated', false);
    }
    if (validOrNot) {
    $(selector).each(function() {
            $(this).removeClass('error');
            $('label.error[for='+$(this).attr('id')+']').remove();
        });
    }
    return validOrNot;
}, jQuery.format("Please fill out at least {0} of these fields."));

Satu-satunya masalah yang tersisa dengan ini sekarang adalah kasus tepi di mana bidang tersebut kosong, lalu diisi, lalu kosongkan lagi ... dalam hal ini kesalahan akan diterapkan ke bidang tunggal bukan grup. Tetapi tampaknya sangat tidak mungkin terjadi dengan frekuensi apa pun dan secara teknis masih berfungsi dalam kasus itu.

squarecandy
sumber
Tidak ada gunanya jawaban ini karena metode / aturan ini diintegrasikan ke dalam plugin pada bulan April 2012.
Sparky
Saya memiliki masalah yang sama dengan yang dimiliki Rocket Hazmat dengan metode yang sekarang dikirimkan dengan validator. Ini memvalidasi bahwa satu grup bidang, tetapi tidak ada bidang lain yang menggunakan metode lain yang divalidasi. Jawaban ini merupakan upaya untuk menyelesaikan masalah itu. Jika Anda memiliki solusi yang lebih baik, beri tahu saya.
squarecandy
Sampai pengembang memperbaiki masalah secara permanen, daripada menambah kebingungan, saya hanya merekomendasikan solusi sementara apa pun yang didukung di sini: github.com/jzaefferer/jquery-validation/issues/412
Sparky
1

Saya mengalami masalah dengan aturan lain yang tidak diperiksa sehubungan dengan ini, jadi saya mengubah:

fields.valid();

Untuk ini:

var validator = this;
fields.each(function(){
   validator.valid(this);
});

Saya juga membuat beberapa peningkatan (pribadi), dan ini adalah versi yang saya gunakan:

jQuery.validator.addMethod("require_from_group", function(value, element, options){
    var numberRequired = options[0],
    selector = options[1],
    $fields = $(selector, element.form),
    validOrNot = $fields.filter(function() {
        return $(this).val();
    }).length >= numberRequired,
    validator = this;
    if(!$(element).data('being_validated')) {
        $fields.data('being_validated', true).each(function(){
            validator.valid(this);
        }).data('being_validated', false);
    }
    return validOrNot;
}, jQuery.format("Please fill out at least {0} of these fields."));
Roket Hazmat
sumber
Berfungsi membuat bidang lain divalidasi lagi, tetapi sekarang, ketika semua bidang grup ditandai sebagai tidak valid dan Anda mengisi satu, hanya satu yang divalidasi. Setidaknya untuk saya?
Christof
@ Chris - lihat jawaban baru saya yang didasarkan pada jawaban ini dan membahasnya.
squarecandy
0

Terima kasih, Nathan. Anda menghemat banyak waktu.

Namun, saya harus memperhatikan bahwa aturan ini belum siap jQuery.noConflict (). Jadi, seseorang harus mengganti semua $ dengan jQuery untuk bekerja, katakanlah,var $j = jQuery.noConflict()

Dan saya punya pertanyaan: bagaimana saya membuatnya berperilaku seperti aturan bawaan? Misalnya, jika saya memasukkan email, pesan "Harap masukkan email yang valid" menghilang secara otomatis tetapi jika saya mengisi salah satu kolom grup pesan kesalahan tetap ada.

Rinat
sumber
Anda benar - saya tidak mempertimbangkan situasi non-konflik. Saya mungkin memperbaruinya di masa mendatang, tetapi Anda dapat dengan mudah melakukan pencarian-dan-ganti jika Anda mau. Untuk pertanyaan kedua Anda, saya tidak melihat masalah yang sama. Dengan uji cepat, jika satu bidang dari grup diperlukan, segera setelah saya mengetik apa pun, seluruh grup melewati aturan itu. Jika diperlukan lebih dari satu, segera setelah yang terakhir diisi dan kehilangan fokus, seluruh kelompok lolos. Apa itu yang kamu lihat?
Nathan Long
hmm untuk beberapa alasan markup kacau dan saya tidak berhasil memperbaikinya
Rinat
Rinat - dapatkah Anda menyederhanakan dan mempersempit masalah? Coba gunakan kode saya pada formulir yang lebih sederhana yang tidak memerlukan perubahan non-konflik. Lakukan formulir paling sederhana yang mungkin Anda dapat mengujinya, dan membuatnya berfungsi terlebih dahulu.
Nathan Long