# 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
Tuesday, February 03, 2009 4:34:29 PM (Mountain Standard Time, UTC-07:00)
Great idea! This should be built into 2010 IMO.
Travis
Friday, February 06, 2009 10:51:28 AM (Mountain Standard Time, UTC-07:00)
Thanks, it works well enough for me and the projects I have been on, and that is the only metric I have to go on as to the usefulness of this approach. If other people can use it to their advantage, then that is a nice side effect. I don't blog often enough to keep any semblance of decent traffic going and really don't expect many people to see this though, so that is a bit of a drag.

Hope you are doing well and staying employed, I haven't seen you in ages...
Tuesday, March 31, 2009 12:56:05 PM (Mountain Standard Time, UTC-07:00)
I tried the vs 9.0 reg script on but it wont work since I upgraded to vista 64bit. Any ideas? It worked great with vs 9.0 xp 32bit.
Ryan
Tuesday, March 31, 2009 2:05:20 PM (Mountain Standard Time, UTC-07:00)
Never mind the last post. I was editing the wrong key. It was the Wow6432Node that needed to be modded. Also, I nested the files that I nested way back from the project without the need to readd them, Thanks again!

I used this post to help a little also... http://www.yyzrichard.com/2008/04/visual-studio-tips-and-tricks.html
Ryan
Saturday, April 04, 2009 12:33:15 AM (Mountain Standard Time, UTC-07:00)
Glad you were able to figure it out, I hadn't yet had a chance to look into this. Hope this was useful to you...
Thursday, April 30, 2009 2:47:28 PM (Mountain Daylight Time, UTC-06:00)
Pretty nifty idea!
All comments require the approval of the site owner before being displayed.
Name
E-mail
(will show your gravatar icon)
Home page

Comment (Some html is allowed: a@href@title, b, blockquote@cite, i, strike, u) where the @ means "attribute." For example, you can use <a href="" title=""> or <blockquote cite="Scott">.  

Live Comment Preview