JavaScript Organization for Asp.Net MVC3 with Razor Engine
Introduction
A nice feature of Asp.Net MVC is how Controllers and Views become automatically related through method names, file names and directory structure.
Some Views can have some related javascript code that could be separated in files(js) and I was wondering if there were some pattern to organize these javascripts and make them automatically included to each correspondent View.
After some research I've found this article by Shayne P Boyer but my project is using Asp.Net MVC 3 with Razor View Engine.
Besides, I was concerned about Partial Views that could have their own related javascript file and thinking in a way to make these scripts included once on the page even if the same Partial View is used multiple times on the same Page.
Here I show a ViewPageBase
that automatically reference javascript files for the current View and for the inner Partial Views (using the structure sugested by Shayne and avoiding that the same file is registered more than once).
ViewPageBase
The ViewPageBase
inherits System.Web.Mvc.WebViewPage<TModel>
and overrides InitializePage
method that will search for the related javascript file.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.WebPages;
using System.Text;
using System.IO;
namespace JSOrganization
{
public abstract class ViewPageBase<T> : WebViewPage<T>
{
protected override void InitializePage()
{
base.InitializePage();
AddScriptForView(this.VirtualPath);
}
AddScriptForView
method will check if the javascript file exists for the View and, if so, will keep the reference.
void AddScriptForView(string path)
{
if (string.IsNullOrEmpty(path)) { return; }
var dctScripts = GetScriptsDictionary();
if (!dctScripts.ContainsKey(path))
{
//Change path to the javascripts folder
path = path.Replace("~/Views/", "~/ViewScripts/");
//Change the extension to .js
path = Path.ChangeExtension(path, "js");
//Check if the file exists
if (File.Exists(Server.MapPath(path)))
{
//Build the javascript reference and keep it in the dictionary
var tagBuilder = new TagBuilder("script");
tagBuilder.Attributes.Add("type", "text/javascript");
tagBuilder.Attributes.Add("src", Url.Content(path));
dctScripts[path] = string.Format("{0}{1}", tagBuilder.ToString(TagRenderMode.Normal), System.Environment.NewLine);
}
}
}
GetScriptsDictionary
will search, in the current HttpContext
, the dictionary that keeps the javascript file references. At this point the program identifies the first View processed, wich is usually the View returned by the current Action and will include later all the script references in the final Html.
bool isPrincipalView = false;
const string _viewScriptsKey = "__ViewScripts";
private Dictionary<string, string> GetScriptsDictionary()
{
var ctx = Context;
var dctScripts = default(Dictionary<string, string>);
if (!ctx.Items.Contains(_viewScriptsKey))
{
//If the dictionary is not created yet, then this view is the principal
isPrincipalView = true;
dctScripts = new Dictionary<string, string>();
ctx.Items[_viewScriptsKey] = dctScripts;
}
else
{
dctScripts = ctx.Items[_viewScriptsKey] as Dictionary<string, string>;
}
return dctScripts;
}
Then, ExecutePageHierarchy
is overriden. It's where the "Principal View" inserts all the javascript references at the top of the html.
public override void ExecutePageHierarchy()
{
base.ExecutePageHierarchy();
//This code will execute only for the principal view, not for the layout view, start view, or partial views.
if (isPrincipalView)
{
var dctScripts = GetScriptsDictionary();
var sw = this.Output as StringWriter;
var sb = sw.GetStringBuilder();
//add here the javascript files for layout and viewstart
AddScriptForView(this.Layout);
AddScriptForView("~/Views/_ViewStart.cshtml");
//Insert the scripts
foreach (var pair in dctScripts)
{
sb.Insert(0, pair.Value);
}
}
}
}
public abstract class ViewPageBase : ViewPageBase<dynamic> { }
}
Using the base view class
To make it work it's necessary that all the Views inherits from ViewPageBase
. There are two ways to acomplish this.
One way is inheriting per-View, inserting the following clause at the top of each View:
@inherits JSOrganization.ViewPageBase
Another way is to specify in Views/Web.config
the base class for all the Views:
<system.web.webPages.razor>
<host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<pages pageBaseType="JSOrganization.ViewPageBase">
Project organization
The project structure must be like the following example:
In the example above each View has your associated javascript file in ViewScripts
folder. Remember that even a partial View can have its own javascript that will be referenced just once, even if the View is rendered multiple times in a parent View.
Post Comment
ZLiQyc Looking forward to reading more. Great post.Much thanks again. Really Cool.
A2vd1p Nice blog here! Also your site loads up very fast! What host are you using? Can I get your affiliate link to your host? I wish my web site loaded up as quickly as yours lol
ynliY7 pretty handy material, overall I think this is well worth a bookmark, thanks
fPq648 sleekness as well as classiness. An elegant ladies watch that
KXqWYd Wohh exactly what I was looking for, regards for putting up.
Wow, marvelous blog layout! How long have you been blogging for? you made blogging look easy. The overall look of your web site is fantastic, as well as the content!
fwmVBl Oakley Sunglasses Outlet Oakley Sunglasses Outlet
wuO68p This is one awesome article.Really looking forward to read more. Awesome.
nqu3DC Your style is so unique in comparison to other folks I have read stuff from. Many thanks for posting when you ave got the opportunity, Guess I all just book mark this site.
n3hD0Y
iCnRYq Very good blog post. I certainly appreciate this website. Continue the good work!
e0ojTG F*ckin' awesome things here. I'm very glad to peer your article. Thank you so much and i'm looking ahead to contact you. Will you please drop me a e-mail?
479hVB Heya i'm for the first time here. I found this board and I find It really helpful & it helped me out much. I am hoping to present something again and help others like you helped me.
Tiybf8 Hello there! Would you mind if I share your blog with my myspace group? There's a lot of folks that I think would really appreciate your content. Please let me know. Thank you
rKNNmy Major thanks for the blog article.Much thanks again. Much obliged.
ySSyOa Im obliged for the blog article.Really looking forward to read more. Really Cool.