背景
我有一个带有网格的asp.net webform,当用户更新该网格中的文本框时,onchange事件将启动WebMethod调用并更新已更改行的其余部分。当时没有保存任何内容 - 我们只是更新了用户界面。
要提交更改,请单击“保存”按钮。
这几乎在每种情况下都能可靠地运行。然而,有一个非常持久的感觉我应该能够解决,但现在是时候打电话给专家了。
问题情景
我正在使用jQuery来捕获回车键,不幸的是,该事件首先触发,导致页面在回调完成之前提交。该行未正确更新。保存陈旧和令人困惑的数据。
更新
我认为你不能使回车行为取决于回调,因为你可以在不改变行的情况下保存。在这种情况下,如果您没有更改行,它将永远不会保存。
现在,如果有某种方法来检查javascript的内部事务列表,或者可能创建我自己的事情,然后以某种方式管理它,那就行了。但对于一些应该很容易的东西来说,这是一个繁重的举动。因此,除非专家告诉我其他情况,否则我必须认为这是错误的。
尝试
现在我正在使用内置的jQuery事件,我已经有了这个精心设计的setTimeout,它坚持了一个事实,即尝试保存,暂停足够长时间让WebMethod至少被调用,并依赖于回调提交。但事实证明,javascript ansychrony并不像我希望的那样工作,并且onchange事件甚至不会触发,直到代码块完成。这令人惊讶。
我以为我可以使用自己的小对象以正确的顺序对这些事件进行排队,并找到一种巧妙的触发方式,等等。
这一切似乎都是错误的方向。当然这是疯狂的矫枉过正,这是一个常见的问题,我忽略了一个简单的解决方案,因为我没有在24/7的javascript中工作。
右?
代码
这就是我这一刻所做的。这显然不起作用 - 我试图利用jquery的异步性质,但所有这些显然必须在行的onchange事件事件触发之前结束:
$(document).bind("keypress", function (e) {
if (e.keyCode == 13) {
handleEnter();
return false; //apparently I should be using e.preventDefault() here.
}
});
function handleEnter() {
setTimeout(function () {
if (recalculatingRow) { //recalculatingRow is a bit managed by the onchange code.
alert('recalculating...');
return true; //recur
}
//$('input[id$="ButtonSave"]').click();
alert('no longer recalculating. click!');
return false;
}, 1000);
}
然后典型的行看起来像这样。请注意,我没有使用jquery来绑定它:
<input name="ctl00$MainContent$GridOrderItems$ctl02$TextOrderItemDose" type="text" value="200.00" maxlength="7" id="ctl00_MainContent_GridOrderItems_ctl02_TextOrderItemDose" onchange="recalculateOrderItemRow(this);" style="width:50px;" />
我可以发布recalculateOrderItemRow的代码,但它真的很长而且现在的问题是它不会触发,直到after keypress事件结束。
更新Dos 根据{{3}}(并且man是一篇很酷的文章),使用setTimeout会导致它变为异步。进一步深入研究setTimeout和jQuery之间的交互,以及普通javascript事件和jQuery事件之间的交互。
答案 0 :(得分:2)
防止 ENTER 不应该给你带来太多麻烦!确保您的代码中包含以下内容:
$(document).on('keydown', 'input', function(e) {
if(e.keyCode == 13) {
e.preventDefault();
}
});
<强>更新强>
看起来你做希望保存在 ENTER 上,但只有在change
上更新UI后才能保存。这是可能的。您可以使用上面提到的Matthew Blancarte旗帜,触发change
回调中的保存,并摆脱setTimeout
。
但我不建议这样做。 您最好完全依靠保存按钮进行保存。如果不这样做,则在保存完成之前,您的用户必须等待两个异步操作完成。因此,您必须阻止UI,或跟踪所有异步操作,根据需要中止一些操作。我认为这不值得,如果保存时间太长, ENTER 会变得不那么直观。
答案 1 :(得分:2)
下面的大量解决方法,有效地占据了我今天和昨天的一半写作,似乎解决了每一种排列。
有趣的是,如果你调用e.preventDefault(),则输入本身不会触发onchange。为什么会这样?在单击保存按钮的默认行为发生之前,实际上不会发生更改。
关于这一点很少有趣。
//Used in handleEnter and GridOrderItems.js to handle a deferred an attempt to save by hitting enter (see handleEnter).
var isSaving = false;
var saveOnID = '';
//When one of the fields that trigger WebMethods get focus, we put the value in here
//so we can determine whether the field is dirty in handleEnter.
var originalVal = 0;
//These fields trigger callbacks. On focus, we need to save their state so we can
//determine if they're dirty in handleEnter().
$('[id$=TextOrderItemDose], [id$=TextOrderItemUnits]').live("focus", function() {
originalVal = this.value;
});
$(document).bind("keypress", function (e) {
if (e.keyCode == 13) { //enter pressed.
e.preventDefault();
handleEnter();
}
});
//Problem:
//In the products grid, TextOrderItemDose and TextOrderItemUnits both have js in their onchange events
//that trigger webmethod calls and use the results to update the row. Prsssing enter is supposed to
//save the form, but if you do it right after changing one of those text fields, the row doesn't always
//get updated due to the async nature of js's events. That leads to stale data being saved.
//Solution:
//First we capture Enter and prevent its default behaviors. From there, we check to see if one of our
//special boxes has focus. If so, we do some contortions to figure out if it's dirty, and use isSaving
//and saveOnID to defer the save operation until the callback returns.
//Otherwise, we save as normal.
function handleEnter() {
var focusedElement = $("[id$=TextOrderItemDose]:focus, [id$=TextOrderItemUnits]:focus")
//did we press enter with a field that triggers a callback selected?
if (isCallbackElement(focusedElement) && isElementDirty(focusedElement)) {
//Set details so that the callback can know that we're saving.
isSaving = true;
saveOnID = focusedElement.attr('id');
//Trigger blur to cause the callback, if there was a change. Then bring the focus right back.
focusedElement.trigger("change");
focusedElement.focus();
} else {
forceSave();
}
}
function isCallbackElement(element) {
return (element.length == 1);
}
function isElementDirty(element) {
if (element.length != 1)
return false;
return (element.val() != originalVal);
}
function forceSave() {
isSaving = false;
saveOnID = '';
$('input[id$="ButtonSave"]').click();
}
在文本框的更改事件中调用它:
function recalculateOrderItemRow(textbox) {
//I'm hiding a lot of code that gathers and validates form data. There is a ton and it's not interesting.
//Call the WebMethod on the server to calculate the row. This will trigger a callback when complete.
PageMethods.RecalculateOrderItemRow($(textbox).attr('id'),
orderItemDose,
ProductItemSize,
orderItemUnits,
orderItemUnitPrice,
onRecalculateOrderItemRowComplete);
}
然后,在WebMethod回调代码的末尾,我们将更新的表单值拉出来,使用jquery.caret将插入符号放在需要的位置,然后检查是否需要强制保存:
function onRecalculateOrderItemRowComplete(result) {
var sender, row;
sender = $('input[id="' + result.Sender + '"]');
row = $(sender).closest('tr');
row.find('input[id$="TextOrderItemDose"]').val(result.Dose);
row.find('input[id$="TextOrderItemUnits"]').val(result.Units);
row.find('span[id$="SpanTotalPrice"]').html(formatCurrency(result.TotalPrice));
calculateGrandTotalPrice();
$(document.activeElement).select();
if (isSaving && saveOnID == result.Sender) {
forceSave();
}
}
result.Sender是调用控件的ID,我将其填入WebMethod调用然后返回。 saveOnID可能并不完美,实际上甚至可以更好地维护一个active / uncallback-ed WebMethod调用的计数器,以确保在保存之前所有内容都已完成。呼。
答案 2 :(得分:1)
你可以发布你的javascript吗?听起来你走在正确的轨道上。在进行AJAX调用之前,我会更改我的OnChange事件以增加变量。我将调用变量inProcess并将其初始化为零。当AJAX调用返回时,我会将inProcess更新为当前值减1。在Enter键事件上,我将检查inProcess是否等于零。如果没有,您可以警告用户或设置超时以稍微重试。
答案 3 :(得分:1)
您可以在onChange事件中取消绑定Enter键捕获,然后在回调函数结束时重新绑定它。如果您发布一些代码,我可以给出更具体的答案。
答案 4 :(得分:0)
听起来你不应该异步调用WebMethod。同步调用它,并在成功时保存您的数据。