我们的UpdatePanelIFrameExecutor继承了WebRequestExecutor,因此需要实现许多方法和属性。但是我们事实上不用完整地实现所有的成员,因为客户端的异步刷信机制只会访问其中的一部分。以下是异步刷信过程中会使用的成员列表,我们必须正确地实现它们:
- get_started: 表示一个Executor是否已经开始 了。
- get_responseAvailable: 表示一个请求是否成功。
- get_timedOut: 表示一个请求是否超时。
- get_aborted: 表示一个请求是否被取消了。
- get_responseData: 获得文本形式的Response Body。
- get_statusCode: 获得Response的状态代码
- executeRequest: 执行一个请求。
- abort: 停止正在运行的请求。
UploadPanelIFrameExecutor依旧非常简单,只是定义了一些私有变量:
UpdatePanelIFrameExecutor构造函数Jeffz.Web.UpdatePanelIFrameExecutor = function(sourceElement) { Jeffz.Web.UpdatePanelIFrameExecutor.initializeBase(this); // for properties this._started = false; this._responseAvailable = false; this._timedOut = false; this._aborted = false; this._responseData = null; this._statusCode = null; // the element initiated the async postback this._sourceElement = sourceElement; // the form in the page. this._form = Sys.WebForms.PageRequestManager.getInstance()._form; // the handler to execute when the page in iframe loaded. this._iframeLoadCompleteHandler = Function.createDelegate( this, this._iframeLoadComplete); }
当executeRequest方法被调用时,我们会准备一个隐藏的iframe和所有的附加的隐藏输入元素,并将form的target指向iframe。当然,其他一些工作也是必须的,例如准备一个衡量超时的计时器:
executeRequest : function() { // create an hidden iframe this._iframe = this._createIFrame(); // all the additional hidden input elements this._addAdditionalHiddenElements(); // point the form's target to the iframe this._form.target = this._iframe.id; this._form.encType = "multipart/form-data"; // set up the timeout counter. var timeout = this._webRequest.get_timeout(); if (timeout > 0) { this._timer = window.setTimeout( Function.createDelegate(this, this._onTimeout), timeout); } this._started = true; // restore the status of the element after submitting the form setTimeout(Function.createDelegate(this, this._restoreElements), 0); // sumbit the form this._form.submit(); },
建立一个隐藏得iframe元素很简单,但是我们该创建哪些附加的隐藏输入元素呢?自然我们表示“异步回送”的自定义标记是其中之一,那么剩下的还需要哪些呢?似乎我们只能通过阅读PageRequestManager的代码来找到问题的答案。还好,似乎阅读下面的代码并不困难:
function Sys$WebForms$PageRequestManager$_onFormSubmit(evt) { // ... // Construct the form body var formBody = new Sys.StringBuilder(); var count = form.elements.length; for (var i = 0; i < count; i++) { // ... // Traverse the input elements to construct the form body // ... } var request = new Sys.Net.WebRequest(); // ... // prepare the web request object // ... var handler = this._get_eventHandlerList().getHandler("initializeRequest"); if (handler) { var eventArgs = new Sys.WebForms.InitializeRequestEventArgs( request, this._postBackSettings.sourceElement); handler(this, eventArgs); continueSubmit = !eventArgs.get_cancel(); } // ... this._request = request; request.invoke(); // ... }
请注意红色部分的代码。可以发现有两种数据需要被添加为隐藏的输入元素。其一是ScriptManager相关的信息(第一部分的红色代码),其二则是变量“_additionalInput”的内容。我们很容易得到前者的值,但是后者的内容究竟是什么呢?我们继续阅读代码:
function Sys$WebForms$PageRequestManager$_onFormElementClick(evt) { var element = evt.target; if (element.disabled) { return; } // Check if the element that was clicked on should cause an async postback this._postBackSettings = this._getPostBackSettings(element, element.name); if (element.name) { if (element.tagName === 'INPUT') { var type = element.type; if (type === 'submit') { this._additionalInput = element.name + '=' + encodeURIComponent(element.value); } else if (type === 'image') { var x = evt.offsetX; var y = evt.offsetY; this._additionalInput = element.name + '.x=' + x + '&' + element.name + '.y=' + y; } } else if ((element.tagName === 'BUTTON') && (element.name.length !== 0) && (element.type === 'submit')) { this._additionalInput = element.name + '=' + encodeURIComponent(element.value); } } }
_onFormElmentClick方法会在用户点击form中特定元素时执行。方法会提供变量“_additionalInput”的内容,然后紧接着,我们之前分析过的_onFormSubmit方法会被调用。现在我们就能够轻松地为form添加额外的隐藏输入元素了:
_addAdditionalHiddenElements : function() { var prm = Sys.WebForms.PageRequestManager.getInstance(); // clear the array of hidden input elements this._hiddens = []; // custom sign to indicate an async postback this._addHiddenElement("__AjaxFileUploading__", "__IsInAjaxFileUploading__"); // the value related to the ScriptManager this._addHiddenElement(prm._scriptManagerID, prm._postBackSettings.panelID); // find the additional data var additionalInput = null; var element = this._sourceElement; if (element.name) { var requestBody = this.get_webRequest().get_body(); if (element.tagName === 'INPUT') { var type = element.type; if (type === 'submit') { var index = requestBody.lastIndexOf("&" + element.name + "="); additionalInput = requestBody.substring(index + 1); } else if (type === 'image') { var index = requestBody.lastIndexOf("&" + element.name + ".x="); additionalInput = requestBody.substring(index + 1); } } else if ((element.tagName === 'BUTTON') && (element.name.length !== 0) && (element.type === 'submit')) { var index = requestBody.lastIndexOf("&" + element.name + "="); additionalInput = requestBody.substring(index + 1); } } // parse the additional data if (additionalInput) { var inputArray = additionalInput.split("&"); for (var i = 0; i < inputArray.length; i++) { var nameValue = inputArray[i].split("="); this._addHiddenElement(nameValue[0], decodeURIComponent(nameValue[1])); } } }, _addHiddenElement : function(name, value) { var hidden = document.createElement("input"); hidden.name = name; hidden.value = value; hidden.type = "hidden"; this._form.appendChild(hidden); Array.add(this._hiddens, hidden); },
除去附加的隐藏输入元素非常简单,不值一提。另外iframe在加载结束后的逻辑也很容易理解——不过解析内容的机制就另当别论了:
_iframeLoadComplete : function() { var iframe = this._iframe; delete this._iframe; var responseText = null; try { // ... // retrieve the data we need // ... this._statusCode = 200; this._responseAvailable = true; } catch (e) { this._statusCode = 500; this._responseAvailable = false; } $removeHandler(iframe, "load", this._iframeLoadCompleteHandler); iframe.parentNode.removeChild(iframe); this._clearTimer(); this.get_webRequest().completed(Sys.EventArgs.Empty); },
我们已经快完成我们的项目了。:)
本文转自 jeffz 51CTO博客,原文链接:http://blog.51cto.com/jeffz/59906,如需转载请自行联系原作者