[ Scenario ] [ The Solution ] [ Implemented with JavaScript ] [ Implemented with JQuery ] [ The Code ]
Problem Statement
A page may have several links and buttons to produce different forms of outout. Every single piece of data defined in the page including the input supplied by the user is needed for postback process. Is there a simple way to redirect the output in another window without overriding the current page so that the current page content stays perisistent?
Solution 1: Can we use target
attribute of the element?
For a hyperlink, you may think using target
attribute to accomplish this.
With this approach, you have to gather all the needy input yourself from the current page
into the querystring of the URL before posting data back to the server. This will introduce
you another problem: querystring size limitation. Different plaforms and browser types
have different limitations. Least to say, there is some work to be done in the client side.
In addition, your server page must be configured and coded to handle HTTP GET to process the
request.
For input button, we are out of luck. There isn't a target
attribute defined in
the specification.
Thus, the action is completely relying on the form action.
Solution 2: How about AJAX?
Another approach to resolve this is using partial page rendering that would provide
better user experience by eliminating the full page round trip overhead. Does it really
matter in our case if we need every single piece of data defined or entered by the user
in that page? It may save some overhead on some resources being loaded. Other than that,
every data specified in that page is still posted back to the server for process. What
we want is the output in another window so that the user may continue to use the
same data to generate another form of output in a separate window and so on. Indeed, using
AJAX is a bit complicated here. Of course, you may use some library like JQuery to set up
your AJAX calls. For ASP.NET, you can simply accomplish this by using ScriptManager
and UpdatePanel
. Unfortunately, all of them require you to re-architect
your page to accommodate the changes. To me, it is too much work. I need something
simpler and make changes as least as possible in the page.
The Ultimate and Simple Solution: by using target
attribute of the
form
.
The solution is to use the old trick defined in HTML with a little Javascript assistance.
In order to post data back to the server, we need a form
tag. We usually
don't specify the target
attribute. In this case,
the data is returned to the current page by default. If a target
attribute is specified in
the form
tag, the output will be automatically routed to the specific frame or
window by the browser when the data is returned by the server. For example,
Route data to a new window:
<form name="form1" id="form1" target="_blank" method="post" action="Default.aspx" onsubmit="..." >
...
</form>
</pre>
Route data to an iframe:
<form name="form1" id="form1" target="myIframe" method="post" action="Default.aspx" onsubmit="..."> ... </form> <iframe name="myIframe" id="myIframe" width="400" height="300"></iframe>
Note that the
name
attribute of the iframe
is required in this case.
By specification,
we should specify the_name_of_the_frame_or_window to the target
attribute,
not the_id_of_the_frame_or_window. But the id works for some Webkit type of browsers
like Google Chrome.
With this simple solution in mind, I finally come up a way to unpuzzle the above problem with a very minor change in the page. The following is the solution presented in ASP.NET. The technique can be applied to elsewhere such as a simple HTML/CGI program.
What I need to do is dynamically adding a target
attribute to
the form
before the data is being posted back to the server.
JavaScript Solution
First, let's define the script which does the injection. The JavaScript function may look like
the following.
function changeFormTarget() { var formObj = document.getElementById('form1'); formObj.setAttribute('target', '_blank'); }
What the above code does is, before posting data back to the server for process,
it injects the target
attribute into the form
element by
producing the following HTML code, which instructs the browser where to output the
next document.
<form name="form1" id="form1" target="_blank" method="post" action="Default.aspx" onsubmit="...">
Then, we hook this function to the onclick event of the element before form submission.
In ASP.NET, simply add OnClientClick
event on the control to instruct the ASP.NET
engine to process the client script first before postback.
<asp:LinkButton ID="lnkRptToNewTarget" runat="server" OnClientClick="changeFormTarget()" OnClick="RptToNewTarget_Click">Generate Report to New Target</asp:LinkButton><br /> <asp:Button ID="btnRptToNewTarget" runat="server" OnClientClick="changeFormTarget()" OnClick="RptToNewTarget_Click" Text="Generate Report to New Target" />
The above markup will result in the following HTML code:
<a onclick="changeFormTarget();" id="lnkRptToNewTarget" href="javascript:WebForm_DoPostBackWithOptions(new WebForm_PostBackOptions("lnkRptToNewTarget", "", true, "", "", false, true))">Generate Report to New Target</a><br /> <input type="submit" name="btnRptToNewTarget" id="btnRptToNewTarget" value="Generate Report to New Target" onclick="changeFormTarget();WebForm_DoPostBackWithOptions(new WebForm_PostBackOptions("btnRptToNewTarget", "", true, "", "", false, false))" />
As you can see, with the very minimal changes in the page, a new window will be spawned off from the current page when the result is back.
JQuery Solution
If you're using JQuery, the solution is even simpler without changing any element in the page. Simply put the following script and then the onclick event will be automatically wired to the appropriate elements. In my example, simply ask JQuery to scan my elements and then wire them up with my supplied onclick event.
$('#lnkRptToNewTarget,#btnRptToNewTarget').click(function() { $('form').attr('target', '_blank'); });
With JQuery, basically the page remains intact without any markup or element being changed. The result is the same as JavaScript approach. Of course, you can write your own auto event wire up to achieve what JQuery does but there will be a lot of work.
Sometimes a simple solution works like a charm and it also eliminates the time to re-test and shortens the development time. I hope you will find this piece of information somehow useful.
The Code
If you want to test it yourself and see how this works, here is my simple backend event handler for
output. In real life, the handler could be in another process which generates PDF or
other non-HTML types of documents, and then call Response.Redirect() or Response.TransmitFile()
to return the document to the client.
protected void RptToNewTarget_Click(object sender, EventArgs e) { Response.Write( string.Format("Your report <b>{0}</b> is generated.", this.txtReportName.Text)); Response.End(); }
Here is the markup for JQuery approach. You can simply alter it to JavaScript solution that
I discussed above.
<form id="form1" runat="server" defaultfocus="txtReportName"> <div> Enter Report Name: <br /> <asp:TextBox ID="txtReportName" runat="server"></asp:TextBox> <asp:RequiredFieldValidator ID="RequiredFieldValidatorTxtReportName" runat="server" ErrorMessage="Please enter the report name." Display="Dynamic" ControlToValidate="txtReportName"></asp:RequiredFieldValidator><br /> <asp:LinkButton ID="lnkRptToNewTarget" runat="server" OnClick="RptToNewTarget_Click">Generate Report to New Target</asp:LinkButton><br /> <asp:Button ID="btnRptToNewTarget" runat="server" OnClick="RptToNewTarget_Click" Text="Generate Report to New Target" /> </div> </form> <script type="text/javascript"> $('#lnkRptToNewTarget,#btnRptToNewTarget').click(function() { $('form').attr('target', '_blank'); }); </script>