使用 ASP.NET AJAX 和 Web 服务实现闪电般的行内编辑(架构与自定义)- 第二部分






4.94/5 (13投票s)
用于网格行内编辑的 JavaScript+AJAX 解决方案。

在文章的第一部分中,我展示了如何使用 IEC(行内编辑控制器)在网格中启用行内编辑。我们在一个实际项目中使用了这种行内编辑 - TargetProcess - 基于 ASP.NET 的敏捷项目管理软件。本部分将介绍架构和高级功能,如验证和扩展。
基本类
行内编辑解决方案包含三个类
InlineEditController
- 处理行内编辑的控制器类EditAreaSettings
- 存储关于 IEC 应处理内容的类的类TransactObject
- 管理保存过程的辅助对象(它充当同步器,并在服务器端错误时恢复旧值,并确认保存正确)
var editAreasSettings = new Array();
var shipNameEditAreaSettings =
new EditAreaSettings("ShipName","ShipNameIdPrefix",null, true);
editAreasSettings.push(shipNameEditAreaSettings);
var shipAddressAreaSettings = new EditAreaSettings("ShipAddress","ShipAddressIdPrefix");
editAreasSettings.push(shipAddressAreaSettings);
var shipRegionAreaSettings = new EditAreaSettings("ShipRegion","ShipRegionIdPrefix");
editAreasSettings.push(shipRegionAreaSettings);
var inlineEditController =
new InlineEditController('gridID', editAreasSettings, onSaveEventHandler, true);
function onSaveEventHandler(retObj)
{
var transactionObj = new TransactionObject(retObj.itemID, inlineEditController);
transactionObj.onStatusSuccess = onRequestComplete;
transactionObj.onStatusError = onRequestError;
retObj.OrderID = retObj.itemID;
TargetProcess.DaoTraining.BusinessLogicLayer.OrderService.UpdateOrder(retObj,
transactionObj.onSaveSuccess ,
transactionObj.onSaveError)
}
让我们看看所有的类。
InlineEditController
处理行内编辑的控制器类。
var inlineEditControllerObj = new InlineEditController
(gridID, editAreasSettings , onSaveEventHandler, isExplicitSave)
参数
-
gridID
网格的 ID -
editAreasSettings
EditAreaSettings
对象的数组 -
isExplicitSave
一个标志,指定当另一个行进入编辑状态时,处于编辑状态的行是否应被保存。例如,当用户单击另一行时,如果此标志设置为true
,则第一行将被保存。 -
OnSaveEventHandler
保存事件调用时的回调函数 -
IEC 不知道如何保存项目,因此应提供保存过程(例如,它可以是 Web 服务调用)。
function onSaveEventHandler(retObj){ retObj.OrderID = retObj.itemID; MyWebService.OrderService.UpdateItem(retOb); }
-
retObj
包含已编辑值的对象
公共方法
-
inlineEditControllerObj.commitChanges(itemID)
应调用此方法以确认数据已成功保存,无需恢复初始值。 -
inlineEditControllerObj.abandonChanges(itemID)
在服务器端出错时,应调用此方法以恢复初始值。
EditAreaSettings
存储关于 IEC 应处理什么的类的类。IEC 需要有关应编辑什么以及如何编辑的信息。EditAreaSettings
对象包含编辑项目所需的所有基本信息。
var editAreaSettingsObj = new EditAreaSettings(areaName,
areaPrefix,
areaDataSourceControlID,
isFocus,
onSelectValue,
onExtractEditedValue,
onRenderEditedValue,
onCancelEditValue,
onEditValue)
参数
-
areaName
将编辑后的值映射到retObj
(您还记得retObj
已传递到OnSaveEventHandler
并存储了所有需要保存的新值)。 -
areaPrefix
我们使用此参数查找“编辑区域”。“编辑区域”的完整 ID 是areaPrefix
+“项目 ID”。<asp:TemplateField HeaderText="Ship Name"> <ItemTemplate> <span id="ShipNameIdPrefix<%# Eval("OrderID")%>"> <asp:Label ID="Label1" runat="server" Text='<%#Eval("ShipName")%>'></asp:Label> </span> </ItemTemplate> </asp:TemplateField> var shipNameEditAreaSettings = new EditAreaSettings("ShipName","ShipNameIdPrefix",null, true); editAreasSettings.push(shipNameEditAreaSettings); var inlineEditController = new InlineEditController('uxGridID', editAreasSettings, onSaveEventHandler, true); ...
-
areaDataSourceControlID
数据源控件的 ID当行为行启用编辑状态时,所有标签都将转换为可编辑状态。这意味着文本将被输入字段替换。另一种情况是,文本值应被预定义值的下拉框替换。IEC 将尝试根据列中的文本设置下拉框中的值。
DataSourceControl
是一个包含所有必需值的下拉框。 -
isFocus
当编辑状态激活时,“编辑区域”应获得焦点吗? -
onSelectValue
在大多数情况下,您将使用此处理程序在DataSource
控件中设置正确的值。 -
onExtractEditedValue
可以使用自定义处理程序从“编辑区域”提取已编辑的值。 -
onCancelEditValue
可以使用自定义处理程序将编辑区域设置为原始(只读)状态。 -
onEditValue
可以使用自定义处理程序将编辑区域设置为炫酷(行内编辑)状态。 -
OnRenderEditedValue
可以使用自定义处理程序渲染具有特殊性的原始(只读)值,例如显示代表优先级或其他内容的图像。
所有这些处理程序都可以省略。您应该在复杂情况下使用它们。
TransactObject
管理保存过程的辅助对象(它充当同步器,并在服务器端错误时恢复旧值,并确认保存正确)。
var transactionObject = new TransactionObject(itemID, inlineEditController);
参数
itemID
将要更新的项目 ID。inlineEditController
IEC 的引用。
IEC 生命周期
我应该说,IEC 的生命周期并不简单。很难用简短的语言描述它是如何工作的,但让我们试试。
第一阶段。IEC 初始化
IEC 通过整个网格将事件处理程序绑定到相应元素。我们只有三个处理程序:编辑、保存和取消。因此,将它们绑定到必需的控件。
function InlineEditController
(gridID, editAreasSettings, onSaveEventHandler, isExplicitSave)
{
...
//******************** Fields ****************************
var activeInlineEditPanel;
var thisRefInlineEditController = this;
//the object that stores the previous values of item,
//used in case of calling abandonChanges(itemId)
var previousStates = new Object();
attachEvents();
function attachEvents()
{
var grid = document.getElementById(gridID);
if(grid == null)
{
alert("error: Unable to find a grid.");
return;
}
//assign the click handler to the grid(HTMLTable)
grid.onclick = onGridClick;
for(var index=0; index < grid.rows.length;index++)
{
var cells = grid.rows[index].cells;
for(var index1=0; index1 < cells.length; index1++)
{
var element = findInlineEditPanelInElement(cells[index1]);
if(element == null)
continue;
if(element.attributes == null)
continue;
//look up for "Inline Edit Panel"
if(element.attributes[INLINE_PANEL_FLAG_ATTRIBUTE] != null)
{
var row = findRowForElement(element);
row.ondblclick = onDblClickRow;
row.onkeypress = onEditing;
var children = extractDHTMLElements(element);
for(var index2 = 0; index2 < children.length; index2++)
{
//look up for save, edit and cancel image buttons
var control = children[index2];
switch(control.attributes[INLINE_ACTION_ATTRIBUTE].nodeValue)
{
case INLINE_EDIT_ACTION:
control.onclick = onEditAction;
break;
case INLINE_SAVE_ACTION:
control.onclick = onSaveAction;
...
第二阶段。启用编辑模式
当用户单击“编辑”链接时会发生什么?系统会不会像往常一样崩溃?这次不会!它将表格行设置为可编辑状态。最有趣的方法是 editRow
(嗯,看起来它应该稍微重构一下,好的,我将为您留出一些改进的空间:)。那么它做了什么?
- 隐藏一些操作并显示行内编辑操作。
- 用漂亮的绿色突出显示可编辑行(嗯,如果您喜欢粉红色,也可以自定义)。
- 存储当前值(如果用户按下“取消”或出现服务器问题,我们以后可能需要它们)。
- 调用特定的
onEditValue
处理程序(如果存在)。 - 创建输入元素以允许用户实际编辑内容。
//********************* Events ************************
......
//fires on edit image click
function onEditAction()
{ //suppose that some row can be in Edit State
cancelEditRow();
editRow(this.parentNode);
}
//fires on double click on row
function onDblClickRow(eventObj)
{
if(eventObj == null)
eventObj = event;
var srcElement = eventObj.srcElement ? eventObj.srcElement : eventObj.target;
if(srcElement.tagName == "A" )
return;
//look up for "inline edit panel"
var inlineEditPanel = findInlineEditPanelInElement(this);
if(inlineEditPanel)
{
cancelEditRow();
editRow(inlineEditPanel);
}
}
function editRow(inlineEditPanel)
{
if(activeInlineEditPanel != null)
{
alert("error: Unable to set edit state.
There must be no item in edit state.");
return;
}
activeInlineEditPanel = inlineEditPanel;
var children = extractDHTMLElements(activeInlineEditPanel);
for(var index=0; index < children.length; index++)
{
var control = children[index];
switch(control.attributes[INLINE_ACTION_ATTRIBUTE].nodeValue)
{
case INLINE_EDIT_ACTION: control.style.display='none'; break;
case INLINE_SAVE_ACTION: control.style.display='';break;
case INLINE_CANCEL_ACTION: control.style.display='';break;
default:/* "Do nothing" */;
}
}
highLightInlineEditRow(INLINE_ROW_BACKGROUNDCOLOR_ACTIVE);
var itemID = getActiveInlineEditPanelID();
if(itemID == null)
{
alert("error: Unable to find item id.");
return;
}
// create a new object to store previuos values (just in the case)
previousStates[itemID] = new Object();
for(var index=0; index < editAreasSettings.length; index++)
{
var editAreaSettings = editAreasSettings[index];
//get the Edit Area ID
var editAreaID = editAreaSettings.areaPrefix+itemID
var editArea = document.getElementById(editAreaID);
if(editArea == null)
continue;
//saving particular value
previousStates[itemID][editAreaID] = editArea.parentNode.innerHTML;
//check if there is specific handler on edit event.
if(editAreaSettings.onEditValue != null)
{
editAreaSettings.onEditValue(editAreaSettings, editArea);
}
//otherwise call predefined handler of IEC
else
{
if(editAreaSettings.areaDataSourceControlID == null)
editTextField(editArea);
else
editDataSourceField(editArea, editAreaSettings.areaDataSourceControlID);
}
//check if there is specific handler on select event.
if(editAreaSettings.onSelectValue != null)
editAreaSettings.onSelectValue(editAreaSettings, editArea);
else
//otherwise call predefined handler of IEC
selectValueInDataSourceControl(editArea);
if(editAreaSettings.isFocus)
setFocusForEditableField(editArea);
}
}
在此阶段有两种可能的自定义选项
第三阶段。保存更改
首先,我们应该从表单字段中提取新值。名为 extractEditedValues
和 extractEditedValue
的方法负责所有工作。结果是我们得到了一个包含所有新值的对象。然后,我们调用 onSaveEventHandler
将新值保存到数据库。
function onSaveAction()
{
var obj = extractEditedValues();
//call the saving handler(the procedure that is defined outside of IEC)
if(onSaveEventHandler != null)
onSaveEventHandler(obj);
//get row back into read state
cancelEditRow();
}
function extractEditedValues()
{
if(activeInlineEditPanel == null)
return null;
var retObj = new Object();
var itemID = getActiveInlineEditPanelID();
if(itemID == null)
{
alert("error: Unable to find item id.");
return;
}
retObj.itemID = itemID;
for(var index=0; index < editAreasSettings.length; index++)
{
var editAreaSettings = editAreasSettings[index];
var editArea = document.getElementById(editAreaSettings.areaPrefix+itemID);
if(editArea == null)
continue;
//check if there is specific handler on extract data event.
if(editAreaSettings.onExtractEditedValue != null)
editAreaSettings.onExtractEditedValue
(retObj, editAreaSettings, editArea);
else
//otherwise call predefined event of IEC
extractEditedValue(retObj, editAreaSettings, editArea);
}
//return the obj that holds new edited values
return retObj;
}
function extractEditedValue(refRetObj, editAreaSettings, editArea)
{
var children = extractDHTMLElements(editArea);
if(editAreaSettings.areaDataSourceControlID == null)
{ //save new value
refRetObj[editAreaSettings.areaName] = children[1].value;
//assign new value for readable field
children[0].innerHTML = children[1].value;
}
else if(children[1].tagName == 'SELECT' )
{
var select = children[1];
refRetObj[editAreaSettings.areaName] =
(trimText(select.options[select.selectedIndex].value)=="")
? null : select.options[select.selectedIndex].value;
children[0].innerHTML =
(trimText(select.options[select.selectedIndex].value)=="") ?
"" : select.options[select.selectedIndex].text;
}
}
在此阶段有一个可能的自定义选项。
onExtractEditedValue
可以使用自定义处理程序从“编辑区域”提取已编辑的值。示例
第四阶段。禁用编辑模式
当我们保存更改后,表格行应恢复到原始状态。实际上,这并非易事。cancelEditRow
函数负责转换。
function onEditing(eventObj)
{
if(eventObj == null)
eventObj = event;
//27 - key code of escape key
if(eventObj.keyCode == 27)
onCancelAction();
...
}
function onCancelAction()
{
cancelEditRow();
}
function cancelEditRow()
{
if(activeInlineEditPanel == null)
return;
var children = extractDHTMLElements(activeInlineEditPanel);
for(var index=0; index < children.length; index++)
{
var control = children[index];
switch(control.attributes[INLINE_ACTION_ATTRIBUTE].nodeValue)
{
case INLINE_EDIT_ACTION: control.style.display=''; break;
case INLINE_SAVE_ACTION: control.style.display='none';break;
case INLINE_CANCEL_ACTION: control.style.display='none';break;
default:/* "Do nothing" */;
}
}
highLightInlineEditRow(INLINE_ROW_BACKGROUNDCOLOR_NORMAL);
var itemID = getActiveInlineEditPanelID();
if(itemID == null)
{
alert("error: Unable to find item id.");
activeInlineEditPanel = null;
return;
}
for(var index=0; index < editAreasSettings.length; index++)
{
var editAreaSettings = editAreasSettings[index];
var editArea = document.getElementById(editAreaSettings.areaPrefix+itemID);
if(editArea == null)
continue;
if(editAreaSettings.onCancelEditValue != null)
{
editAreaSettings.onCancelEditValue(editAreaSettings, editArea);
}
else
{
if(editAreaSettings.areaDataSourceControlID == null)
cancelEditTextField(editArea);
else
cancelEditDataSourceField(editArea);
}
if(editAreaSettings.onRenderEditedValue!=null)
editAreaSettings.onRenderEditedValue(editAreaSettings, editArea);
}
activeInlineEditPanel = null;
}
在此阶段有两种可能的自定义选项。
onCancelEditedValue
可以使用自定义处理程序将编辑区域设置为原始(只读)状态。示例
onRenderEditedValue
可以使用自定义处理程序渲染具有特殊性的原始(只读)标签,例如显示代表优先级或其他内容的图像。示例
TransactObject 实现
TransactObject
是一个辅助对象,用于管理保存过程(它充当同步器,并在服务器端错误时恢复旧值,并确认保存正确)。
IEC 在服务器仍在处理请求时,用新值替换可编辑字段中的文本。由于更新是异步的,因此存在 Web 服务异常同步问题。当对象无法保存并且我们收到异常时会发生什么?新值将被设置为行,但这些值不再正确。
另一个问题是 AJAX.NET 将 Web 服务的所有异常包装到 WebServerError
类中,无法获取有关哪个项目失败的信息。TransactionObject
处理了这些问题。它有一个 IEC 的引用,可以在出错时恢复初始值。IEC 在调用 commitChanges
或 abandonChanges
方法之前一直保留项目的初始值。
function onSaveEventHandler(retObj)
{
var transactionObj = new TransactionObject(retObj.itemID, inlineEditController);
transactionObj.onStatusSuccess = onRequestComplete;
transactionObj.onStatusError = onRequestError;
retObj.OrderID = retObj.itemID;
TargetProcess.DaoTraining.BusinessLogicLayer.OrderService.UpdateOrder(retObj,
transactionObj.onSaveSuccess ,
transactionObj.onSaveError)
}
function TransactionObject(itemID, inlineEditController, onSuccessStatus, onErrorStatus)
{
var thisRefTransactionObject = this;
this.inlineEditController = inlineEditController;
this.itemID = itemID;
this.onStatusSuccess = onSuccessStatus;
this.onStatusError = onErrorStatus;
//--- Events ---
this.onSaveSuccess = function(result)
{
//remove the initial values for item from IEC.
thisRefTransactionObject.inlineEditController.commitChanges
(thisRefTransactionObject.itemID);
//call custom handler if such handler is provided
if(thisRefTransactionObject.onStatusSuccess)
thisRefTransactionObject.onStatusSuccess(result);
}
this.onSaveError = function(ex, context)
{
//restore the initial values for item
thisRefTransactionObject.inlineEditController.abandonChanges
(thisRefTransactionObject.itemID);
if(thisRefTransactionObject.onStatusError)
thisRefTransactionObject.onStatusError(ex);
}
扩展基本功能。自定义编辑处理程序
默认情况下,IEC 具有简单的文本和下拉框编辑逻辑,因为不可能实现所有情况。
有时,您会遇到基本功能不够用的情况。例如,Order
类的 Priority
属性。当行处于只读状态时,它必须显示优先级图像,而在编辑状态时,它必须显示下拉框。

<asp:TemplateField>
<ItemTemplate>
<%--priorityname attribute holds priority value --%>
<span priorityname='<%#Eval("Priority")%>'
id="PriorityIdPrefix<%# Eval("OrderID")%>">
<span>
<%#GetPriorityHTML(Container.DataItem)%>
</span>
</span>
</ItemTemplate>
</asp:TemplateField>
var prioritySettings = new EditAreaSettings
("Priority","PriorityIdPrefix","uxPriorities");
prioritySettings.onRenderEditedValue = onRenderPriorityValue;
prioritySettings.onSelectValue = onSelectPriorityValue;
editAreasSettings.push(prioritySettings);
var inlineEditController = new InlineEditController
('<%=uxOrders.ClientID%>', editAreasSettings, onSaveEventHandler, true);
var HIGHEST_PRIORITY_IMAGE = "<%=HIGHEST_PRIORITY_IMAGE%>";
var NORMAL_PRIORITY_IMAGE = "<%=NORMAL_PRIORITY_IMAGE %>";
var LOWEST_PRIORITY_IMAGE = "<%=LOWEST_PRIORITY_IMAGE %>";
var HIGHEST_PRIORITY = "<%=Priority.Highest %>";
var NORMAL_PRIORITY = "<%=Priority.Normal %>";
var LOWEST_PRIORITY = "<%=Priority.Lowest%>";
var PRIORITY_ATTR = "priorityname";
需要提供两个处理程序:onRenderPriorityValue
和 onSelectPriorityValue
。
onSelectPriorityValue
处理程序仅在下拉框中选择预定义的值。
function onSelectPriorityValue(editAreaSettings, editArea)
{
var dataSourceControl =
document.getElementById(editAreaSettings.areaDataSourceControlID);
dataSourceControl.value = editArea.attributes[PRIORITY_ATTR].nodeValue;
}
onRenderPriorityValue
将 Priority
值转换为特定图像。
function onRenderPriorityValue(editAreaSettings, editArea)
{
var dataSourceControl =
document.getElementById(editAreaSettings.areaDataSourceControlID);
var children = extractDHTMLElements(editArea);
var readField = children[0];
switch(dataSourceControl.value)
{
case HIGHEST_PRIORITY:
readField.innerHTML = HIGHEST_PRIORITY_IMAGE;
break;
case NORMAL_PRIORITY:
readField.innerHTML = NORMAL_PRIORITY_IMAGE;
break;
case LOWEST_PRIORITY:
readField.innerHTML = LOWEST_PRIORITY_IMAGE;
default:/*Do nothing*/
}
editArea.attributes[PRIORITY_ATTR].nodeValue = dataSourceControl.value;
}
这种情况相当独特。在大多数情况下,IEC 的基本逻辑将满足您的要求。例如,Order
类的 Country
属性工作得很好。
<asp:TemplateField HeaderText="Ship Country">
<ItemTemplate>
<span id="ShipCountryIdPrefix<%# Eval("OrderID")%>">
<asp:Label ID="Label1" runat="server" Text='<%#Eval("ShipCountry")%>'></asp:Label>
</span>
</ItemTemplate>
</asp:TemplateField>
.....
<select style="display: none" id="uxCountries">
<option>-- Select Country</option>
<option value="USA">United States</option>
<option value="Canada">Canada</option>
<option value="Mexico">Mexico</option>
<option value="Afghanistan">Afghanistan</option>
<option value="Albania">Albania</option>
....
var shipCountrySettings =
new EditAreaSettings("ShipCountry","ShipCountryIdPrefix","uxCountries");
editAreasSettings.push(shipCountrySettings);
如您所见,没有自定义处理程序。您所要做的就是将 uxCountries
ID 传递给 shipCountrySettings
。IEC 将使用列中的文本成功选择下拉框中的值并呈现新值。
扩展基本功能。复选框处理程序
如果您的布尔值在网格中表示为复选框怎么办?让我们看看如何实现。

<asp:TemplateField HeaderText="Rush Order">
<ItemTemplate>
<span id="IsRushOrderIdPrefix<%#Eval("OrderID")%>" >
<asp:CheckBox Enabled="false" runat="server"
Checked='<%#Eval("isRushOrder") %>' ID="uxRushOrder" />
</span>
</ItemTemplate>
</asp:TemplateField>
var isRushOrderSettings = new EditAreaSettings("IsRushOrder","IsRushOrderIdPrefix");
isRushOrderSettings.onEditValue = onEditRushValueHandler;
isRushOrderSettings.onExtractEditedValue = onExtractRushValueHandler;
isRushOrderSettings.onCancelEditValue = onCancelEditRushValueHandler;
editAreasSettings.push(isRushOrderSettings);
我需要创建三个自定义处理程序:onEditRushValueHandler
、onExtractRushValueHandler
和 onCancelEditRushValueHandler
。onEditValue
仅启用复选框。
function onEditRushValueHandler(editAreaSettings, editArea)
{
var isRushOrderSpan = getFirstDHTMLElement(editArea);
isRushOrderSpan.disabled = false;
var isRushOrderChk = getFirstDHTMLElement(isRushOrderSpan);
isRushOrderChk.disabled = false;
}
onExtractRushValueHandler
从复选框获取新值并将其保存在 retObj
中(您还记得,它会传递到 onSaveEventHandler
)。
function onExtractRushValueHandler(retObj, editAreaSettings, editArea)
{
var isRushOrderChk = getFirstDHTMLElement(getFirstDHTMLElement(editArea));
retObj[editAreaSettings.areaName] = isRushOrderChk.checked;
}
onCancelEditValue
禁用复选框。
function onCancelEditRushValueHandler(editAreaSettings, editArea)
{
var isRushOrderSpan = getFirstDHTMLElement(editArea);
isRushOrderSpan.disabled = true;
var isRushOrderChk = getFirstDHTMLElement(isRushOrderSpan);
isRushOrderChk.disabled = true;
}
这样,您就可以为任何情况创建自定义处理程序,即使是极其复杂的情况。
输入验证
在 JavaScript 中没有标准的输入验证解决方案(例如 ASP.NET 验证器),但您仍然可以使用充当验证器的处理程序。幸运的是,TransactObject
会在服务器端出错时恢复初始值,这可能是由无效输入引起的,但这还不够。我们需要为 Freight
属性实现额外的验证,该属性应为 Decimal
类型以验证输入。
<asp:TemplateField HeaderText="Freight">
<ItemTemplate>
<span id="FreightIdPrefix<%# Eval("OrderID")%>">
<asp:Label ID="Label1" runat="server" Text='<%#Eval("Freight")%>'></asp:Label>
</span>
</ItemTemplate>
</asp:TemplateField>
var freightSettings = new EditAreaSettings("Freight","FreightIdPrefix");
freightSettings.onSelectValue = onSelectFreightValueHandler;
editAreasSettings.push(freightSettings);
我们只需要实现一个处理程序 onSelectFreightValueHandler
。此处理程序为 Freight
输入框分配验证处理程序,并且无法键入任何除了数字和小数分隔符以外的字符。
function onSelectFreightValueHandler(editAreaSettings, editArea)
{
var children = extractDHTMLElements(editArea);
//children[0] - readable field that gets invisible since the row is in editable state
//children[1] - input field that gets added since the row is in editable state
children[1].onkeypress = onKeyPressDecimalValueHandler;
}
function onKeyPressDecimalValueHandler(eventObj,obj)
{
var thisRef;
if(obj == null)
thisRef = this;
else
thisRef = obj;
function isValidInput(key,value)
{
if(key == "." && value.indexOf(".")==-1)
return true;
if(isNaN(key))
return false;
else
return true;
}
if(typeof event == "undefined")
{
var key = String.fromCharCode(eventObj.charCode);
if(eventObj.charCode == 0)
return;
if(!isValidInput(key, thisRef.value))
eventObj.preventDefault();
}
else
{
var key = String.fromCharCode(event.keyCode);
if(!isValidInput(key, thisRef.value))
event.returnValue = false;
}
}
另一个解决方案是使用 TransactObject
作为验证器。例如,如果我尝试输入无效日期,TransactObject
会恢复初始值并显示错误消息。
现在您可以在所有列表中无问题地使用行内编辑了。