AJAX Deferred Loader for ASP.NET

by Jon Davis 11. April 2009 14:19

Here’s a simple ASP.NET control that takes a URL parameter, renders a placeholder on the page, and uses Javascript to “lazy-load” the view content for that placeholder area. It basically does what my sprinkle.js did a long while back, but it takes advantage of ASP.NET controls and jQuery simplicity with its load() function.

It also has self-refreshing. This is good for updating a section of a page with progress information, without refreshing the entire page.

To use it,

  1. Compile the source code (below) to your Web Application project’s assembly, or create an assembly and reference the assembly in your project. You should put this control in its own namespace or in a “.Controls” namespace because of #2 below.
  2. Add a header to any page that would use it to explicitly import all controls under the namespace for which you’re using this control.
    <%@ Register Assembly="MvcApplication1" Namespace="MvcApplication1.Controls" TagPrefix="demo" %>
  3. Be sure jQuery is referenced on the page, ideally in the <head> tag. If you’re using Web Forms and <head runat=”server”>, the process of getting it in there is a little complicated but that’s a different topic. I’m using ASP.NET MVC and I just put it in the Master page. Ultimately, jQuery needs to be loaded before anything else loads, that’s your objective, so you figure it out.
    <html><!-- ... -->
    <head>
      <!-- ... -->
      <script language="javascript" type="text/javascript" src="../../Scripts/jquery-1.3.2.min-vsdoc.js"></script>
      <script language="javascript" type="text/javascript" src="<%=ResolveUrl("~/Scripts/jquery-1.3.2.min.js") %>"></script>
      <!-- ... -->
    </head>
    <body><!-- ... -->
    </body>
    </html>
  4. Now you can reference inline. Note the “RefreshInterval” setting, which is an integer that indicates, in seconds, how often to refresh the region. In this sample, it updates every two seconds. Note also that the HTML that comes back from the referenced URL can include script markup that cancels the interval, such as if a process has completed.
     
    <demo:AjaxDeferredView runat="server" ViewUrl="~/Deferred" RefreshInterval="2">
        <asp:Panel runat="server">Please wait ...</asp:Panel>
    </demo:AjaxDeferredView>
  5. This is what gets outputted with the above tag. Note the “undefined, undefined” are there because the PostData and the Callback optional properties are not set on the control tag.
    <div id="ctl00_MainContent_ctl00">
        <div>
    		Please wait ...
        </div>
    </div>
    <script language="javascript" type="text/javascript"><!--
    $(document).ready(function() {
    	window['ctl00_MainContent_ctl00_interval'] = setInterval(function() {
    		$('#ctl00_MainContent_ctl00').load('http://localhost:5577/Deferred', undefined, undefined);
    	}, 2000);
    	$('#ctl00_MainContent_ctl00').load('http://localhost:5577/Deferred');
    });
    --></script>
  6. And finally, what the end user will actually see is a momentary, split-second, or too-fast-to-see placeholder being swapped out for the content at the loaded URL.
  7. Here’s an example of how to clear that interval in the sample ~/Deferred view that comes back, such as if a process has completed, or in this case after the 5th hit (from any visitor). This sample might be the actual page that is invoked from the AJAX call.  Note the ClearParentInterval control, and that the logic that changes its ClearInterval property precedes its position on the page.
    <%@ Page Title="" Language="C#" Inherits="System.Web.Mvc.ViewPage" %>
    <%@ Register Assembly="MvcApplication1" Namespace="MvcApplication1.Controls" TagPrefix="demo" %>
    <% Response.Cache.SetCacheability(HttpCacheability.NoCache); %>
    Welcome to my deferred content. 
    <%
        
        //live counter
        var i = (int)(Context.Application["defercnt"] ?? 0);
        i++;
        Context.Application["defercnt"] = i;
        %><%=i %>
        
    <% if (i >= 5) // shutdown interval after 5 views
     {
         ClearIntervalControl.ClearInterval = true;
     } %>
     <demo:ClearParentInterval runat="server" ID="ClearIntervalControl" />
    

    This outputs the following when ClearInterval is set to true. The point of the example is in the ID’ing of the <span> tag and in the <script> tag’s contents. It basically walks up the DOM tree by one parent to get the placeholder’s ID in the DOM, then tacks on “_interval” and assumes that to be the name of the interval (which it is).
    <div>Welcome to my deferred content. 5</div>
        
    <span id="ClearIntervalControl">
    </span>
    <script type="text/javascript" language="javascript">
    	var ClearIntervalControl_ClearIntervalParentRef = $('#ClearIntervalControl').parent().attr('id') + '_interval';
    	if (window[ClearIntervalControl_ClearIntervalParentRef]) {
    		clearInterval(window[ClearIntervalControl_ClearIntervalParentRef]);
    	}
    </script>
    

Here’s the control source:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;

namespace MvcApplication1.Controls
{
    public class AjaxDeferredView : System.Web.UI.Control
    {
        protected override void OnInit(EventArgs e)
        {
            if (string.IsNullOrEmpty(ContainerTag))
                ContainerTag = "div";
        }

        protected override void Render(HtmlTextWriter writer)
        {
            writer.WriteLine();
            writer.WriteBeginTag(ContainerTag);
            writer.WriteAttribute("id", ClientID);
            writer.Write(">");
            base.Render(writer);
            writer.WriteEndTag(ContainerTag);
            writer.WriteLine();
            writer.WriteLine("<script language=\"javascript\" type=\"text/javascript\"><!--");
            writer.WriteLine("$(document).ready(function() {");
            if (RefreshInterval > 0)
            {
                writer.WriteLine("\twindow['" + this.ClientID + "_interval']"
                    + " = setInterval(function() {");
                writer.WriteLine("\t\t$('#" + this.ClientID + "').load('"
                    + ResolveFullUrl(this.ViewUrl) + "', " 
                    + GetDataArg() + ", " + GetCallbackArg() + ");");
                writer.WriteLine("\t}, " + RefreshInterval * 1000 + ");");
            }
            writer.WriteLine("\t$('#" + this.ClientID + "').load('"
                + ResolveFullUrl(this.ViewUrl) + "');");
            writer.WriteLine("});");
            writer.WriteLine("--></script>");
        }

        [PersistenceMode(PersistenceMode.Attribute)]
        public object PostData { get; set; }

        [PersistenceMode(PersistenceMode.Attribute)]
        public string Callback { get; set; }

        [PersistenceMode(PersistenceMode.Attribute)]
        public string ViewUrl { get; set; }

        [PersistenceMode(PersistenceMode.Attribute)]
        public string ContainerTag { get; set; }

        [PersistenceMode(PersistenceMode.Attribute)]
        public int RefreshInterval { get; set; }

        private string GetDataArg()
        {
            if (PostData == null) return "undefined";
            // todo: convert complex class to JSON
            return PostData.ToString();
        }

        private string GetCallbackArg()
        {
            if (string.IsNullOrEmpty(Callback)) return "undefined";
            return Callback;
        }

        private string ResolveFullUrl(string url)
        {
            var ret = new Uri(Request.Url, Page.ResolveUrl(url));
            return ret.ToString();
        }

        private HttpResponse Response
        {
            get { return HttpContext.Current.Response; }
        }

        private HttpRequest Request
        {
            get { return HttpContext.Current.Request; }
        }
    }

    public class ClearParentInterval : Control
    {
        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);
            if (string.IsNullOrEmpty(ContainerTag))
                ContainerTag = "span";
        }
        protected override void Render(HtmlTextWriter writer)
        {
            writer.WriteBeginTag(ContainerTag);
            writer.WriteAttribute("id", this.ClientID);
            writer.WriteLine(">");
            base.Render(writer);
            writer.WriteEndTag(ContainerTag);
            writer.WriteLine();
            if (ClearInterval.HasValue && ClearInterval.Value)
            {
                writer.WriteLine("<script type=\"text/javascript\" language=\"javascript\">");
                writer.WriteLine("\tvar " + ClientID + "_ClearIntervalParentRef = $('#" + this.ClientID +
                                 "').parent().attr('id') + '_interval';");
                writer.WriteLine("\tif (window[" + ClientID + "_ClearIntervalParentRef]) {");
                writer.WriteLine("\t\tclearInterval(window[" + ClientID + "_ClearIntervalParentRef]);");
                writer.WriteLine("\t}");
                writer.WriteLine("</script>");
            }
        }
        [PersistenceMode(PersistenceMode.Attribute)]
        public string ContainerTag { get; set; }

        public bool? ClearInterval { get; set; }
    }
}

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: ,

Web Development

Comments

Add comment


(Will show your Gravatar icon)  

  Country flag

biuquote
  • Comment
  • Preview
Loading




 

Powered by BlogEngine.NET 1.4.5.0
Theme by Mads Kristensen

About the author

Jon Davis (aka "stimpy77") has been a programmer, developer, and consultant for web and Windows software solutions professionally since 1997, with experience ranging from OS and hardware support to DHTML programming to IIS/ASP web apps to Java network programming to Visual Basic applications to C# desktop apps.
 
Software in all forms is also his sole hobby, whether playing PC games or tinkering with programming them. "I was playing Defender on the Commodore 64," he reminisces, "when I decided at the age of 12 or so that I want to be a computer programmer when I grow up."

Jon was previously employed as a senior .NET developer at a very well-known Internet services company whom you're more likely than not to have directly done business with. However, this blog and all of jondavis.net have no affiliation with, and are not representative of, his former employer in any way.

Contact Me 


Tag cloud

Calendar

<<  December 2020  >>
MoTuWeThFrSaSu
30123456
78910111213
14151617181920
21222324252627
28293031123
45678910

View posts in large calendar