I spent the better part of yesterday trying to Ajax upload a File using a Web Service and Ext.Net (formerly Coolite). Let me first explain the situation I was working in.
I had a Page that renders to the browser. This page then loads appropriate UserControls from the server depending upon user actions. One of these User Controls allowed the User to upload files to the server, together with some data about the file being uploaded. A naive (and easy) approach would have been to handle the file upload in a normal ASP.NET Postback, or Ext.Net DirectMethod or DirectEvent.
However, this would have led all controls on the page submitting to the server together with the ViewState. As such a submission would have been pretty large in my case, I wanted to just upload the file using a Web service without submitting any other unrelated content on the page. This turned out to be more tricky than I anticipated.
I used regular <ext:FormPanel> and <ext:FileUploadField> inside a User Control, that was rendered on demand to the page. By default, ExtJs FormPanel renders a Html <form> tag. However, Ext.Net does not render a <form> tag for <ext:FormPanel> because the FormPanel itself is nested inside the parent <form runat=”server”> ASP.NET tag. This would lead to nesting of <form> tags in html which is not allowed as per Html specifications, and generates a javascript error in Internet Explorer (and possibly other browsers).
But you need the <form> tag to render for the <ext:FormPanel> if you want to be able to selectively submit the FormPanel content to a Web Service. Otherwise, the entire page would be submitted, which as noted above, was highly undesirable in my case.
Enabling the <form> tag to render for <ext:FormPanel> is as easy as setting the RenderFormElement=”true” for the FormPanel. However, as soon as you do that, you get javascript errors on the html page, because of nested <form> tags. I requested help at Ext.Net forums, but could not get a solution I was looking for. I almost decided to load the UserControl that needs to upload files in an <iframe> and then submit the <iframe> to the Web Service. But then, I had another idea, that worked seamlessly with all Ext.Net, ASP.NET and Html restrictions.
I was reminded of the good old “renderTo” attribute for ExtJs components. I thought why not to get the FormPanel to render inside a Window which has its renderTo property assigned to a parent <div> tag outside the <form runat=”server”> ASP.NET tag. And bingo, I had the solution.
The first step was to add some extra markup to the parent ASP.NET page:
<html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title></title> </head> <body> <form id="form1" runat="server"> <div> ... Regular page content </div> </form> <%--Div containers to act as parents for windows to be rendered outside the <form runat="server" tag. These windows can freely contain <form> tags themselves without causing html or javascript issues.--%> <div id="form-container"> <div id="usercontrol-win-container"> </div> </div> </body> </html>
Then, I added my FormPanel to an <ext:Window> which had its renderTo attribute set to one of the above divs.
<ext:Window runat="server" ID="wnd2" Title="Window 2" Hidden="true" Layout="Fit" Width="400" Height="300"> <Items> <ext:FormPanel runat="server" ID="pnl2" FileUpload="true" RenderFormElement="true" Layout="Form" Padding="5"> <Items> <ext:TextField runat="server" Name="first-name" FieldLabel="First Name" /> <ext:ComboBox runat="server" Name="country" FieldLabel="Country"> <Items> <ext:ListItem Text="India" Value="India" /> <ext:ListItem Text="USA" Value="USA" /> <ext:ListItem Text="Canada" Value="Canada" /> </Items> </ext:ComboBox> <ext:FileUploadField runat="server" ID="file2" Name="file" AllowBlank="false" Width="200" /> <ext:Button runat="server" text="Upload File" OnClientClick=" // Javascript code... " /> </Items> </ext:FormPanel> </Items> </ext:Window>
In the code-behind:
{syntaxhighlighter brush: csharp;fontsize: 100; first-line: 1; }protected void Page_Load (object sender, EventArgs e)
{
//The all important renderTo property assigmnent.
this.wnd2.RenderTo = “usercontrol-win-container”;
}{/syntaxhighlighter}
And that was it. Now, I just needed to submit the <ext:FormPanel> content to the Web Service when the appropriate button was clicked. Here’s the javascript for doing that:
{syntaxhighlighter brush: jscript;fontsize: 100; first-line: 1; }if(!#{file2}.isValid()){
Ext.Msg.alert(‘Error’, ‘Please select a file to upload’);
return;
}
#{pnl2}.getForm().submit({ url: #{pnl2}.submitUrl,
waitMsg: ‘Uploading… Please wait…’,
success: function(fp, o) {
Ext.Msg.alert(‘Success’, o.result.msg);
},
failure: function(form, action) {
switch (action.failureType) {
case Ext.form.Action.CLIENT_INVALID:
Ext.Msg.alert(‘Failure’, ‘Form fields may not be submitted with invalid values’);
break;
case Ext.form.Action.CONNECT_FAILURE:
Ext.Msg.alert(‘Failure’, ‘Ajax communication failed’);
break;
case Ext.form.Action.SERVER_INVALID:
Ext.Msg.alert(‘Failure’, action.result.msg);
}
}
});{/syntaxhighlighter}
Finally, on the server-side, you need to handle the file uploaded in the appropriate web service method. You can find the complete code with the Web Service, the ASP.NET page, the User Control and javascript attached with this post below.
Please note that this approach worked seamlessly for me, because I actually had to display the File Upload fields in a <ext:Window>. This window was rendered in html outside the ASP.NET form tag, and hence avoided any issues.
However, if you need to actually display these File Upload fields inline the other content on your ASP.NET page (and not inside a <ext:Window>), then the above approach would not be suitable for you, because the renderTo attribute target element would be towards the end (or top) of the page, below or after the ASP.NET <form runat=”server”> attribute. This implies that your file upload UI would be either towards the top or bottom of the page. If this is not desirable, <ext:Window> is not an option, and you strictly need to have the upload UI inline other content, but still want the upload to handle asynchronously to a Web Service method without submitting the entire page, then <iframe> would be the best bet for you.
Hi,
Small tip:
You can set DefaultRenderTo=”Body” to the window (dafault value is Form). In this case additional divs outside the form is not required
First very sorry for my english. When I testing solution as above using VS 2008 or 2010 on my local computer (Win XP SP3 with standard IIS) it’s worging very good but when I deployed solution on test server (Win XP SP3 with standard IIS) I have problem. When I click upload file still is “Uploading file…” message. I checked web.config file on local and server and it’s the same. Do you have idea where is the problem?
Hi rahul,
I updated my web.config file as you suggested and it’s working on test server with Win XP SP3:) Also I test it on W2k3 server and it’s working too.
Thanks a lot.
This would have led all controls on the page submitting to the server together with the ViewState. As such a submission would have been pretty large in my case, I wanted to just upload the file using a Web service without submitting any other unrelated content on the page.