Validate dynamic table rows on event listeners

2019-09-14 02:05发布

I have an html table that I am populating via AJAX using jQuery dataTables. I have two forms on the page the first form validates the table parameters which works fine.

The second validation wraps around the entire table and my goal is to create a custom validation using tooltips unless there is another way I can use the submit form validate approach to validate multiple input[type='number'] and a datepicker in each row:

input[type=number] events - click, keyup the input field type=number

input[NAME=BUYDATE] (.hasDatePicker) event - onfocusout

How Should I trigger submission of the rows in a form?

A: Validate one row at a time where like elements use the NAME=ELEMENT

or

B: Validate the entire form using the submit form validation method?

This table is a dynamic order items utility

Here is my dataTable with a sample row:

   <form id="ITEMS">
     <table id="table_001" class="xs-small table table-condensed" >
     <thead>
     <H5>Program: FRESH INCENTIVE</H5>
     <H5>Customer: 330-990076-033 (B/C MANISTEE CLARK)</H5>
     <p><font color="red">Delivery Days: Mon,Thu</font></p>
     <tr>
     <th></th>
     <th class="hidden">
     [
 { "size" : "lg",
     "upper_hidden" : [],
     "lower_hidden" : [1,2,3,4,5,6,7,8,9,10,11,12]
     },
     { "size" : "md",
     "upper_hidden" : [],
     "lower_hidden" : [1,2,3,4,5,6,7,8,9,10,11,12]
     },
     { "size" : "sm",
     "upper_hidden" : [3,4],
     "lower_hidden" : [1,2,5,6,7,8,9,10,11,12]
     }, 
     { "size" : "xs",
     "upper_hidden" : [3,4,5], 
     "lower_hidden" : [1,2,6,7,8,9,10,11,12]
     }
     ]
     </th>
    <th>Item</th>
    <th class='cupc'>UPC</th>
    <th>Pack</th>
    <th>Size</th>
    <th>Description</th
     <th>Mon<br>Qty</th>
     <th>Tue<br>Qty</th>
     <th>Wed<br>Qty</th>
     <th>Thu<br>Qty</th>
     <th>Fri<br>Qty</th>
      <th>Sat<br>Qty</th>
     <th>Start Date</th>
     </tr>
     </thead>
     <tbody>
   <tr role="row" class="odd"><td class="hidden-lg hidden-md"><span class="row-details row-details-close"><i class="fa fa-plus-square icon-large"></i></span></td><td class=" hidden">place holder</td><td><span class="EmptyRow itno live" title="ITEMNO:1525252" style="padding: 3px;">1525252</span></td><td><span class="UPC" title="UPC:010700807229">010700807229</span></td><td class="hidden-sm hidden-xs"><span class="pack" title="package qty:24">24</span></td><td class="hidden-sm hidden-xs"><span class="size" title="size:CT">CT</span></td><td class="hidden-xs"><span class="descrpt" title="desc:PAYDAY">PAYDAY</span></td><td><span title="Monday:"><input type="number" min="1" max="99" name="QTY" title="Qty must be between 1-99" value="" data-delday="1" data-dow="" class="qty non-day" maxlength="2"></span></td><td><span title="Tuesday:"><input type="number" min="1" max="99" name="QTY" title="Qty must be between 1-99" value="" maxlength="2" class="qty non-day" data-delday="2" data-dow=""></span></td><td><span title="Wednesday:"><input type="number" min="1" max="99" name="QTY" title="Qty must be between 1-99" value="" maxlength="2" class="qty non-day" data-delday="3" data-dow=""></span></td><td><span title="Thursday:"><input type="number" min="1" max="99" name="QTY" title="Qty must be between1-99" value="" maxlength="2" class="qty non-day" data-delday="4" data-dow=""></span></td><td><span title="Friday:"><input type="number" min="1" max="99" name="QTY" title="Qty must be between 1-99" value="" maxlength="2" class="qty delivery-day" data-delday="5" data-dow="5"></span></td><td><span title="Saturday:"><input type="number" min="1" max="99" name="QTY" title="Qty must be between1-99" value="" maxlength="2" class="qty non-day" data-delday="6" data-dow=""></span></td><td><span title="Start date for buying item"><input type="text" size="10" class="dp form-control-inline xs-small hasDatepicker" id="1" value="" name="BUYDATE" data-buydate=""><img class="ui-datepicker-trigger" src="/images/calendar.png" alt="Select a start buying date" title="Select a start buying date"></span></td></tr>
     </tbody>
     </table>
     </form>

Is it possible to use the name=value approach even though it goes against the typical DOM/WC3 guidelines where you have duplicate unique IDs/Names?

I guess it's identifying ALL duplicate unique IDs/Names if all rows highlight as red even though the target is the row in which the user has focus in.

NOTE:

Each row's last column have their own datepicker and if any of the other columns in that row have a value the date for that row must be supplied.

Subsequently, if the date is supplied and none of the row's other input fields aka [name='QTY'] in that row have a supplied value then I need an error to trigger.

Basically,

I have two types of rows identified by classes .RecordRow and .EmptyRow.

Each row's input[name='BUYDATE'] must have a valid date entered only when the user triggers one of the two event listeners.

Valid:

enter image description here

AND

enter image description here

Invalid: enter image description here

AND

enter image description here

This is what I have so far

jQuery Validate:

 $("input").on("blur keyup", function(){
        row.children("td").each(function(){ 
           $(this).children('input').each(function () {
             if($(this).attr("name") === 'BUYDATE') && $(this).valid()){
                 //validate tds
             }
           });
        });
    });

form.validate({
        focusInvalid: false,  
        onkeyup: function(element) {  rule!!
            var element_id = $(element).attr('name');
            if (this.settings.rules[element_id]) {
                if (this.settings.rules[element_id].onkeyup !== false) {
                    $.validator.defaults.onkeyup.apply(this, arguments);
                }
            }
        },  
        rules: {
            "BUYDATE": { 
                required: { depends:function(){
                            //iterate through rows here?
                            //this only validate onn submit
                            //I guess maybe on could trigger submit onkeyup
                            //or blur?
                         }
                }
            },
            "DLOCN": { 
                required:{
                 depends: function(){
                            //iterate through rows here?
                            //this only validate onn submit
                            //I guess maybe on could trigger submit onkeyup
                            //or blur?
                }
               }
            }
        },
        messages: { // custom messages 
            "EVENT": {
                required: "Select a Program.",
                HTH_SelectValue: "Select a Program."            
            },
            "LOCN": {
                HTH_SingleLOCN: "A single location must be selected when using this option to load items."
            },
            "DLOCN": {
                required: "A customer location must be supplied when using this option to load items."
            }
        },          
        showErrors: function(errorMap, errorList) {
            FormError.hide();
            // Clean up any tooltips for valid elements
            $.each(this.validElements(), function (index, element) {
                element = $(element);
                NoError_ToolTip(element);
            });
            // Create new tooltips for invalid elements
            $.each(errorList, function (index, error) {
                element = $(error.element);
                message = error.message;
                Error_ToolTip(element,message);
                FormError.show();
            });
        },                  
        invalidHandler: function (event, validator) { //display error alert on form submit     
            success.hide();
            FormError.show();
            $(document).scrollTop( $(".form-body:first-of-type").offset().top ); 
        },
         submitHandler: function (form) {
            success.show();
            FormError.hide();
           // Submit1(form,FormError,success);
        }

    });
}

Finally,

Would anyone suggest wrapping each row with a form and validate that way? Seems like if I did that it would go against DOM guidelines as I need to use Id to use jQuery Validate. I have seen the use of multiple unique Ids on a single page and it can work in certain circumstances.

1条回答
Evening l夕情丶
2楼-- · 2019-09-14 02:17

I decided to validate dynamic table rows one at a time using two event listeners where I reset the validation on two event listeners;

A) on input[type=number] click, keyup

&

B) on the datepicker input focusout, and keyup

I reset the validators using the inline rules ie:

$(".element").rules('add',{required: true});

$(".element").rules('remove',"required"); on the fly.

I also create new $("#id").datepicker() objects dynamically after the new rows are created.

I also used $(this).clone() to create dynamic row(s) upon processing the original row. I never thought the $.clone() API would be useful but I finally found an instance where it becomes useful.

From what I understand the cloned objects are not bound to the original row's event listeners which I think depends on the API. I actually have proof where I clone a row with a nested input text of a datepicker.

I ultimately chose to remove the input textbox using the jQuery.remove() function then re-create it and bind it to the datepicker API and then gave it a new id in order to make it work correctly.

The problem now is the changeDate event is not binding to the new datepicker object and I recall that I attempted to remove the id and add a new id but the element was still bound to the original element's events that it was cloned from.

For example, the calendar pops up on the original element or the calendar itself does not display depending on how you choose to re-initialize the nested datepicker from the cloned row. (Please see the follow-up question I posted regarding the onchangeDate issue).

Also look out for which datepicker API you are referencing because there are many APIs for this functionality such as Bootstrap-datepicker and jQuery-datepicker which mine was from Metronic and is from the jQuery-datepicker API

I will show sample codes of this too in case someone comes across the same problem as me.

NOTE: I haven't tested in all browser a of 2/23/2017 so I will update upon testing.

Here is some sample code snippets for $.rules(), $.datepicker(), and $.clone() APIs:

Here is how I validate and process table rows WITHOUT using the $("form").submit() technique.

NOTE: My form wraps around my table so I do have a validate declaration, but I do NOT use the form.submit() technique, rather I process based on the dynamic validation rules I reset and set on each event trigger.

function TableEventHandlers(){
    var form = $("#ITEMS");
    var FormError = $('.item-failure',form);
    var FormSuccess = $('.item-success',form);
    //used to trigger datepicker on calendar selection
    $(".hasDatepicker").on("changeDate",function(e){
       e.stopImmediatePropagation();
      $(this).trigger("focusout");
      //alert("onchange date" + $(this).val() + e.type);
    });

    //Processes a single qty at a time w/o popout
    $("#table_001").on("click keyup",".qty",function (e) {
        e.stopImmediatePropagation();
        //reset validators
        $(".qty").rules('remove','min');
        $(".qty").rules('remove','max');    
        $(".qty").rules('remove','required');
        $(".dp").rules('remove','required');
        $(".dp").rules('remove','UsaDate');
        $(".dp").removeClass("error").tooltip("disable").tooltip("hide");
        $(".qty").removeClass("error").tooltip("disable").tooltip("hide");
        var row = $(this).closest('tr');
        flag = true;
        row.find('.dp').rules('add',{required:true,messages:{required:"Must supply a start buy date."}});
        row.find('.dp').rules('add',{UsaDate:true,messages:{UsaDate:"Enter date in mm/dd/yyyy format"}});
        hasQtys = false;
        hadOtherQtys = false;
        var actualQty = parseInt($(this).val(), 10);
        var dow = $(this).data("dow");
        var qty = $();
        var num = 0;              
        var buydate = row.find(".dp");
        var delday = "";
        var quans = 0;  
        var Error = false;      
        //iterate thru tr check if multiple records 
        row.children("td").each(function(index){
            qty = $(this).find(".qty");
            if(qty.val() !== undefined){
                num = parseInt(qty.val(), 10);
                //console.log(isNaN(num)+ "index=" + index);
                if(isNaN(num))
                    num = 0;
                if(num > 0){
                    hasQtys = true;
                    if(quans > 1) 
                        hadOtherQtys = true;
                    quans++;
                }
                //Min max validation process  
                console.log(num + ">"+  +parseInt(qty.attr("max"),10) + "<"+ parseInt(qty.attr("min"),10) + "not 0 = "+ num);
                //qty out of range: min or Max attr or != 0?
                if(num != 0 && (num > parseInt(qty.attr("max"),10) || num < parseInt(qty.attr("min"),10)))
                {   
                     if(num > parseInt(qty.attr("max"),10)){        
                        qty.rules('add',{max: parseInt(qty.attr('max')), messages: {max: "Quantity must not be greater than " + qty.attr("max")}});
                     }
                     else{
                        qty.rules('add',{min: parseInt(qty.attr('min')), messages: {max: "Quantity must be greater than " + qty.attr("min")}});
                     }
                         qty.addClass("error").tooltip("enable").tooltip('show');
                        $('.item-failure').removeClass("hidden").show().html(qty.data("originalTitle"));
                      Error = true;
                }
                else
                    qty.removeClass("error").tooltip("disable").tooltip("hide");
            }//eof undefined qty
        });
        console.log("has qtys= " + hasQtys + "has hadotherQtys = " + hadOtherQtys);
        //.EmptyRow all require qtys when empty row
        if(row.find(".itno").hasClass("EmptyRow") && hasQtys === false)
        {
            row.find(".qty").rules('add',{required: true, messages: {required: "Quantity must be entered when the item request date is new"}});
            row.find(".qty").addClass("error").tooltip("enable");
            Error = true;
        }
        else
        {   //.EmptyRow has Qtys or .RecordRow
            row.find(".qty").rules('remove','required');
            //row.find(".qty").removeClass("error").tooltip("disable");
        }
        console.log("buydate valid = "  + buydate.valid() + "buydate.val = " +  buydate.val());
        //new date is valid
        if(buydate.valid() == false || buydate.val() == ""){ 
            $('.item-failure').removeClass("hidden").show().html("You have some errors. See below.");
             row.find(".dp").addClass("error").tooltip("enable");    
             Error = true;
        }
        if(Error === true)
            return true;
        else{
            //Qtys met requirements of >= max && <= min or 0    
            buydate.removeClass("error").tooltip("disable");
            delday = qty.data("delday");
            $('.item-failure').addClass("hidden").hide();
            $('.item-success').removeClass("hidden").html("Processing future order request...").show();
            ProcessRequest(row,actualQty,delday, buydate.val());
        }//eof valid date
    });//eof qty event listener

     //Iterates thru the entire row when date changed 
     //NOTE: no popout atm causes erroneous results
    $(".dp").on("keyup focusout",function (e) {
        e.stopImmediatePropagation();
        //reset validators
        $(".qty").rules('remove','min');
        $(".qty").rules('remove','max');    
        $(".qty").rules('remove','required');
        hasQtys = false;
        hadOtherQtys = false;
        var row = $(this).closest('tr');
        $(this).rules('add',{required:true,messages:{required:"Must supply a start buy date."}});
        $(this).rules('add',{UsaDate:true,messages:{UsaDate:"Enter date in mm/dd/yyyy format"}});
        var buydate = $(this);
        var num = 0;
        var dow = '';
        var delday = '';
        var quans = 0;
            flag = true;
        var qty = $();
        var Error = false;
        //console.log("dp triggered" + e.type);
        //only check date when manually entered. 
        if(e.type === "keyup" && ($(this).valid() === false || $(this).val() ===""))
        {   console.log(e.type + $(this).valid());
            $(this).addClass("error").tooltip("enable").show(); 
            $('item-failure').removeClass("hidden").html("You have some errors. See below.").show();
            Error = true;
        } 
        //check for qtys in row before processing    
        row.children("td").each(function(index){
            qty = $(this).find(".qty");
            if(qty.val() !== undefined){
                num = parseInt(qty.val(), 10);
                if(isNaN(num))
                    num = 0;
                if(num > 0){
                    hasQtys = true;
                    if(quans > 1) 
                        hadOtherQtys = true;
                    quans++;
                }
                //Min max or 0 validation process  
                console.log(num + ">"+  +parseInt(qty.attr("max"),10) + "<"+ parseInt(qty.attr("min"),10) + "not 0 = "+ num);
                 console.log(num > parseInt(qty.attr('max'),10));;
                //qty out of range: min or Max attr or != 0?
                if(num != 0 && (num > parseInt(qty.attr("max"),10) || num < parseInt(qty.attr("min"),10)))
                {   
                     if(num > parseInt(qty.attr("max"),10)){        
                        qty.rules('add',{max: parseInt(qty.attr('max')), messages: {max: "Quantity must not be greater than " + qty.attr("max")}});
                     }
                     else{
                        qty.rules('add',{min: parseInt(qty.attr('min')), messages: {max: "Quantity must be greater than " + qty.attr("min")}});
                     }
                         qty.addClass("error").tooltip("enable").tooltip('show');
                        $('.item-failure').removeClass("hidden").show().html(qty.data("originalTitle"));
                   Error = true;
                }
             }//eof undefined qty
        });
            //Empty rows require atleast one qty
            if(row.find(".itno").hasClass("EmptyRow") && hasQtys === false){
                row.find(".qty").rules('add',{required: true, messages: {required: "Quantity must be entered when the item request date is new"}});
                row.find(".qty").addClass("error").tooltip("enable");
                Error = true;
            }
            if(Error === true)
                return true;
            else{
            //Final stage of processing multiple records  
            row.children("td").each(function(){
            qty = $(this).find(".qty");      
            if(qty.val() !== undefined){
                num = parseInt(qty.val(), 10);
                if(isNaN(num))
                    num = 0;
                //console.log("buydate.valid() = " +buydate.valid());
                //console.log(qty.val() + "<="+ qty.attr("max") +qty.val() + ">="+ qty.attr("min"));
                if(buydate.valid() == "1" && buydate.val() !== "" && ((row.find(".itno").hasClass("EmptyRow") && hasQtys === true) || row.find(".itno").hasClass("RecordRow"))){
                    $('.item-failure').addClass("hidden").hide();
                    $('.item-success').removeClass("hidden").html("Processing future order requests...").show();
                    console.log("processing..");
                    ProcessRequest(row,num,delday);
                }

            }//eof qty.val undefined
          });//eof td children
         }//eof error
     });//eof event datepicker listener

    form.validate({
        focusInvalid: false, // do not focus the last invalid input
        onkeyup: function(element) { //only allow if 'onkeyup:false' is rule!!
            var element_id = $(element).attr('NAME');
            if (this.settings.rules[element_id]) {
                if (this.settings.rules[element_id].onkeyup !== false) {
                    $.validator.defaults.onkeyup.apply(this, arguments);
                }
            }
        },  
        rules: {
          //dynamic rules worked better in this instance
        },
        messages: { 
          // same with custom messages 
        },          
        showErrors: function(errorMap, errorList) { 
            // Clean up any tooltips for valid elements
            $.each(this.validElements(), function (index, element) {
                element = $(element);
                NoError_ToolTip(element);
            });
            // Create new tooltips for invalid elements
            $.each(errorList, function (index, error) {
                element = $(error.element);
                message = error.message;
                Error_ToolTip(element,message);
                FormError.html("You have some errors. Please check below.").show();
            });
        },                  
        invalidHandler: function (event, validator) { //display error alert on form submit     
            FormError.html("You have some errors. Please check below.").show();
        },
         submitHandler: function (form) { 
            FormSuccess.html("Processing request...").show();
        }

    });
    $.validator.addMethod("UsaDate", function(value, element) {
            return this.optional(element) || /^\b\d{1,2}[\/]\d{1,2}[\/]\d{4}\b/.test(value);
    }, "Please enter mm/dd/yyyy date format");
    }

Here is how I created a new empty row using the $.clone() API from a row that was just processed:

var ProcessRequest = function(tr, count, dow){
        var success = $('#table_001_processing').css("color", "green").removeClass("hidden").show().html("Processing request...");
        //post vars
        var recureItemNo = tr.find(".itno").html();
        var itemCount = count;
        var dp = tr.find("[name='BUYDATE']");
        var newDate = dp.val();
        tempDate = dp.data("date");
        //clear all errors
        tr.children("td").each(function(){
           $(".qty").each(function(){
               $(this).removeClass("error").tooltip("disable").tooltip("hide"); 
           });
        });
        //insert new row because we create new row off emptyRow
        if(tr.find(".itno").hasClass("EmptyRow") && flag == true){
             console.log("this was an .EmptyRow");
            var randId = (Math.floor(Math.random() * 100 * 100)+1);
            var $clone = tr.clone();
            $clone.insertBefore(tr);
            //clear inputs 
            $clone.children("td").each(function(){
                var $input = $(this).find("input");
                $input.val("");  
            });  
            //destroy datepicker    
            var dp = $clone.find(".dp");
                dp.remove();
            //create new DatePicker(dp);
            var dpId = $('#table_001 tr').length + 1;
            $clone.find("td:eq(13) span").html("<input name='BUYDATE' class='dp form-control-inline' style='width:95px'; />");   
            var newDp = $clone.find(".dp").attr("id",dpId);
            DatePicker(newDp);

        }else{//update took place which means future distribution no matter what

        }
        //getJSON web0572 complete stuff
        //add-ons become future distributions by defaults
        if(tr.find(".itno").hasClass("EmptyRow"))
           tr.find(".itno").removeClass("EmptyRow").addClass("RecordRow").removeClass("live").addClass("future"); 
        if(tempDate > newDate) 
           tr.find(".itno").removeClass("live").addClass("future");
        if(tr.find(".itno").hasClass("future"))
           tr.addClass("pending"); 
         //Call web057s2 add all of this below: 
         console.log("hasqtys ="+hasQtys+"itemno");
          //Display successful processing        
          if(hasQtys == true){      
            console.log("processed request add or update");
            if(hadOtherQtys == true && tr.find(".itno").hasClass("RecordRow"))
                success.html("Item #" + recureItemNo + " successfully updated!");           
            else
                success.html("Item #" + recureItemNo + " successfully added!");
          }else{ 
            console.log("processed request deleted row");
            tr.remove(); 
            success.html("Item #" + recureItemNo + " for " + tempDate + " successfully removed!");
          }

          setTimeout(function(){
          success.addClass("hidden").hide().css("color","green").html("Processing...");
          }, 7000);
          flag = false;
}

I found that the key to creating dynamic datepicker is when you clone an object override the old id or even the element itself of the cloned object which an example can be viewed inside the ProcessRequest function:

NOTE: I had to play some games with the onChangeDate event listener in order to process rows on datepicker close. The only problem now is that the onChangeDate event listener is not being fired on the new cloned object. I tried to unbind the event and bind it but no luck atm.

var DatePicker = function(that){
    if (jQuery().datepicker) {
    //destroy old datepicker from clone
    if(that.hasClass('hasDatepicker')){
      that.datepicker('remove');   
     }
      that.datepicker({
      showOn: "button",
      buttonImage: "/images/calendar.png",
      buttonImageOnly: true,
      buttonText: 'Select a start buying date',
      changeMonth: true,
      changeYear: true, 
      beforeShow: function() {
          setTimeout(function(){
              $('.ui-datepicker').css('z-index', 100100);
          }, 0);
      },
      onSelect: function () {
         $(this).removeClass("error").tooltip("disable").tooltip("hide");
         $('.ui-datepicker').css('z-index', -1);
         setTimeout(function(){  
          //allows date to catchup  
         },0);
     },
     onClose: function(){
        $(this).trigger("changeDate");
     },
      minDate: '+1', // The min date that can be selected, i.e. 30 days from the 'now'
      maxDate: '+1y'  // The max date that can be selected, i.e. + 1 month, 1 week, and 1 days from 'now'
      //                       HR   MIN  SEC  MILLI 
      //new Date().getTime() + 24 * 60 * 60 * 1000)
    }).datepicker();

  }
}

NOTE: I have been trying to create dynamic bootstrap popout confirmation dialogs using the similar technique that I used with the datepicker using random Ids but I am having some errors where it is taking it twice to click input. If I fix I will post results.

I had an error with the datepicker where the first selection was not registering probably because it was nested inside an event? After doing some research I found that there was a similar issue with angular where user had to select date twice. I set a setTimeout inside the OnClose and it seemed to correct problem. I am going to use the same approach with the popouts and see how its goes. I hope I could help someone!


UPDATE: Thanks to artemisian I was able to solve the issue with the dynamic datepickers! Please see this post in regards to Cloning datepickers.

    var DatePicker = function(that){
    if (jQuery().datepicker) {
    //alert(that.attr('id'));
      that.datepicker({
      showOn: "button",
      buttonImage: "/images/calendar.png",
      buttonImageOnly: true,
      buttonText: 'Select a start buying date',
      changeMonth: true,
      changeYear: true, 
      beforeShow: function() {
          setTimeout(function(){
              $('.ui-datepicker').css('z-index', 100100);
          }, 0);
      },
      onSelect: function () {
        $('.item-failure').addClass("hidden").hide();
         $(this).removeClass("error").tooltip("disable").tooltip("hide");
         $('.ui-datepicker').css('z-index', -1);
         setTimeout(function(){  
          //allows date to catchup  
         },0);
     },
     onClose: function(){
        $(this).trigger("changeDate");
     },
      minDate: '+1', // The min date that can be selected, i.e. 30 days from the 'now'
      maxDate: '+1y'  // The max date that can be selected, i.e. + 1 month, 1 week, and 1 days from 'now'
      //                       HR   MIN  SEC  MILLI 
      //new Date().getTime() + 24 * 60 * 60 * 1000)
    }).datepicker();

    if($(this).hasClass("newDp")){
        $(this).bind("changeDate", function(e) { // this is the missing part in my opinion
            e.stopImmediatePropagation();
            $(this).trigger("focusout");
            alert("onchange date" + $(this).val());
        }); 
    //Dynamic binding on cloned datepickers only
    $(this).on("focusout",function(){
     RowValidation($(this));
    });
    } 
  }
}

I then did this so I had reusable code:

/******************************************************************************* Wrapped in function to allow dynamic binding ********************************************************************************/

var RowValidation = function(that){
        //reset validators
        $(".qty").rules('remove','min');
        $(".qty").rules('remove','max');    
        $(".qty").rules('remove','required');
        hasQtys = false;
        hadOtherQtys = false;
        var row = that.closest('tr');
        that.rules('add',{required:true,messages:{required:"Must supply a start buy date."}});
        that.rules('add',{UsaDate:true,messages:{UsaDate:"Enter date in mm/dd/yyyy format"}});
        var buydate = that;
        var num = 0;
        var dow = '';
        var delday = '';
        var quans = 0;
            flag = true;
        var qty = $();
        var Error = false;
        //console.log("dp triggered" + e.type);
        //only check date when manually entered. 
        if(e.type === "keyup" && (that.valid() === false || that.val() ===""))
        {   console.log(e.type + $(that.valid());
            that.addClass("error").tooltip("enable").show();    
            $('item-failure').removeClass("hidden").html("You have some errors. See below.").show();
            Error = true;
        } 
        //check for qtys in row before processing    
        row.children("td").each(function(index){
            qty = $(this).find(".qty");
            if(qty.val() !== undefined){
                num = parseInt(qty.val(), 10);
                if(isNaN(num))
                    num = 0;
                if(num > 0){
                    hasQtys = true;
                    if(quans > 1) 
                        hadOtherQtys = true;
                    quans++;
                }
                //Min max or 0 validation process  
                console.log(num + ">"+  +parseInt(qty.attr("max"),10) + "<"+ parseInt(qty.attr("min"),10) + "not 0 = "+ num);
                 console.log(num > parseInt(qty.attr('max'),10));;
                //qty out of range: min or Max attr or != 0?
                if(num != 0 && (num > parseInt(qty.attr("max"),10) || num < parseInt(qty.attr("min"),10)))
                {   
                     if(num > parseInt(qty.attr("max"),10)){        
                        qty.rules('add',{max: parseInt(qty.attr('max')), messages: {max: "Quantity must not be greater than " + qty.attr("max")}});
                     }
                     else{
                        qty.rules('add',{min: parseInt(qty.attr('min')), messages: {max: "Quantity must be greater than " + qty.attr("min")}});
                     }
                         qty.addClass("error").tooltip("enable").tooltip('show');
                        $('.item-failure').removeClass("hidden").show().html(qty.data("originalTitle"));
                   Error = true;
                }
             }//eof undefined qty
        });
            //Empty rows require atleast one qty
            if(row.find(".itno").hasClass("EmptyRow") && hasQtys === false){
                row.find(".qty").rules('add',{required: true, messages: {required: "Quantity must be entered when the item request date is new"}});
                row.find(".qty").addClass("error").tooltip("enable");
                Error = true;
            }
            if(Error === true)
                return true;
            else{
            //Final stage of processing multiple records  
            row.children("td").each(function(){
            qty = $(this).find(".qty");      
            if(qty.val() !== undefined){
                num = parseInt(qty.val(), 10);
                if(isNaN(num))
                    num = 0;
                //console.log("buydate.valid() = " +buydate.valid());
                //console.log(qty.val() + "<="+ qty.attr("max") +qty.val() + ">="+ qty.attr("min"));
                if(buydate.valid() == "1" && buydate.val() !== "" && ((row.find(".itno").hasClass("EmptyRow") && hasQtys === true) || row.find(".itno").hasClass("RecordRow"))){
                    $('.item-failure').addClass("hidden").hide();
                    $('.item-success').removeClass("hidden").html("Processing future order requests...").show();
                    console.log("processing..");
                    ProcessRequest(row,num,delday);
                }

            }//eof qty.val undefined
          });//eof td children
         }//eof error

} 
查看更多
登录 后发表回答