# Tuesday, February 03, 2009

Over the last 4 years, JavaScript has been critical to the majority of the web applications that I have worked on; dealing with the logical organization of said files has always been a less-than-satisfactory experience until about 2 years ago when I began using the following techniques for a easier design-time experience.

Page-Specific JavaScript Files

Let's say I have a page named foo.aspx and I have JavaScript specific to it contained in a file - I name the .js file foo.aspx.js. Then, in a base class for the pages of the application, I have the following code:

   1: protected override void OnLoad(EventArgs e)
   2: {
   3:     base.OnLoad(e);
   4:     AttachPageSpecificJavaScriptFile();
   5: }
   6:  
   7: private void AttachPageSpecificJavaScriptFile()
   8: {
   9:     var appRelativeVirtualPath = string.Format("{0}.js", TemplateControl.AppRelativeVirtualPath);
  10:     RegisterJavaScriptFile(appRelativeVirtualPath);
  11: }
  12:  
  13: protected void RegisterJavaScriptFile(string appRelativeVirtualPath)
  14: {
  15:     if (JavaScriptFileExists(appRelativeVirtualPath))
  16:     {
  17:         string url = ResolveClientUrl(appRelativeVirtualPath);
  18:         ClientScript.RegisterClientScriptInclude(url, url);
  19:     }
  20: }

Essentially, for each page in my application, the app will look for a *.aspx.js file that matches the name of the page (in our example, foo.aspx.js) and place, within the rendered page, a script tag referencing it.

Since every request to a given page would need to check to see whether or not an associated .js file existed, and considering the fact that checking with the file system each time would be somewhat costly, I have the following code take care of checking and then caching the results at the application level:

   1: private Dictionary<string, bool> JavaScriptFileRegistry
   2: {
   3:   get
   4:   {
   5:     var javaScriptFileRegistry = Application["JavaScriptFileRegistry"] as Dictionary<string, bool>;
   6:     if (javaScriptFileRegistry == null)
   7:     {
   8:       javaScriptFileRegistry = new Dictionary<string, bool>();
   9:       Application["JavaScriptFileRegistry"] = javaScriptFileRegistry;
  10:     }
  11:     return javaScriptFileRegistry;
  12:   }
  13: }
  14:  
  15: protected bool JavaScriptFileExists(string appRelativeVirtualPath)
  16: {
  17:   var registry = JavaScriptFileRegistry;
  18:   if (registry.ContainsKey(appRelativeVirtualPath) == false)
  19:   {
  20:     registry.Add(appRelativeVirtualPath, File.Exists(Server.MapPath(appRelativeVirtualPath)));
  21:   }
  22:   return registry[appRelativeVirtualPath];
  23: }

To complete this, I have a registry hack that will cause any *.aspx.js files to collapse underneath the *.aspx page in the solution explorer of Visual Studio (i.e. it will hide underneath the page, just like the *.aspx.cs file does). Depending on the version of Visual Studio you are using, the registry hack is different. Here are a couple that I use with Windows XP (I don't know if they differ for Vista because I don't use Vista) - copy each one into a text file and rename it with a .reg extension, then execute the file.

Visual Studio 2005
   1: Windows Registry Editor Version 5.00 
   2: [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\8.0\Projects\{E24C65DC-7377-472b-9ABA-BC803B73C61A}\RelatedFiles\.aspx\.js] 
   3: @=""
Visual Studio 2008
   1: Windows Registry Editor Version 5.00 
   2: [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\9.0\Projects\{E24C65DC-7377-472b-9ABA-BC803B73C61A}\RelatedFiles\.aspx\.js] 
   3: @=""

You will probably need to reboot your machine before these take effect. Also, the nesting will only take place for newly-added .js files, any that you have which are already named *.aspx.js can be nested by either re-adding them to the project or manually modifying the .csproj file's XML.

Anyway, that is how I do things and it really helps to keep things organized. I also do all of this for user controls (ascx files) - with the above, it should be simple enough to figure out how to do this for the user controls and ascx file extension.

Hope this helps someone!

Kick It on DotNetKicks.com

posted on Tuesday, February 03, 2009 2:21:51 PM (Mountain Standard Time, UTC-07:00)  #    Comments [6]
# Thursday, November 01, 2007

I sure hope so! I noticed this quite some time ago when I first saw the syntax for anonymous types in C#. Being a lover of JavaScript, I quite like the ability to do things in C# in a manner consistent with the syntax I am used to using in JavaScript (my current favorite programming language, especially thanks to MochiKit). The syntax I refer to is partly alluded to in this post by Eilon Lipton (first time I have ever heard of Eilon), where he talks about substituting an anonymous type for a Dictionary<string, string>, using said Dictionary for setting attributes on HTML tags he is creating in C#. Sound familiar? I mentioned something similar in my post yesterday.

To illustrate the syntax I keep referring to, here is what I am used to doing in JavaScript, particularly with MochiKit - let's say I want to create a div element programmatically (and let's spit it out as HTML so we can better get the gist of things):

 toHTML(DIV({id:"myDiv", class:"myCssClass"}, P({ style:"font-weight:bold" }, "Hello World!")));

The HTML rendered looks like:

 <div class="myCssClass" id="myDiv"> <p style="font-weight: bold;">Hello World!</p> </div>

So, rather than declaring a the JavaScript function "DIV" or "P" (which create div and paragraph DOM elements, respectively) with every possible option as a parameter to said function (e.g. id, style, class, etc.), you simply pass in an anonymous object which contains values for the properties you care about setting. This is a fairly typical way of doing things in JavaScript, and despite the protests from the academic portion of my brain as well as other developers, I would love to be able to do the same in C#.

 

Technorati Tags: , , ,
Kick It on DotNetKicks.com

posted on Thursday, November 01, 2007 11:38:15 AM (Mountain Standard Time, UTC-07:00)  #    Comments [0]
# Wednesday, October 31, 2007

Since learning about the SubSonic project last week, I have spent a bit of time reading up on it, watching screencasts about it, playing with it, and reading the blog of its creator, Rob Conery. In his post "How MVC, jQuery, and SubSonic Will Make You Smile," Rob talks about the use of .NET 3.5's extension methods, in particular extending a Dictionary<string, string> to return HTML for a drop down list (HTML select); here is what his version of the extension method looks like:

 

 1: public static string ToHtmlSelect(this Dictionary<string, string> listItems, string name,
object selectedValue, object attributes)
 2: {
 3:  // input formats
 4:  string selectFormat = "<select name='{0}' id='{0}' {1}>\r\n{2}\r\n </select>";
 5:  string optionFormat = "\t<option value='{0}' {1}>{2}</option>\r\n";
 6:  
 7:  // output
 8:  StringBuilder sb = new StringBuilder();
 9:  
 10:  foreach (string s in listItems.Keys)
 11:  {
 12:  string selectedFlag = "";
 13:  string text = listItems[s];
 14:  string value = s;
 15:  if (value.ToLower().Equals(selectedValue.ToString().ToLower()))
 16:  selectedFlag = "selected=true";
 17:  sb.AppendFormat(optionFormat, s.ToString(), selectedFlag, text);
 18:  }
 19:  string result = string.Format(selectFormat, name, attributes.ToAttributeList(), sb.ToString());
 20:  return result;
 21: }



 

He also mentions that (on line 19) there is another extension method that "hangs off the object class" which basically enumerates all of the properties of an object, creating a name/value pair attribute list. He doesn't give the code of that extension method, which he states "is ScottGu magic at work and something he shows in his MVC demos." I have yet to watch those demos myself, but the code is probably something akin to this:

 

 1: public static string ToAttributeList(this object attributes) 
 2: {
 3:  StringBuilder sb = new StringBuilder();
 4:  foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(attributes)) 
 5:  {
 6:  sb.AppendFormat("{0}='{1}' ", property.Name, property.GetValue(attributes).ToString());
 7:  }
 8:  return sb.ToString();
 9: }

 

This allows you to pass anonymous types, specifying what might normally be optional parameters, a la something I have been doing in JavaScript for a while using MochiKit (other JavaScript libs exploit the same literal JavaScript object notation in their function signatures). The idea is that you don't need to have a structured method signature that accepts all possible options - you can pass in what you want to specify as a part of an anonymous object. This will make more sense if you have spent any time doing this in JavaScript or want to wait a moment and read on.

Anyway, the whole reason I was even posting on this subject is because of a comment made on Rob's post by one Joe Chung, wherein he states:

"Seeing StringBuilder-generated HTML makes me sad for the future of ASP.NET. Object-oriented ASP spaghetti code is still spaghetti code."

He is referring to the string hacking done in the ToHtmlSelect extension method. I too don't care for it, and simply wanted to show a nicer way of doing this, which leverages objects we already have access to in ASP.NET which do that grunt work for us.

Here is my version of Rob's method (which I still have issues with, but for now we will let those go for the sake of staying on topic) - note, there are no literal strings for those that despise them:

 1: public static string ToHtmlSelect(this Dictionary<string, string> listItems, object selectedValue,
object attributes)
 2: {
 3:  DropDownList htmlSelect = new DropDownList();
 4:  htmlSelect.Attributes.AddAttributes(attributes);
 5:  foreach (string key in listItems.Keys)
 6:  {
 7:  ListItem item = new ListItem(listItems[key], key);
 8:  item.Selected = (key.ToLower().Equals(selectedValue.ToString().ToLower()));
 9:  htmlSelect.Items.Add(item);
 10:  }
 11:  return htmlSelect.ToHtml();
 12: }

 

Now, my version does not use the ToAttributeList() extension method hanging off of  object - mine uses something a bit different, but an extension method nonetheless (although a bit naive, it will suffice for our purposes):

 

 1: public static void AddAttributes(this System.Web.UI.AttributeCollection attributeCollection,
object attributes)
 2: {
 3:  foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(attributes))
 4:  {
 5:  attributeCollection.Add(property.Name, property.GetValue(attributes).ToString());
 6:  }
 7: }

 

My extension method hangs instead off of System.Web.UI.AttributeCollection because my version of the ToHtmlSelect() method works with the System.Web.UI.DropDownList object, which uses that as the place to store its attributes.

The other key to mine is the following extension method which wraps a technique I have been using for over 2 years to create HTML (I didn't figure it out on my own, but can't remember where I saw this exactly - I think in the Google Group for the AjaxPro.NET component back when it was simply Ajax.NET):

 

 1: public static string ToHtml(this Control control)
 2: {
 3:  StringWriter sw = new StringWriter();
 4:  HtmlTextWriter htw = new HtmlTextWriter(sw);
 5:  control.RenderControl(htw);
 6:  return sw.ToString();
 7: }

 

Note - this can be used for all sorts of Control-derived ASP.NET HTML wrapper objects to get you some HTML without string hacking in your layers of code:

 

 1: Calendar c = new Calendar();
 2: string calendarHtml = c.ToHtml();

 

Notice Joe - no literal strings! That should make you happy (I know it makes me happy!).

Now, I love hacking JavaScript as much as the next guy (been doing it hardcore for the last 2.5 years even with ASP.NET because it is too much fun!), but hacking strings to create HTML is still not that fun. Even in JavaScript I don't do it if I don't have to, preferring to use functions that handle that for me (MochiKit has some simple DOM functions for handling HTML creation).

Anyway, hope this is of some use to someone out there!

 

Technorati Tags: , , , ,
Kick It on DotNetKicks.com

posted on Wednesday, October 31, 2007 12:38:37 PM (Mountain Standard Time, UTC-07:00)  #    Comments [11]
# Friday, October 24, 2003
Brief discussion of the src attributes use
Kick It on DotNetKicks.com

posted on Friday, October 24, 2003 8:17:34 PM (Mountain Daylight Time, UTC-06:00)  #    Comments [0]