jquery-validatejquery-select2

Dynamically added Select 2, Jquery Validations doesn't work


I'm developing a form for users to add multiple items to their cart and need to validate that none of the fields are left empty. I'm utilizing the Select2 plugin and Jquery Validator and jQuery for this purpose. However, despite implementing validation, the form is not preventing users from submitting it when fields are left empty.

I've assigned classes to the fields to apply the same validation to them. The validation should prevent users from submitting if any existing or newly added row lacks an item selected and an empty quantity.

<form name='CartForm' id='CartForm' method='post'>
<table class="table table-condensed table-bordered no-padding" id="myTable">    <thead>
    <tr role="row" class="bg-blue">
         <th class="tcenter" style="vertical-align:top; width:30%">Item #, UPC or Item Name</th>
         <th class="tcenter" style="vertical-align:top; width:7%">Quantity</th>
         <th class="tcenter" style="vertical-align:top; width:9%">PO Number</th>
    </tr>
</thead>
<tbody>
    <tr>
        <td class="tleft">
            <select name="view_by_item[]" data-id="1" id="view_by_item1" class="form-control view_by_item" style='width: 100%'></select>
        </td>
        <td class="tcenter">
            <input type="number" name="qty[]" id="qty1" class="qty form-control" style="text-align:right;">
        </td>
        <td class="tcenter">
            <input type="text" name="ponum[]" class="ponum form-control" placeholder="Optional">
        </td>
    </tr>
    <tr>
        <td class="tleft">
            <select name="view_by_item[]" data-id="2" id="view_by_item2" class="form-control view_by_item" style='width: 100%'></select>
        </td>
        <td class="tcenter">
            <input type="number" name="qty[]" id="qty2" class="qty form-control" style="text-align:right;">
        </td>
        <td class="tcenter">
            <input type="text" name="ponum[]" class="ponum form-control" placeholder="Optional">
        </td>
    </tr>
    </tbody>
 </table>

<button type="button" class="btn btn-secondary plusbutton"> Add Item</button>
<button class="btn btn-primary  btn-flat" id="Place_order_btn">Place Order</button>
</form>

function formatRepo (repo) {
  if (repo.loading) {
    return repo.text;
  }

  var $container = $(
    "<div class='select2-result-repository clearfix'>" +
      "<div class='select2-result-repository__meta'>" +
        "<div class='select2-result-repository__title'></div>" +
        "</div>" +
      "</div>" +
    "</div>"
  );

  $container.find(".select2-result-repository__title").text(repo.full_name);
  return $container;
}

function formatRepoSelection (repo) {
  return repo.full_name || repo.text;
}


function attachItems(item_elt){
    
    item_elt = typeof(item_elt) == 'undefined'?'.view_by_item':item_elt;
    $(document).find(item_elt).select2({
        ajax: {
            url: "https://api.github.com/search/repositories",
            dataType: 'json',
            delay: 250,
            data: function (params) {
              return {
                q: params.term, // search term
                page: params.page
              };
            },
            processResults: function (data, params) {
              params.page = params.page || 1;

              return {
                results: data.items,
                pagination: {
                  more: (params.page * 30) < data.total_count
                }
              };
            },
            cache: true
          },
          placeholder: 'Search for a repository',
          minimumInputLength: 3,
          templateResult: formatRepo,
          templateSelection: formatRepoSelection
    });
}
var rowCount = 2;
$(document).on('click', '.plusbutton',function(e){
    e.preventDefault();
    
    rowCount++;
    
    
    var newRow = '<tr>';

    newRow += '<td class="tleft"><select name="view_by_item[]"  data-id="'+rowCount+'"  id="view_by_item'+rowCount+'" class="form-control view_by_item"  style="width: 100%"></select></td>';
    newRow += '<td class="tcenter"><input  type="number" name="qty[]" id="qty'+rowCount+'" class="qty form-control"  style="text-align:right;" /></td>'; 
    newRow += '<td class="tcenter"><input type="text" name="ponum[]" class="ponum form-control"  placeholder="Optional"/></td>';    
   
    $("#myTable tbody").append(newRow);
    attachItems('#view_by_item'+rowCount);
});

attachItems();


$('#CartForm').validate({
    ignore: [],
    rules:{
        '.view_by_item':{required: true},
        '.qty': {required: true}
        },
    submitHandler: function() {
        alert('No errors');
        return false;
        }   
});

JSFiddle link


Solution

  • When using dynamically added fields with jquery validate, remember it looks for unique name. In your case view_by_item[], qty[], ponum[] are used and they are repeating everytime you add the item. This is recommended as you submit the data to server side and to capture multiple fields data square bracket is recommended for field names.

    For jquery validate to work you've to add index to the names, like view_by_item[0], view_by_item[1], view_by_item[2],... similary qty[0], qty[1], qty[2]...and so on. This way jquery validator will consider this as unique fields and applies the validate.

    You have to add below code to attach validations/rules dynamically when submit button is clicked.

    $('#CartForm').on('submit', function(e){
    $(document).find('.view_by_item').each(function(){
          $(this).rules("add", {
              required: true,
              messages: {
              required: "Item is required"
            }
          });
    });
    
    $(document).find('.qty').each(function(){
          $(this).rules("add", {
            required: true,
            messages: {
            required: "Qty is required"
          }
        });
    });
    
    return false;
    });
    

    And change the validate method like below

    $('#CartForm').validate({
        ignore: [],
        submitHandler: function() {
            alert('No errors');
            return false;
            }   
    });