jQuery Validate - yêu cầu điền ít nhất một trường trong nhóm


98

Tôi đang sử dụng Plugin jQuery Validate tuyệt vời để xác thực một số biểu mẫu. Trên một biểu mẫu, tôi cần đảm bảo rằng người dùng điền vào ít nhất một trong một nhóm trường. Tôi nghĩ rằng tôi đã có một giải pháp khá hay và muốn chia sẻ nó. Vui lòng đề xuất bất kỳ cải tiến nào bạn có thể nghĩ đến.

Không tìm thấy cách tích hợp nào để thực hiện việc này, tôi đã tìm kiếm và tìm thấy phương pháp xác thực tùy chỉnh của Rebecca Murphey , rất hữu ích.

Tôi đã cải thiện điều này theo ba cách:

  1. Để cho phép bạn chuyển vào một bộ chọn cho nhóm trường
  2. Để cho phép bạn chỉ định số lượng nhóm đó phải được điền để xác thực vượt qua
  3. Để hiển thị tất cả đầu vào trong nhóm khi vượt qua xác thực ngay sau khi một trong số chúng vượt qua xác thực. (Xem lời cảm ơn Nick Craver ở cuối.)

Vì vậy, bạn có thể nói "ít nhất X đầu vào phù hợp với bộ chọn Y phải được điền."

Kết quả cuối cùng, với đánh dấu như thế này:

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

... là một nhóm các quy tắc như sau:

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

Mục số 3 giả định rằng bạn đang thêm một lớp .checkedthông báo lỗi của mình khi xác thực thành công. Bạn có thể làm điều này như sau, như được minh họa ở đây .

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

Như trong bản trình diễn được liên kết ở trên, tôi sử dụng CSS để cung cấp cho mỗi span.errorhình ảnh X làm nền của nó, trừ khi nó có lớp .checked, trong trường hợp đó, nó sẽ nhận được hình ảnh dấu kiểm.

Đây là mã của tôi cho đến nay:

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

Hoan hô!

La lên

Bây giờ cho lời cảm ơn đó - ban đầu, mã của tôi chỉ ẩn thông báo lỗi một cách mù quáng trên các trường khớp khác thay vì xác thực lại chúng, có nghĩa là nếu có vấn đề khác (như 'chỉ cho phép số và bạn đã nhập chữ cái') , nó bị ẩn cho đến khi người dùng cố gắng gửi. Điều này là do tôi không biết cách tránh vòng lặp phản hồi được đề cập trong các nhận xét ở trên. Tôi biết phải có một cách, vì vậy tôi đã hỏi một câu hỏi , và Nick Craver đã khai sáng cho tôi. Cảm ơn, Nick!

Câu hỏi đã được giải quyết

Đây ban đầu là một loại câu hỏi "hãy để tôi chia sẻ điều này và xem liệu ai có thể đề xuất cải tiến". Mặc dù tôi vẫn hoan nghênh phản hồi nhưng tôi nghĩ rằng nó đã khá hoàn chỉnh vào thời điểm này. (Nó có thể ngắn hơn, nhưng tôi muốn nó dễ đọc và không nhất thiết phải súc tích.) Vì vậy, hãy cứ tận hưởng!

Cập nhật - hiện là một phần của Xác thực jQuery

Điều này chính thức được thêm vào jQuery Validation vào ngày 4/3/2012.


Ngoài ra, xem quy tắc có liên quan chặt chẽ - “Hoặc là bỏ qua các trường này, hoặc điền ít nhất X của họ" - stackoverflow.com/questions/1888976/...
Nathan dài

Tại sao một đầu vào tùy ý lại chịu trách nhiệm kiểm tra xem các đầu vào khác đã được lấp đầy chưa? Điều này không có ý nghĩa. Có lẽ bạn có thể bao gồm một chút đánh dấu với các yếu tố liên quan?
montrealist vào

@dalbaeb - Tôi đã làm rõ ví dụ một chút. Nó không phải là một đầu vào tùy ý chịu trách nhiệm kiểm tra những người khác; đó là mỗi đầu vào trong một nhóm có trách nhiệm kiểm tra tất cả các đầu vào khác.
Nathan Long,

Đó là những gì tôi nghĩ, cảm ơn rất nhiều!
montrealist

3
Cảm ơn, điều này phù hợp với tôi, nhưng các trường bắt buộc khác trong biểu mẫu hiện không phản hồi nữa trừ khi chúng đạt được và mất tiêu điểm sau khi kiểm tra. (Ai đó đã thêm câu trả lời này làm câu trả lời cho câu hỏi khác của bạn, nhưng nó phải được gắn cờ vì nó không phải là câu trả lời).
mydoghasworms

Câu trả lời:


21

Đó là một giải pháp tuyệt vời Nathan. Cảm ơn rất nhiều.

Đây là một cách làm cho đoạn mã trên hoạt động, trong trường hợp ai đó gặp sự cố khi tích hợp nó, như tôi đã làm:

Mã bên trong tệp tin bổ sung-method.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"]
});

Mã bên trong tệp 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" />

Đừng quên bao gồm tệp tin bổ sung-method.js!


Rất vui vì nó hữu ích cho bạn và cảm ơn vì đã chia sẻ thông tin. Tuy nhiên, thay vì thực hiện phương thức addClassRules, tôi thích sử dụng một mảng các quy tắc trên từng biểu mẫu riêng lẻ. Nếu bạn truy cập trang này ( jquery.bassistance.de/validate/demo/milk ) và nhấp vào "hiển thị tập lệnh được sử dụng trên trang này", bạn sẽ thấy một ví dụ. Tôi thực hiện thêm một bước nữa: Tôi khai báo một mảng được gọi là "quy tắc", sau đó riêng biệt, tôi sử dụng chúng với var validator = $ ('# formtovalidate'). Validate (rules);
Nathan Long

Một suy nghĩ khác: lớp 'fillone' mà bạn hiển thị ở đây có thể có vấn đề. Điều gì xảy ra nếu trên cùng một biểu mẫu, bạn cần yêu cầu ít nhất một số bộ phận VÀ ít nhất một tên liên hệ? Quy tắc của bạn sẽ cho phép 0 tên liên hệ miễn là có ít nhất một số bộ phận. Tôi nghĩ tốt hơn là đặt ra các quy tắc như require_from_group: [1,".partnumber"]...[1,".contactname"]để đảm bảo bạn đang xác thực những điều đúng đắn.
Nathan Long

6

Giải pháp tốt. Tuy nhiên, tôi gặp vấn đề là các quy tắc bắt buộc khác không hoạt động. Thực thi .valid () đối với biểu mẫu đã khắc phục sự cố này cho tôi.

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

1
Cảm ơn Sean, tôi cũng đang gặp vấn đề này. Tuy nhiên, có một vấn đề với giải pháp này, khi người dùng truy cập biểu mẫu lần đầu tiên - ngay sau khi anh ta điền vào trường yêu cầu từ nhóm đầu tiên, tất cả các trường biểu mẫu khác sẽ được xác thực và do đó được đánh dấu là bị lỗi. Cách tôi giải quyết vấn đề này là thêm một trình xử lý form.submit () trước khi tạo một phiên bản của trình xác thực, trong đó tôi đặt cờ validator.formSubmit = true. Trong phương thức request-from-group, tôi kiểm tra cờ đó; nếu nó ở đó tôi làm $(element.form).valid();, nếu không thì tôi làm fields.valid();.
Christof

Bất cứ ai có thể giải thích những gì thực sự đang xảy ra ở đây? Tôi có một quy tắc khá tương tự, đã hoạt động nhưng trong đó chúng tôi chưa giải quyết được vấn đề xác thực lại (các trường khác trong nhóm vẫn được đánh dấu là không hợp lệ). Nhưng bây giờ tôi thấy rằng biểu mẫu gửi ngay cả khi không hợp lệ. Nếu các trường được nhóm hợp lệ thì nó sẽ không nhập trình xử lý và nếu không hợp lệ thì nhập vào trình xử lý không hợp lệ nhưng vẫn gửi! Tôi muốn nói rằng đây là một lỗi khá nghiêm trọng trong plugin xác thực? Quy tắc trả về giá trị chỉ áp dụng cho quy tắc đó (thậm chí không phải toàn bộ trường), vậy tại sao biểu mẫu không hợp lệ lại được gửi đi?
Adam

Tôi đã điều tra thêm và đó là các trường trước nhóm không xác thực proeprly. Tôi đã hỏi điều này như một câu hỏi riêng biệt (với một giải pháp giải quyết một phần mà tôi đã phát hiện ra): stackoverflow.com/questions/12690898/…
Adam

4

Cảm ơn sean. Điều đó đã khắc phục sự cố tôi gặp phải với mã bỏ qua các quy tắc khác.

Tôi cũng đã thực hiện một vài thay đổi để thông báo 'Vui lòng điền vào ít nhất 1 trường ..' hiển thị trong một div riêng biệt thay vì sau tất cả các trường.

đưa vào biểu mẫu xác thực script

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

thêm cái này vào đâu đó trong trang

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

thêm vào hàm request_from_group method addMethod

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

4

Tôi đã gửi một bản vá không bị các vấn đề mà phiên bản hiện tại mắc phải (theo đó tùy chọn "bắt buộc" ngừng hoạt động bình thường trên các trường khác, một cuộc thảo luận về các vấn đề với phiên bản hiện tại có trên github .

Ví dụ tại 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."));

3

Bắt đầu một tên biến bằng $ là bắt buộc trong PHP, nhưng khá lạ (IMHO) trong Javascript. Ngoài ra, tôi tin rằng bạn gọi nó là "$ module" hai lần và "module" một lần, phải không? Có vẻ như mã này không hoạt động.

Ngoài ra, tôi không chắc đó có phải là cú pháp plugin jQuery bình thường hay không, nhưng tôi có thể thêm nhận xét phía trên lệnh gọi addMethod của bạn, giải thích những gì bạn đạt được. Ngay cả với mô tả văn bản của bạn ở trên, thật khó để làm theo mã, bởi vì tôi không quen thuộc với bộ trường,: điền, giá trị, phần tử hoặc bộ chọn tham chiếu đến. Có lẽ hầu hết điều này là hiển nhiên đối với những người quen thuộc với plugin Validate, vì vậy hãy sử dụng phán đoán về lượng giải thích phù hợp.

Có lẽ bạn có thể phá vỡ một vài vars để tự ghi lại mã; giống,

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

(giả sử tôi đã hiểu ý nghĩa của những đoạn mã này của bạn! :))

Tôi không chắc chính xác "mô-đun" nghĩa là gì - có tên cụ thể hơn mà bạn có thể đặt cho biến này không?

Mã đẹp, nhìn chung!


Cảm ơn những gợi ý - tôi đã làm rõ tên biến và chia nhỏ mã để dễ đọc hơn một chút.
Nathan Long

2

Bởi vì biểu mẫu tôi đang làm việc có một số vùng được sao chép với các đầu vào được nhóm như thế này, tôi đã chuyển một đối số bổ sung cho hàm tạo request_from_group, thay đổi chính xác một dòng trong hàm addMethod của bạn:

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

và theo cách này, một bộ chọn, ID hoặc tên phần tử có thể được chuyển một lần:

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

và trình xác thực sẽ chỉ giới hạn xác thực cho các phần tử có lớp đó bên trong mỗi tập trường, thay vì cố gắng đếm tất cả các phần tử được phân loại .reqgrp trong biểu mẫu.


2

Đây là câu trả lời của tôi về câu trả lời của Rocket Hazmat, cố gắng giải quyết vấn đề của các trường được xác định khác cũng cần được xác thực, nhưng đánh dấu tất cả các trường là hợp lệ khi điền thành công một trường.

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

Vấn đề còn lại duy nhất với điều này bây giờ là trường hợp cạnh mà trường trống, sau đó được lấp đầy, sau đó lại trống ... trong trường hợp đó, lỗi sẽ áp dụng cho trường đơn không phải nhóm. Nhưng điều đó dường như rất khó xảy ra với bất kỳ tần suất nào và về mặt kỹ thuật nó vẫn hoạt động trong trường hợp đó.


Không có điểm nào trong câu trả lời này vì phương pháp / quy tắc này đã được tích hợp vào plugin vào tháng 4 năm 2012.
Sparky

Tôi gặp vấn đề tương tự mà Rocket Hazmat gặp phải với phương pháp hiện giao với trình xác thực. Nó xác thực rằng một nhóm trường, nhưng không có trường nào khác sử dụng các phương pháp khác được xác thực. Câu trả lời này là một nỗ lực để giải quyết vấn đề đó. Nếu bạn có một giải pháp tốt hơn, xin vui lòng cho tôi biết.
squarecandy

Cho đến khi nhà phát triển khắc phục vĩnh viễn sự cố, thay vì thêm vào bất kỳ sự nhầm lẫn nào, tôi chỉ đề xuất bất kỳ giải pháp tạm thời nào đang được xác nhận tại đây: github.com/jzaefferer/jquery-validation/issues/412
Sparky

1

Tôi gặp sự cố với các quy tắc khác không được kiểm tra cùng với quy tắc này, vì vậy tôi đã thay đổi:

fields.valid();

Về điều này:

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

Tôi cũng đã thực hiện một vài cải tiến (cá nhân) và đây là phiên bản tôi đang sử dụng:

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

Hoạt động làm cho các trường khác xác thực lại, nhưng bây giờ, khi tất cả các trường của một nhóm được đánh dấu là không hợp lệ và bạn điền vào một trường, chỉ một trường đó được xác thực. Ít nhất là đối với tôi?
Christof

@Chris - xem câu trả lời mới của tôi dựa trên câu trả lời này và giải quyết vấn đề này.
squarecandy

0

Cảm ơn, Nathan. Bạn đã tiết kiệm cho tôi rất nhiều thời gian.

Tuy nhiên, tôi phải lưu ý rằng quy tắc này chưa sẵn sàng jQuery.noConflict (). Vì vậy, người ta phải thay thế tất cả $ bằng jQuery để làm việc vớivar $j = jQuery.noConflict()

Và tôi có câu hỏi: làm thế nào tôi sẽ làm cho nó hoạt động như quy tắc tích hợp sẵn? Ví dụ: nếu tôi nhập email, thông báo "Vui lòng nhập email hợp lệ" sẽ tự động biến mất nhưng nếu tôi điền vào một trong các trường nhóm thì thông báo lỗi vẫn còn.


Bạn nói đúng - tôi đã không xem xét tình huống không liên quan. Tôi có thể cập nhật điều đó trong tương lai, nhưng bạn có thể dễ dàng thực hiện tìm và thay thế nếu muốn. Đối với câu hỏi thứ hai của bạn, tôi không thấy vấn đề tương tự. Với một bài kiểm tra nhanh, nếu một trường từ một nhóm được yêu cầu, ngay khi tôi nhập bất kỳ thứ gì, cả nhóm sẽ vượt qua quy tắc đó. Nếu yêu cầu nhiều hơn một, ngay sau khi yêu cầu cuối cùng được lấp đầy và mất tập trung, cả nhóm sẽ vượt qua. Đây có phải là thứ bạn nhìn thấy không?
Nathan Long

hmm vì một số lý do đánh dấu bị vặn và tôi không sửa được thành công
Rinat

Rinat - bạn có thể đơn giản hóa và thu hẹp vấn đề không? Hãy thử sử dụng mã của tôi trên một biểu mẫu đơn giản hơn mà không cần các thay đổi không liên quan. Thực hiện biểu mẫu đơn giản nhất mà bạn có thể kiểm tra nó và làm cho nó hoạt động trước.
Nathan Long
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.