我決定使用兩個事件偵聽器來一次驗證一個動態錶行,其中我重置了兩個事件偵聽器上的驗證;
A)上輸入[類型=數]點擊,關於日期選擇器輸入事件的內容KEYUP
&
B),和KEYUP
我使用內聯復位驗證規則即:
$(".element").rules('add',{required: true});
$(".element").rules('remove',"required");
在飛行中。
我還在創建新行後動態創建新的$("#id").datepicker()
對象。我也用$(this).clone()
在處理原始行時創建動態行。我從來沒有想過$.clone() API會有用,但我終於找到了一個有用的例子。
從我的理解克隆的對象不綁定到我認爲依賴於API的原始行的事件偵聽器。我實際上已經證明我用datepicker的嵌套輸入文本克隆了一行。
我最終選擇使用jQuery.remove()
函數刪除輸入文本框,然後重新創建它並將其綁定到datepicker API,然後爲其提供一個新ID以使其正確工作。
現在的問題是changeDate事件沒有綁定到新的datepicker對象,我記得我試圖刪除id並添加一個新的id,但元素仍然綁定到從它克隆的原始元素的事件。
例如,日曆在原始元素上彈出或日曆本身不顯示,具體取決於您如何選擇重新初始化克隆行中的嵌套日期選擇器。 (請參閱follow-up question I posted regarding the onchangeDate issue)。
也看出來哪個日期選擇器API你引用因爲有這個功能,如Bootstrap-datepicker和jQuery-datepicker這礦是從美創,距離jQuery的日期選擇器API許多API
我將展示樣品如果有人遇到與我相同的問題,也可以使用這些代碼。
注意:我沒有在所有瀏覽器中測試2017年2月23日,所以我會在測試後更新。
這裏是$.rules(), $.datepicker(), and $.clone()
API的一些示例代碼段:
這是我如何驗證和處理表中的行,而無需使用$("form").submit()
技術。
注:我的形式在我的桌子包裹所以我有一個驗證的聲明,但我不使用form.submit()
技術,而我處理的,動態驗證規則我重置和每個事件的觸發設置。
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");
}
這裏是我如何使用$ .clone()API從剛處理的行創建了一個新的空行:
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;
}
我發現,關鍵是創造動態的日期選擇器是當你克隆一個對象覆蓋舊的id,或者甚至是克隆對象的元素本身,這個例子可以在ProcessRequest函數裏看到:
NOTE: I ha d與onChangeDate事件監聽器一起玩一些遊戲,以便處理datepicker關閉的行。現在唯一的問題是onChangeDate事件偵聽器未在新克隆的對象上觸發。我試圖解除綁定事件並綁定它,但沒有運氣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();
}
}
注:我一直在嘗試使用類似的技術,我使用隨機的ID與日期選擇器用於創建dynamic bootstrap popout confirmation dialogs但我有一些錯誤的地方正在採取兩次點擊輸入。如果我修復,我會發布結果。
我有一個datepicker錯誤,其中第一個選擇不註冊可能是因爲它嵌套在一個事件內?在做了一些研究之後,我發現有一個similar issue with angular where user had to select date twice。我在OnClose中設置了一個setTimeout,它似乎可以解決問題。我將對彈出窗口使用相同的方法,看看它是如何發揮作用的。我希望我能幫助別人!
UPDATE: 感謝artemisian,我能夠與動態datepickers解決的問題!關於克隆日期選擇器請參閱this。
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));
});
}
}
}
那麼我這樣做,所以我不得不重用代碼:
/************ ************************************************** * 包裝功能允許動態綁定 **************************************** ****************************************/
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
}