Joshua Stengel

Everything...Is going...
To be okay!

August 2008 - Posts

"Just do it!", New Report Tells Government Execs about Web 2.0

In a recent report from the IBM Center for the Business of Government, government executives are strongly urged not to ignore the use of Web 2.0 technologies in their agencies and to re-think the way they serve the public.

The report is targeted at executive and management-level readers in the U.S. government and provides a nice outline of Web 2.0 technologies, how they are being used, and how the government can and should be leveraging them. Information is conveyed in a non-intimidating but firm manner using terminology and concepts common to the government world.

In my opinion, lacking from the report seems to be a good strategy for self-motivating the government to adopt Web 2.0 practices.  The government is not motivated by money in the same manner as businesses and therefore needs a realistic strategy for accepting risk as an acceptable part of innovation.

For much of the government, Evidence-Based Practice (EBP) is substantially the driving force. Unfortunately, acting only on ideas that provide evidence of a sufficient level of effectiveness creates a polarity with innovation. Therefore, what motivation does government have for adopting the always-beta, rapidly changing, and open elements found in Web 2.0?

Government, especially on a national level, typically has measures in place to determine how well it is serving its purpose. Feedback is the profit/loss performance indicator of the government world (at least in the USA). While politics and power-mongering are also forces in the system, as an ordinary citizen, feedback is how we help the government measure its customer service.

The next time you use a government service, don't just accept poor service as being "good enough for government work". If you see improvements in a government service, don't just be surprised and say nothing.  In a democracy, the government must hear from the public before it can respond.  We are not limited to a little booth on election days to talk to the government.  Every government survey, feedback form, comment card, and formal complaint is vote. Don't waste your opportunity to provide the government with the motivation they need to serve the public effectively.

Avoiding Duplication When Dynamically Adding Scripts and Styles to A Webpage

Recently, I had a project where I was wanting to be able to create a reusable UserControl to display tooltips throughout the application. The control would need to access the jQuery framework, a jQuery plugin, required some inline scripting, and had its own class of styles.  Not every page in the application would have tooltips but the pages that did use them would have a lot of them.

If I added all the references to the JavaScript and styles in the Master page, I would be introducing unnecessary bloat for the majority of the application.  So, I needed a way to request these resources in the UserControl so they would only be added to the page "on demand".

As you might image, adding them directly into the UserControl would mean that the resources would be requested for EVERY instance of the UserControl on the page.  If I used the control 10 times, the jQuery framework would get requested 10 times.  Not the most efficient design.

Fortunately, the .NET framework has a nice way of dealing with this by providing a way to dynamically add resources AND check to see if they are already loaded. 

Dynamically Adding JavaScript

If we just dynamically added links to script files, we would still have the same problem as adding them inline. So, here is a method that checks to see if the script has already been loaded and loads it only if needed:

   1: Protected Sub AddScriptFileToPage(ByVal ID As String, ByVal URL As String)
   2:     If Not Page.ClientScript.IsClientScriptIncludeRegistered(ID) Then
   3:         Page.ClientScript.RegisterClientScriptInclude(ID, URL)
   4:     End If
   5: End Sub

The one thing to note is that you need to provide some kind of ID for the script.  This ID is how .NET checks to see if the script has already been added to the page.  Personally, I think that using the filename of the JavaScript helps keep things consistent and kind of makes logical sense but it doesn't really matter as long as it will be unique to that resource.

If you need to add a block of inline JavaScript, no problem.  It's basically the same process with a tiny little tweak:

   1: Public Sub AddScriptBlockToPage(ByVal ID As String, ByVal Script As String)
   2:     If Not Page.ClientScript.IsClientScriptBlockRegistered(ID) Then
   3:         Page.ClientScript.RegisterClientScriptBlock(Page.GetType(), ID, Script, True)
   4:     End If
   5: End Sub

 

Dynamically Adding Stylesheets

Though not as simple as adding scripts, we can do the same thing for our styles.  There are probably a number of way to do this but I chose to check the page header for an existing instance, and add a literal with the link to the stylesheet if it's not found.

   1: Public Sub AddCSSFileToPage(ByVal ID As String, ByVal URL As String)
   2:     If Page.Header.FindControl(ID) Is Nothing Then
   3:         Dim lit As New Literal()
   4:         lit.ID = ID
   5:         lit.Text = "<link rel=""Stylesheet"" type=""text/css"" href=""" & URL & """/>"
   6:         Page.Header.Controls.Add(lit)
   7:     End If
   8: End Sub

 

Making it Practical

You might be wondering why you would go through the trouble of doing all this. Lets make this a bit more useful by creating a class to help us use this functionality efficiently. I created a little ResourceLoader class in my application that looked something like this:

   1: Namespace Sample
   2:     Public Class ResourceLoader
   3:  
   4:         Public Shared Sub AddScriptFileToPage(ByVal ID As String, ByVal URL As String)
   5:             Dim p As Page = CType(System.Web.HttpContext.Current.Handler, Page)
   6:             If Not p.ClientScript.IsClientScriptIncludeRegistered(ID) Then
   7:                 p.ClientScript.RegisterClientScriptInclude(ID, URL)
   8:             End If
   9:         End Sub
  10:  
  11:         Public Shared Sub AddScriptBlockToPage(ByVal ID As String, ByVal Script As String)
  12:             Dim p As Page = CType(System.Web.HttpContext.Current.Handler, Page)
  13:             If Not p.ClientScript.IsClientScriptBlockRegistered(ID) Then
  14:                 p.ClientScript.RegisterClientScriptBlock(p.GetType(), ID, Script, True)
  15:             End If
  16:         End Sub
  17:  
  18:         Public Shared Sub AddCSSFileToPage(ByVal ID As String, ByVal URL As String)
  19:             Dim p As Page = CType(System.Web.HttpContext.Current.Handler, Page)
  20:             Dim lit As New Literal()
  21:             lit.ID = ID
  22:             lit.Text = "<link rel=""Stylesheet"" type=""text/css"" href=""" & URL & """/>"
  23:             If p.Header.FindControl(ID) Is Nothing Then
  24:                 p.Header.Controls.Add(lit)
  25:             End If
  26:         End Sub
  27:  
  28:     End Class
  29: End Namespace

Quick Note: In a class, you can't directly access the Page.  So, note how you can declare a Page and then use it in a class environment.

Now that we have this functionality in our application, we can efficiently request resources without having to worry about duplicating what has already been requested for the page.  For example, going back to my tooltip UserControl, here is how I could safely make sure that my resources are being requested but not duplicated:

   1: Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
   2:    Sample.ResourceLoader.AddScriptFileToPage("jQuery.js", "Script/jQuery.js")
   3:    Sample.ResourceLoader.AddScriptFileToPage("jQuery.ClueTip.js", "Scripts/jQuery.ClueTip.js")
   4:    Sample.ResourceLoader.AddScriptBlockToPage("Tooltip.ascx", "$(document).ready(function() {$('a.Tooltip').cluetip({splitTitle: '|'},{cursor: 'pointer'});});)")
   5:    Sample.ResourceLoader.AddCSSFileToPage("jQuery.ClueTip.css", "CSS/jQuery.ClueTip.css")
   6: End Sub

Notice that every instance of the control will make sure that the jQuery framework, the plugin, the inline script, and the custom styles are loaded but, thanks to our handy-dandy ResourceLoader class, no duplication will take place.

 

Final Thoughts

Even though we aren't duplicating resource requests, we are adding more objects to the page and this potentially introduces latency issues.  It also introduces the opportunity to look at how we might extend this technique to combine everything before page makes the request.  I've been reading a few ideas on how this might done and, if I end up with a good solution, I'll write a follow up.

If you have any ideas, comments, criticisms, or suggestions please share.

Posted: Aug 01 2008, 05:23 PM by jstengel | with 1 comment(s)
Filed under: , ,
More Posts