Dynamically Loading ASP.NET User Controls with jQuery

UPDATE (16-07-2009)

Here is a new sample website with source code, it now disposes the page object and allows for WebControl rendering.

Dynamically Loading ASP.NET User Controls with jQuery v2.zip (25.52 kb)

—————————————— 

This post is only relevant if you are using WebForms. If you have converted to the MVC approach, then go read about Dependency Injection or NHibernate :)

If however you are using WebForms or are maintaining an application that was build with WebForms. Here is a way to partially render a usercontrol via jQuery (ajax).

Introduction

User controls are a great way to group markup and functionality, but in these AJAX times, the problem and nature of User Controls, in relation to ajax, is their fixed position in the Page Control tree.

Sam Mueller describes the issues and gives his solution to dynamically loaded user controls using jQuery.

Read it here:

http://samuelmueller.com/post/2008/12/20/Dynamically-Loading-ASPNET-User-Controls-with-jQuery.aspx

Sam Muellers raises some very interesting points, however I have two things I would like to change (URL and security).

URL

I very much like that Sam Mueller has found a solution to “directly” call a user control (view) with an URL. What I do not like is the URL to be called:

/ajax.svc/renderuc?path=/usercontrols/myusercontrol.ascx

I would like to be able to call the user control directly by this kind of URL:

/usercontrols/myusercontrol.ascx

And by using a HttpHandler this is possible. I have written an example here:

   1: public class AjaxUserControlHandler : IHttpHandler
   2:     {
   3:         public void ProcessRequest(HttpContext context)
   4:         {
   5:             // Get the path to the user control
   6:             string path = context.Request.Url.LocalPath;
   7: 
   8:             // Intialize the pseudo page and user control
   9:             Page page = new Page();
  10:             UserControl viewControl = page.LoadControl(path) as UserControl;
  11: 
  12:             // Display error if the user control was not found
  13:             if (viewControl == null)
  14:             {
  15:                 context.Response.StatusCode = 404;
  16:                 context.Response.Output.WriteLine("The requested url was not found");
  17:                 return;
  18:             }
  19: 
  20:             // Check existense of the AjaxEnabled attribute. Only add the AjaxEnabled attribut to 
  21:             // user controls that is safe for direct calls
  22:             var type = viewControl.GetType();
  23:             var attributes = type.GetCustomAttributes(typeof (AjaxEnabledAttribute), true);
  24:             AjaxEnabledAttribute attribute = attributes.FirstOrDefault() as AjaxEnabledAttribute;
  25: 
  26:             // If the attribute was not found, display an error
  27:             if (attribute == null)
  28:             {
  29:                 context.Response.StatusCode = 403;
  30:                 context.Response.Output.WriteLine("Access to the resource is not allowed");
  31:                 return;
  32:             }
  33: 
  34:
  35:             // Check if the request is valiud with regards to the requirements of the attribute.
  36:             // If not, display error
  37:             if ((attribute.Method == RequestMethodSupport.GET && context.Request.RequestType != "GET")
  38:                 || (attribute.Method == RequestMethodSupport.POST && context.Request.RequestType != "POST"))
  39:             {
  40:                 context.Response.StatusCode = 403;
  41:                 context.Response.Output.WriteLine(string.Format("The request method {0} is not allowed.", context.Request.RequestType));
  42:                 return;
  43:             }
  44: 
  45:             // Add user control to the pages control tree
  46:             page.Controls.Add(viewControl);
  47: 
  48:             // Disable caching, remove this if you will allow client caching
  49:             context.Response.CacheControl = "No-Cache";
  50: 
  51:             // Execute and return result to Output stream
  52:             context.Server.Execute(page, context.Response.Output, true);
  53:         }
  54: 
  55:         public bool IsReusable
  56:         {
  57:             get { return true; }
  58:         }
  59:     }

To make it work, you need to add the following to httpHandlers tag in the web.config.

<add verb="*" path="*.ascx" type="[NAMESPACE].AjaxUserControlHandler, [ASSEMBLY]"/>

Security

I have implemented an AjaxEnabled attribut, so that it is not possible to directly call any of the user controls (if you know the path to them) in the solution.

The attribute is simple and looks like this:

   1: public class AjaxEnabledAttribute : Attribute
   2: {
   3:     [DefaultValue(RequestMethodSupport.All)]
   4:     public RequestMethodSupport Method { get; set; }
   5: }
   6: 
   7: public enum RequestMethodSupport
   8: {
   9:     All,
  10:     GET,
  11:     POST
  12: }

By doing this, the handler will only allow calls to user controls that have the [AjaxEnabled[ attribute, like this:

   1: [AjaxEnabled]
   2: public partial class Test : System.Web.UI.UserControl
   3: {
   4:     ...

Conclusion

With the HttpHandler, and the attribute in place, my two concerns (url and security) are dealt with.

You are now able to call a user control directly:

/usercontrols/myusercontrol.ascx

And you are only able to call user controls that have the AjaxEnabled attribute.

Any comments and suggestions are welcome btw :)

Improve jQuery – 25 tips

Posted in Development. Tags: , , . No Comments »

jQuery Ajax uploader plugin (with progress bar!)

Original post here:

http://blog.codeville.net/2008/11/24/jquery-ajax-uploader-plugin-with-progress-bar/

——————————————

Do your web applications ever involve letting the user upload a file? If so, how’s the end-user experience: do you show a nice progress bar during the upload, or do you just leave the user waiting for minutes, with no clue when (if ever) the upload will complete?

Please show a progress bar, otherwise users will be justified in hating you. Check out this video to see one way it can work:


If you’re not seeing a video here, your feed reader is hiding it. View this post in a browser to see the video.

Those of you who attended my ASP.NET MVC talk at DDD7 last weekend might recognise this ;)

To create this behaviour, I implemented a simple jQuery plugin that replaces normal <input type=”file”/> elements with funky Ajaxy asynchronous uploader widgets. Behind the scenes, it uses the excellent SWFUpload library. All the clever stuff is in SWFUpload; all I did is set up the progress bar / cancellation behaviours, and make it easier to use if you’re already using jQuery.

Notice that it still works if the user doesn’t have JavaScript running in their browser. It gracefully degrades to “traditional” <input type=”file”/> behaviour. This is known as progressive enhancement or unobtrusive JavaScript.

Download

Here are all the files you need to accomplish this: Download jQuery-asyncUpload-0.1.js.

Setup instructions

Uploading files via Ajax, by nature, involves setting things up both on the server and on the client. The most reliable way to get this working successfully in your own app is to download the demo ASP.NET MVC project (see the end of this post) and copy the relevant aspects of its workings into your own app.

Nonetheless, here is an outline of the steps needed to get jQuery-asyncUpload-0.1.js working in your app, assuming you’ve already got jQuery in there:

1.  Add jQuery-asyncUpload-0.1.js, swfupload.js, and swfupload.swf to your project. In an ASP.NET MVC app, you might like to put these in /Scripts.

2.  Add script tags to reference the JavaScript files.

<head>
    <!-- Adjust the file paths as needed for your project -->
    <script src="/Scripts/jquery-1.2.6.min.js"></script>
    <script src="/Scripts/swfupload.js"></script>
    <script src="/Scripts/jquery-asyncUpload-0.1.js"></script>
</head>

3.  Add an old-style HTML file upload control to one of your pages:

<input type="file" id="yourID" name="yourID" />

4. Add a jQuery statement that replaces this file upload control with an asynchronous uploader when JavaScript is available:

<script>
$(function() {
        $("#yourID").makeAsyncUploader({
            upload_url: "/Home/AsyncUpload",
flash_url: '/Scripts/swfupload.swf',
button_image_url: '/Scripts/blankButton.png'
        });
});
</script>

These options are explained later in this blog post. You must make sure to correctly reference the location of swfupload.swf, and put a button image wherever button_image_url specifies.

5. Add some CSS rules to style the progress bar. I’m using the following, though bear in mind it has some nasty hacks to make IE do an inline float properly. CSS gurus might structure this more cleanly.

DIV.ProgressBar { width: 100px; padding: 0; border: 1px solid black; margin-right: 1em; height:.75em; margin-left:1em; display:-moz-inline-stack; display:inline-block; zoom:1; *display:inline; }
DIV.ProgressBar DIV { background-color: Green; font-size: 1pt; height:100%; float:left; }
SPAN.asyncUploader OBJECT { position: relative; top: 5px; left: 10px; }
5. At this point, check you have something working. The visitor should now be able to select a file to upload, and should immediately get an alert box saying “Error 404” – that’s because you’ve configured the control to do an asynchronous upload to /Home/AsyncUpload, but your web app probably doesn’t have anything at that URL.

Also, if you use FireBug to inspect the DOM, you’ll see that your <input type=”file” /> has been dynamically replaced with the following:

<span class="asyncUploader">
   <div class="ProgressBar" style="display: none;">
<!-- This is the progress bar itself - you can style it with CSS -->
   </div>
   <object type="application/x-shockwave-flash" ... >
       ... SWF config here ...
</object>
   <input type="hidden" name="yourID_filename"/>
   <input type="hidden" name="yourID_guid"/>
</span>

Those two hidden inputs let you keep track of any file that was asynchronously uploaded.

6. Work on your web app so that it *does* handle file uploads to /Home/AsyncUpload (or whatever URL you’ve configured in step 4). The handler should save the uploaded file to disk, then return a unique token, such as a GUID or filename, to will identify the file you just uploaded. See the demo project for a simple way to do this using ASP.NET MVC.

7. When the containing form is finally submitted, check whether a file was sent with the request. This will happen if the user doesn’t have JavaScript enabled, as they’ll revert to traditional uploading behaviour. Also check for the hidden inputs called yourID_guid and yourID_filename – these will be populated if the visitor *does* have JavaScript enabled, and reflect any file that was uploaded asynchronously.

Further configuration

The asynchronous uploader plugin has plenty of properties you can configure in step 4 above:

Property Meaning Example
flash_url Location of swfupload.swf “/Scripts/swfupload.swf”
upload_url URL to which files will be asynchronously uploaded “/Home/AsyncUpload”
file_size_limit Files above this size will be rejected before uploading even begins “3 MB”
file_types “Select files” popup will only show files of this type “*.jpg, *.gif”
file_types_description “Select files” popup will use this caption to describe the selectable file types “All images”
button_image_url Location of an image to be used for the “Choose file” button “blankButton.png”
button_image_width, button_image_height Dimensions of “Choose file” button 109
button_text Text that appears on the “Choose file” button "<font face=’Arial’ size=’13pt’>Choose file</font>"
disableDuringUpload Elements matching this jQuery selector will be disabled while an upload is in progress (useful to prevent form submission during async upload). “INPUT[type=’submit’]”
existingFilename Prepopulates the control with the name of a file already uploaded (useful when retaining state across multiple posts) “somefile.zip”
existingGuid Prepopulates the control with the arbitrary unique token you’ve given to a file already uploaded (useful when retaining state across multiple posts) “ec42555e-bfe7-45b0-87bf-36b1299f0398”
existingFileSize Prepopulates the control with the size, in bytes, of a file already uploaded (useful when retaining state across multiple posts) 548293
debug Turns on SWFUpload’s debugging console true
Posted in Development. Tags: , , . No Comments »

How Do I: Debug ASP.NET AJAX Applications Using Visual Studio 2005?

Original post here: http://www.asp.net/learn/ajax-videos/video-167.aspx

In this video we learn how to use Visual Studio 2005 to debug ASP.NET AJAX applications. We are shown how the ScriptManager is used to access the JavaScript created by the ASP.NET AJAX server controls, and we also see how to use the Sys.Debug class to insert a trace message and a breakpoint.

View video: WinVideo-ASP-DebugAJAXApplicationsUsingVisualStudio2005.wmv (16.89 mb)

Play Video

How Do I: Build a Custom ASP.NET AJAX Server Control?

Original post here:

http://www.asp.net/learn/ajax-videos/video-170.aspx

Learn how to create a custom Web server control with ASP.NET AJAX functionality using the AJAX extensions in the Microsoft AJAX Library. This video walks you through both the server-side .NET code and the client-side JavaScript code, and provides an explanation of the ‘prototype’ concept. The custom ASP.NET AJAX control can then be used in an .aspx page like any other server control.

View video: WinVideo-ASP-AJAXCustomAJAXControl.wmv (12.85 mb)

Play Video

Posted in Development. Tags: , , . No Comments »

How Do I: Build Custom Server Controls that Work With or Without ASP.NET AJAX?

Play Video

Do you have an idea for a custom server control that should be able to work without ASP.NET AJAX, yet could take advantage of ASP.NET AJAX if it is available? In this video we learn how to build a custom server control that does not reference the ASP.NET AJAX assembly, but uses Reflection to “play nice” with the ASP.NET AJAX UpdatePanel.

Presented by Chris Pels

Duration: 19 minutes, 21 seconds

Date: 9 July 2007

How Do I: Create an ASP.NET AJAX Extender from Scratch?

— 
Play Video

Learn how to create an ASP.NET AJAX extender for a standard ASP.NET server control. We are shown how to add server-side properties and client-side JavaScript to extend the behavior of the standard TextBox control, though the same approach can be applied to any other server control.

For further help in creating an ASP.NET AJAX extender, please refer to the Creating a New Extender walkthrough.

Presented by Chris Pels

Duration: 15 minutes, 11 seconds

Date: 20 August 2007

Posted in Development. Tags: , . No Comments »