|
 |
栏目导栏 |
|
| |
|
|
|
|
 |
资料搜索 |
|
| |
|
|
|
|
 |
热门文章 |
|
| |
|
|
|
|
 |
最新文章 |
|
| |
|
|
|
| |
| |
|
|
|
|
在UpdatePanel对页面进行部分刷新时注册一些Data Item是ASP.NET AJAX的特点之一。我们可以在服务器端为某个控件注册一个字符串甚至是一个对象,然后在客户端将将其取回。但是现在我希望向您展示一些您可能会忽视的事情。 SFFLinux联盟 SFFLinux联盟 让我们先从最基本的用法开始。在一个异步回送过程中,我们可以调用RegisterDataItem方法将一个字符串与一个UpdatePanel绑定起来: SFFLinux联盟 SFFLinux联盟 RegisterDataItem方法调用 SFFLinux联盟 ScriptManager.GetCurrent(this.Page).RegisterDataItem(this.UpdatePanel1, "DataItem"); SFFLinux联盟 SFFLinux联盟 SFFLinux联盟 在客户端,如果我们监听pageLoading,pageLoaded或者endRequest事件,我们就可以使用下面的代码,使用控件的ClientID将这个字符串取回: SFFLinux联盟 SFFLinux联盟 将字符串取回 SFFLinux联盟 Sys.WebForms.PageRequestManager.getInstance().add_pageLoading( SFFLinux联盟 function(sender, e) SFFLinux联盟 { SFFLinux联盟 var dataItem = e.get_dataItems()["<%= this.UpdatePanel1.ClientID %>"]; // "DataItem" SFFLinux联盟 // more implementations... SFFLinux联盟 }); SFFLinux联盟 SFFLinux联盟 SFFLinux联盟 RegisterDataItem方法还有一个重载的版本会接受一个额外的参数,其作用是使用一个布尔值来表明我们注册的dataItem是不是已经被序列化为JSON。但是这里就出了一个问题——甚至在官方文档中的示例也是不正确的。 SFFLinux联盟 SFFLinux联盟 下面的代码选自官方文档中的示例: SFFLinux联盟 SFFLinux联盟 官方文档中的示例 SFFLinux联盟 protected void Page_Load(object sender, EventArgs e) SFFLinux联盟 { SFFLinux联盟 if (ScriptManager1.IsInAsyncPostBack) SFFLinux联盟 { SFFLinux联盟 System.Web.Script.Serialization.JavaScriptSerializer json = SFFLinux联盟 new System.Web.Script.Serialization.JavaScriptSerializer(); SFFLinux联盟 ScriptManager1.RegisterDataItem(Label1, DateTime.Now.ToString()); SFFLinux联盟 ScriptManager1.RegisterDataItem(Label2, json.Serialize("more data"), true); SFFLinux联盟 } SFFLinux联盟 } SFFLinux联盟 SFFLinux联盟 SFFLinux联盟 这个示例的确能够正常工作,但是它事实上掩盖了一个问题。请注意,序列化之后的Data Item会被JavaScript内置的eval方法解释执行: SFFLinux联盟 SFFLinux联盟 使用eval函数解释执行 SFFLinux联盟 function Sys$WebForms$PageRequestManager$_onFormSubmitCompleted(sender, eventArgs) SFFLinux联盟 { SFFLinux联盟 //... SFFLinux联盟 SFFLinux联盟 for (i = 0; i < dataItemJsonNodes.length; i++) { SFFLinux联盟 var dataItemJsonNode = dataItemJsonNodes[i]; SFFLinux联盟 this._dataItems[dataItemJsonNode.id] = eval(dataItemJsonNode.content); SFFLinux联盟 } SFFLinux联盟 SFFLinux联盟 //... SFFLinux联盟 } SFFLinux联盟 SFFLinux联盟 SFFLinux联盟 在官方文档的示例中,字符串“more data”会被序列化为“"more data"”(请注意多出的双引号),这样从eval方法中得到的结果就是“more data”,这正是我们注册的内容。这个再正常不多了,不是吗?猜猜看如果我们将一个序列化的对象注册到客户端时会发生什么事情呢?我为此写了一个示例: SFFLinux联盟 SFFLinux联盟 注册一个对象 SFFLinux联盟 // the definition of Person class SFFLinux联盟 public class Person SFFLinux联盟 { SFFLinux联盟 public string Name; SFFLinux联盟 } SFFLinux联盟 SFFLinux联盟 SFFLinux联盟 // the code to register a Person object SFFLinux联盟 Person person = new Person(); SFFLinux联盟 person.Name = "Jeffz"; SFFLinux联盟 SFFLinux联盟 JavaScriptSerializer serializer = new JavaScriptSerializer(); SFFLinux联盟 ScriptManager.GetCurrent(this.Page).RegisterDataItem( SFFLinux联盟 this.UpdatePanel1, serializer.Serialize(person), true); SFFLinux联盟 SFFLinux联盟 SFFLinux联盟 当我们进行异步更新时就会抛出异常。似乎这是因为服务器端注册的表示Person对象的JSON字符串“{"Name":"Jeffz"}”无法被直接传递进入eval方法。具体的原因关乎语法方面的问题,可以在ECMAScript Specification中找到解释,因此我在这里就不多作解释了。那么,请注意我下面的代码: SFFLinux联盟 SFFLinux联盟 为JSON字符串添加括号 SFFLinux联盟 ScriptManager.GetCurrent(this.Page).RegisterDataItem( SFFLinux联盟 this.UpdatePanel1, "(" + serializer.Serialize(person) + ")", true); SFFLinux联盟 SFFLinux联盟 SFFLinux联盟 现在,我们的代码就能正常工作了,于是我们就可以在客户端将Person对象取出: SFFLinux联盟 SFFLinux联盟 在客户段取出Person对象 SFFLinux联盟 Sys.WebForms.PageRequestManager.getInstance().add_pageLoading( SFFLinux联盟 function(sender, e) SFFLinux联盟 { SFFLinux联盟 var person = e.get_dataItems()["<%= this.UpdatePanel1.ClientID %>"]; SFFLinux联盟 alert(person.Name); // "Jeffz" SFFLinux联盟 }); SFFLinux联盟 SFFLinux联盟 SFFLinux联盟 这样看来,这个问题也就可以使用这个方法来解决了。不过为什么ASP.NET AJAX会使用eval方法,而不是定义在Microsoft AJAX Library中的deserialize方法来反序列化一个对象呢?我们可以发现,在deserialize方法中,一个表达式被传递给eval方法之前会被自动加上括号: SFFLinux联盟 SFFLinux联盟 desrialize方法 SFFLinux联盟 Sys.Serialization.JavaScriptSerializer.deserialize = function (data) SFFLinux联盟 { SFFLinux联盟 try SFFLinux联盟 { SFFLinux联盟 var exp = data.replace(...); SFFLinux联盟 return eval('(' + exp + ')'); SFFLinux联盟 } SFFLinux联盟 catch (e) SFFLinux联盟 { SFFLinux联盟 throw Error.argument('data', Sys.Res.cannotDeserializeInvalidJson); SFFLinux联盟 } SFFLinux联盟 } SFFLinux联盟 SFFLinux联盟 SFFLinux联盟 我猜想,会不会是因为编写部分刷新相关代码的开发人员并不完全了解Microsoft AJAX Library中的功能呢?不过可能性最大的原因则是这些代码没有被完整地Review和测试过。事实上这并不是ASP.NET AJAX最终版本中唯一的bug,其他的还包括那个著名的“跨域名frame的拒绝访问错误”和StringBuilder的Bug。 SFFLinux联盟 SFFLinux联盟 嗯,我们问题也看够了,就把目光移向别处吧。尽管逻辑上一个Data Item应该是一个包含信息的对象,但是请注意我们是使用eval方法来“反序列化”一个JSON字符串的。这意味着我们事实上可以将任意的合法表达式发送到客户端,然后它就会被正确执行。请看下面的示例: SFFLinux联盟 SFFLinux联盟 服务器端代码 SFFLinux联盟 ScriptManager.GetCurrent(this.Page).RegisterDataItem( SFFLinux联盟 this.UpdatePanel1, "var __f = function(){alert('Hello World!');}; __f;", true); SFFLinux联盟 SFFLinux联盟 客户端代码 SFFLinux联盟 Sys.WebForms.PageRequestManager.getInstance().add_pageLoading( SFFLinux联盟 function(sender, e) SFFLinux联盟 { SFFLinux联盟 e.get_dataItems()["<%= this.UpdatePanel1.ClientID %>"](); SFFLinux联盟 }); SFFLinux联盟 SFFLinux联盟 SFFLinux联盟 我在上面代码中将一个函数注册为一个Data Item并且在客户端将其执行了。如果您深入了解UpdatePanel局部刷新的过程,您会注意到您在服务器端注册的JavaScript代码只有在页面更新结束后才会生效,也就是说,我们无法在pageLoading事件被触发时执行这些代码。我会在之后的文章中针对这样的设计以及如何使用work arounds来解决这个问题进行详细的探讨。这些work arounds就是基于Data Item注册的使用方式的,因为表示Data Item的“JSON字符串”在pageLoading触发之前就被解释执行。
Linux联盟收集整理 ,转贴请标明原始链接,如有任何疑问欢迎来本站Linux论坛讨论 |
|
|
|
|
|