jQuery Validate - bir gruptaki en az bir alanın doldurulmasını zorunlu kılın


98

Bazı formları doğrulamak için mükemmel jQuery Validate Eklentisini kullanıyorum . Bir formda, kullanıcının bir grup alandan en az birini doldurduğundan emin olmam gerekiyor. Sanırım oldukça iyi bir çözümüm var ve bunu paylaşmak istedim. Lütfen aklınıza gelebilecek iyileştirmeleri önerin.

Bunu yapmanın yerleşik bir yolunu bulamayınca, Rebecca Murphey'in özel doğrulama yöntemini araştırdım ve buldum , bu çok yardımcı oldu.

Bunu üç şekilde geliştirdim:

  1. Alan grubu için bir seçiciye geçmenize izin vermek için
  2. Doğrulamanın geçmesi için o gruptan kaçının doldurulması gerektiğini belirtmenize izin vermek için
  3. Gruptaki tüm girdileri, biri doğrulamayı geçer geçmez geçerliliğini geçerken göstermek için. ( Sonunda Nick Craver'a seslenmeye bakın .)

Yani "Y seçici ile eşleşen en az X girişi doldurulmalıdır" diyebilirsiniz .

Bunun gibi işaretlemeyle sonuç:

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

... bunun gibi bir kural grubudur:

// 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"]
}

Öğe # 3 .checked, başarılı doğrulama sonrasında hata mesajlarınıza bir sınıf eklediğinizi varsayar . Bunu aşağıda gösterildiği gibi yapabilirsiniz .

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

Yukarıda bağlantısı verilen demoda span.errorolduğu gibi, sınıfa sahip olmadığı sürece her X görüntüsünü arka planı olarak vermek için CSS kullanıyorum .checked, bu durumda bir onay işareti görüntüsü alır.

İşte şimdiye kadarki kodum:

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."));

Yaşasın!

Haykırmak

Şimdi bu haykırış için - başlangıçta, kodum hata mesajlarını yeniden doğrulamak yerine diğer eşleşen alanlara körü körüne gizledi, bu da başka bir sorun varsa ('sadece sayılara izin verilir ve harfleri girmişsiniz' gibi) anlamına gelir. , kullanıcı göndermeyi deneyene kadar gizlendi. Bunun nedeni, yukarıdaki yorumlarda belirtilen geri bildirim döngüsünden nasıl kaçınacağımı bilmiyordum. Bir yolu olması gerektiğini biliyordum, bu yüzden bir soru sordum ve Nick Craver beni aydınlattı. Teşekkürler Nick!

Soru Çözüldü

Bu aslında "bunu paylaşmama izin verin ve iyileştirme önerebilecek biri var mı görelim" türü bir soruydu. Geri bildirimi yine de memnuniyetle karşılıyor olsam da, bu noktada oldukça tamamlandığını düşünüyorum. (Daha kısa olabilir, ancak okumasının kolay olmasını ve özlü olmasını istemiyorum.) Bu yüzden keyfini çıkarın!

Güncelleme - artık jQuery Doğrulamanın bir parçası

Bu resmi olarak 4/3/2012 tarihinde jQuery Doğrulaması'na eklendi .


Ayrıca, yakından ilgili kurala bakın - "Bu alanları atlayın veya en az X tanesini doldurun" - stackoverflow.com/questions/1888976/…
Nathan Long

Diğer girdilerin doldurulup doldurulmadığını kontrol etmekten neden keyfi bir girdi sorumlu olsun? Bu mantıklı değil. Belki de ilgili unsurlara biraz işaretleme ekleyebilirsiniz?
montrealist

@dalbaeb - Örneğe biraz açıklık getirdim. Diğerlerini kontrol etmekten keyfi bir giriş sorumlu değildir; bir gruptaki her girdi diğerlerinin hepsini kontrol etmekten sorumludur.
Nathan Long

Ben de öyle düşünmüştüm, çok teşekkürler!
montrealist

3
Teşekkürler, bu benim için işe yarıyor, ancak artık formdaki diğer gerekli alanlar kontrolün ardından odak kazanıp kaybolmadıkça artık yanıt vermiyor. (Biri bunu diğer sorunuza cevap olarak ekledi, ancak bir cevap olmadığı için işaretlenmesi gerekiyordu).
mydoghasworms

Yanıtlar:


21

Bu mükemmel bir çözüm Nathan. Çok teşekkürler.

İşte benim yaptığım gibi, birisi onu entegre etmekte sorun yaşarsa yukarıdaki kodu çalıştırmanın bir yolu:

Ek-method.js dosyası içindeki kod :

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

Html dosyasının içindeki kod :

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

Ek metodlar.js dosyasını eklemeyi unutmayın!


Size yardımcı olmasına sevindim ve bilgi verdiğiniz için teşekkürler. Ancak, addClassRules yöntemini yapmak yerine, her bir formda bir dizi kural kullanmayı tercih ediyorum. Bu sayfaya ( jquery.bassistance.de/validate/demo/milk ) gidip "bu sayfada kullanılan komut dosyasını göster" i tıklarsanız bir örnek göreceksiniz. Bunu bir adım daha ileri götürüyorum: "kurallar" adlı bir dizi tanımlıyorum, sonra bunları ayrı ayrı var validator = $ ('# formtovalidate') ile kullanıyorum. Validate (kurallar);
Nathan Long

Başka bir düşünce: Burada gösterdiğiniz 'fillone' sınıfı sorunlu olabilir. Ya aynı formda en az bir parça numarası VE en az bir kişi adı girmeniz gerekiyorsa? Kuralınız, en az bir parça numarası olduğu sürece 0 kişi adına izin verecektir. Bence gibi kurallar koymak require_from_group: [1,".partnumber"]ve ...[1,".contactname"]doğru şeyleri onayladığınızdan emin olmak daha iyidir .
Nathan Long

6

Güzel çözüm. Ancak, diğer gerekli kuralların çalışmaması sorunuyla karşılaştım. Forma karşı .valid () çalıştırmak benim için bu sorunu çözdü.

if(!$(element).data('being_validated')) {
  var fields = $(selector, element.form);
  fields.data('being_validated', true); 
  $(element.form).valid();
  fields.data('being_validated', false);
}

1
Teşekkürler Sean, ben de bu sorunu yaşıyordum. Ancak bu çözümle ilgili bir sorun var, kullanıcı forma ilk kez geldiğinde - ilk gruptan gerekli alanı doldurur doldurmaz, diğer tüm form alanları doğrulanacak ve bu nedenle hatalı olarak işaretlenecektir. Bunu çözme şeklim, içinde bir bayrak belirlediğim doğrulayıcı örneğini oluşturmadan önce bir form.submit () işleyicisi eklemekti validator.formSubmit = true. Gruptan gerektirme yönteminde daha sonra bu bayrağı kontrol ederim; oradaysa yaparım $(element.form).valid();, yoksa yaparım fields.valid();.
Christof

Burada gerçekte ne olduğunu kimse açıklayabilir mi? Oldukça benzer bir kuralım var, işe yaradı, ancak temdit sorununu ele almadığımız (gruptaki diğer alanlar hala geçersiz olarak işaretlendi). Ama şimdi formun geçersiz olsa bile gönderildiğini deneyimliyorum. Gruplandırılmış alanlar geçerliyse, alt taşıyıcıya girmez ve geçersizse geçersizHandler'a girer ancak yine de gönderir! Bunun doğrulama eklentisinde oldukça ciddi bir hata olduğunu söyleyebilirim. Bir kuralın dönmesi, yalnızca o kural için geçerlidir (tüm alan için bile geçerli değildir), öyleyse neden geçersiz bir form gönderiliyor?
Adam

Daha fazla araştırdım ve önceden doğrulamayanlar grubun önündeki alanlar. Bunu ayrı bir soru olarak sordum (keşfettiğim kısmi bir çözümle birlikte): stackoverflow.com/questions/12690898/…
Adam

4

Teşekkürler Sean. Bu, diğer kuralları görmezden gelen kodla yaşadığım sorunu çözdü.

Ayrıca birkaç değişiklik yaptım, böylece 'Lütfen en az 1 alanı doldurun ..' mesajı her alan yerine ayrı bir bölümde gösteriliyor.

forma koymak doğrulama komut dosyası

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

bunu sayfada bir yere ekle

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

require_from_group yöntemi addMethod işlevine ekle

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

4

Mevcut sürümün yaptığı sorunlardan etkilenmeyen bir yama gönderdim ("gerekli" seçeneği diğer alanlarda düzgün çalışmayı durdurur, mevcut sürümle ilgili sorunların bir tartışması github'da .

Http://jsfiddle.net/f887W/10/ adresindeki örnek

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."));

3

PHP'de $ ile bir değişken adı başlatmak gerekir, ancak Javascript'te oldukça garip (IMHO). Ayrıca, ona iki kez "$ modül" ve bir kez "modül" dediğinize inanıyorum, değil mi? Görünüşe göre bu kod çalışmamalı.

Ayrıca, normal bir jQuery eklenti sözdizimi olup olmadığından emin değilim, ancak addMethod çağrınızın üzerine, başardığınız şeyi açıklayan yorumlar ekleyebilirim. Yukarıdaki metin açıklamanızla bile, kodu takip etmek zordur, çünkü hangi alan kümesi,: dolu, değer, öğe veya seçici ile ilgili bilgi sahibi değilim. Belki de bunların çoğu Validate eklentisine aşina olan biri için açıktır, bu nedenle doğru miktarda açıklamanın ne olduğu konusunda yargıya varın.

Belki de kodu kendi kendinize belgelemek için birkaç değişken ayırabilirsiniz; sevmek,

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

(kodunuzun bu parçalarının anlamını anladığımı varsayarsak! :))

Aslında "modül" ün ne anlama geldiğinden tam olarak emin değilim - bu değişkene verebileceğiniz daha spesifik bir isim var mı?

Genel olarak güzel kod!


Önerileriniz için teşekkürler - Değişken adlarını netleştirdim ve kodu biraz daha okunaklı hale getirdim.
Nathan Long

2

Üzerinde çalıştığım form, bunun gibi gruplanmış girdilere sahip birkaç klonlanmış bölgeye sahip olduğundan, require_from_group yapıcısına, addMethod işlevinizin tam olarak bir satırını değiştirerek fazladan bir argüman ilettim:

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

ve bu şekilde bir seçici, kimlik veya eleman adı bir kez geçilebilir:

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

ve doğrulayıcı, formdaki tüm .reqgrp sınıflı öğeleri saymaya çalışmak yerine, doğrulamayı yalnızca her alan kümesinin içindeki bu sınıfa sahip öğelerle sınırlar.


2

İşte Rocket Hazmat'ın cevabındaki çatlağım, diğer tanımlanmış alanların da doğrulanması gereken, ancak birinin başarılı bir şekilde doldurulduğunda tüm alanları geçerli olarak işaretleyen sorununu çözmeye çalışıyor.

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."));

Bununla ilgili geriye kalan tek sorun, alanın boş olduğu, daha sonra doldurulduğu ve sonra tekrar boş olduğu uç durumdur ... Bu durumda hata gruba değil tek alana uygulanacaktır. Ancak bunun herhangi bir frekansta olması pek olası görünmüyor ve bu durumda teknik olarak hala çalışıyor.


Bu yöntem / kural, Nisan 2012'de eklentiye entegre edildiğinden, bu yanıtın hiçbir anlamı yok.
Sparky

Roket Hazmat'ın şu anda doğrulayıcıyla birlikte gelen yöntemle aynı sorunu yaşıyorum. Bir grup alan olduğunu, ancak diğer yöntemleri kullanan diğer alanların doğrulanmadığını doğrular. Bu cevap, bu sorunu çözme girişimidir. Daha iyi bir çözümünüz varsa lütfen bana bildirin.
squarecandy

Geliştirici herhangi bir karışıklığa neden olmak yerine sorunu kalıcı olarak çözene
Sparky

1

Bununla bağlantılı olarak kontrol edilmeyen diğer kurallarla ilgili sorunlar yaşıyordum, bu yüzden değiştirdim:

fields.valid();

Buna:

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

Ayrıca birkaç (kişisel) iyileştirme yaptım ve bu benim kullandığım sürüm:

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."));

Diğer alanları tekrar doğrulayan işler, ancak şimdi, bir grubun tüm alanları geçersiz olarak işaretlendiğinde ve birini doldurduğunuzda, yalnızca biri doğrulanır. En azından benim için?
Christof

@Chris - buna dayanan ve bunu ele alan yeni cevabımı görün.
squarecandy

0

Teşekkürler Nathan. Beni bir sürü zaman kurtardın.

Ancak, bu kuralın jQuery.noConflict () için hazır olmadığına dikkat etmeliyim. Yani, çalışabilmek için tüm $ 'ı jQuery ile değiştirmelisiniz.var $j = jQuery.noConflict()

Ve bir sorum var: nasıl yerleşik bir kural gibi davranmasını sağlayabilirim? Örneğin, e-posta girersem, "Lütfen geçerli bir e-posta girin" mesajı otomatik olarak kaybolur ancak grup alanlarından birini doldurursam hata mesajı kalır.


Haklısın - çatışmasız durumu düşünmedim. Bunu gelecekte güncelleyebilirim, ancak isterseniz kolayca bul ve değiştir yapabilirsiniz. İkinci sorunuz için aynı sorunu görmüyorum. Hızlı bir test ile, bir gruptan bir alan gerekliyse, herhangi bir şey yazdığım anda tüm grup bu kuralı geçer. Birden fazla ihtiyaç duyulursa, en son gerekli olan doldurulur doldurulmaz ve odak kaybedilir, tüm grup geçer. Gördüğün bu mu?
Nathan Long

hmm nedense işaretleme bozuldu ve düzeltmede başarılı
olamadım

Rinat - sorunu basitleştirebilir ve daraltabilir misiniz? Kodumu çatışmasız değişikliklere ihtiyaç duymayan daha basit bir formda kullanmayı deneyin. Üzerinde test edebileceğiniz en basit formu yapın ve önce onu çalıştırın.
Nathan Long
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.