随着Ajax技术的发展和成熟,要进行页面无刷新的数据交互已经并不是什么困难的事情。然而,如何使用该技术与web系统更好的融合并提升用户体验仍然是一个值得探讨的话题。完全从头编写和开发前端的Ajax交互的js脚本,似乎工作量和难度都较大,编写相应的js脚本时还需要考虑到浏览器兼容性问题,开发和测试过程都很容易影响到项目的进度。网上可用的Ajax套件也不少,比如基于jquery的ajax控件或基于Yahoo UI的扩展包yui-ext都可以实现这样的功能。由于以上工具包都实现了完整的ajax交互并且还附带一些其它常规的脚本控制功能而显得较为庞大,对于需要开发一些轻量级的、要具备较高效率的系统来说似乎是“杀鸡焉用牛刀”了。
我们这里所要介绍的是基于.net技术的页面无刷新实时交互功能的实现,其实在.net框架下只需使用少量的脚本便可以实现页面的无刷新交互功能,无需其它的ajax控件的支持。
以信息管理系统中对作者信息的在线实时编辑为例来进行说明,示例图片如图1,我们需要实时修改的是第4条信息中的名为“张学荣”的作者信息。
我们想要实现的效果是:双击该信息的单元格,该单元格出现可编辑的文本框,内容可任意修改,修改完成后在页面其它地方单击即触发该单元格中信息的提交。在系统后台接收修改后的信息,修改数据库中相应的数据,反馈到前台,前台该单元格中的信息更新为修改后的数据,整个过程页面无需刷新。
这里涉及到前后台的配合,在.net框架下,要实现无刷新的信息提交,需要对前台页面和后台程序进行一些设置。以.net2.0版本为例,需要在前台页面相应的文本单元格中绑定数据并添加处理脚本:
<td align="center" id='Aut<%# Eval("ID") %>' ondblclick='OnlineEdit(this,"Aut",<%# Eval("ID") %>)' title='<%# Eval("Author")%>'><%# TextManage.TrimString(Eval("Author").ToString(),4) %></td>
这里对单元格进行了编号,添加了"Aut"前缀并绑定文章ID值以方便脚本读取和功能区分。脚本响应了鼠标双击事件,即ondblclick触发脚本OnlineEdit,其参数分别是当前单元格对象、编辑对象标识名称、当前信息ID值。OnlineEdit的脚本代码如下:
function OnlineEdit(obj, act, id) { var tag; if(obj.firstChild!=null) tag = obj.firstChild.tagName; if ((typeof(tag) != "undefined" && tag.toLowerCase() == "input")||flag) { return; } var org = obj.innerHTML; var val = obj.getAttribute("title"); var txt = document.createElement("INPUT"); txt.value = (val == 'N/A') ? '' : val; txt.style.width = (obj.clientWidth-6) + "px" ; obj.innerHTML = ""; obj.appendChild(txt); txt.focus(); txt.onkeypress = function(e) { var evt = Utils.fixEvent(e); var obj = Utils.srcElement(e); if (evt.keyCode == 13) { obj.blur(); return false; } if (evt.keyCode == 27) { obj.parentNode.innerHTML = org; } } txt.onblur = function(e) { var arg=encodeURIComponent(Utils.trim(txt.value))+"$"+act+id; EditOnline(arg); } }
简单叙述一下以上脚本的功能:通过当前对象获取原始完整信息,然后在该单元格中绘制一个文本框,文本框的内容即为原始信息(如图2)。对该单元格监听事件onkeypress,即键盘按下事件,如果发现用户按下ESC则返回原始信息取消编辑,若用户按下回车键则触发onblur事件。这里的onblur事件即失去焦点事件,用于提交信息,提交前对信息进行编码,最后通过EditOnline方法向后台异步提交。提交完成服务器返回信息后再调用回调函数EditOnCallback,这也是写入前台页面中的,代码如下:
function EditOnCallback(result) { var obj=document.getElementById(CurID); if(result!="Error") { obj.removeAttribute("title"); obj.setAttribute("title",result); var w = obj.clientWidth||obj.offsetWidth; if(result.length>w/18) obj.innerHTML=result.substring(0,w/18)+"..."; else obj.innerHTML=result; flag=false; } else obj.innerHTML="<font color='red'>更新失败!</font>"; }
该函数的作用是获取服务器返回的更新后的信息,判断信息长度,根据要求截断后显示在用户所编辑的单元格中。
为了让前后台能异步交互,就需要在程序页面需要在原有的页面类的基础上再继承ICallbackEventHandler类,这个类的作用就是处理异步信息的提交。除此以外,在后台页面还需要手工编写一些其它的代码:
public void RaiseCallbackEvent(string Id) { try { string content = ""; int index = Id.IndexOf("$"); if (index != -1) { content =System.Web.HttpUtility.UrlDecode(Id.Substring(0, index)); Id = Id.Substring(index+1); } ArticleInfo info = SysManage.ReadArticleInfo(Convert.ToInt32(Id.Substring(3))); using (DataProvider provider = new DataProvider()) { if (Id.IndexOf("Aut") != -1) { info.Author = content; SysManage.ModifyArticle(info, provider); this.a = content; } } } catch (Exception e) { this.a = "Error"; } }
以上均为异步提交函数的重写代码,这里的实现逻辑是,通过前台提交的数据以"$"符号分隔,符号前为更新的信息内容(由于内容经过前台转码,因此这里需要解码),后为更新的文章ID值。根据该ID值和数据信息更新数据库,然后将更新后的信息内容通过参数this.a返回,如果出错则返回Error。
另外,为了使前后台的数据能通过脚本交互,还需要在前台注入脚本代码,如下:
string str1 = "<script language=\"javascript\">\n//<!--\nfunction EditOnline(Id) \n{ \nif(!flag){\n CurID=Id.substring(Id.indexOf(\"$\")+1);\n flag=true;\n document.getElementById(CurID).innerHTML=\"<font color='Green'>提交中…</font>\";\n " + this.Page.ClientScript.GetCallbackEventReference(this, "Id", "EditOnCallback", null) + ";\n} \n return false;\n}\n//-->\n</script>";
Page.ClientScript.RegisterClientScriptBlock(base.GetType(), "OnlineEdit", str1);
该代码一方面与OnlineEdit(obj, act, id)脚本对接实现信息的提交,另一方面也同时处理正在提交过程中的提交事件,这里显示的是提交状态信息“提交中…”,为了防止信息编辑和提交过程中的逻辑冲突,这里用到了一个起互斥锁作用的变量flag。至此,在线实时编辑信息的功能便完成了,类似的方式还可以实现其它项目的编辑、数据的删除、状态更改等。