views:

412

answers:

5

Problem

When working with MasterPages, a common irritation I run into is that script tags in the master are relative to the consuming page.

So for instance, your JavaScript might work if your consuming page is in the root of your app, but when you put another page in a subfolder, the relative path breaks and the JavaScript is not found. And there isn't a way to use absolute paths that I'm aware of in this case.

This last time, I decided to really attack this and find a good solution.

Proposed Solutions

I tried one strategy that revolved around calling ClientScriptManager.RegisterClientScriptInclude in Page_Load, but that didn't seem to render anything (granted, my understanding of the related plumbing is incomplete).

I tried another one that looked something like this:

<script language="javascript" src='<%= ResolveClientUrl("~/js/ddnmenu.js") %>' type="text/javascript"></script>

...But that throws an exception: The Controls collection cannot be modified because the control contains code blocks.

Working (but somewhat fugly) Code

So, what I ended up going with is a Literal control in the Head where I render the appropriate Html:

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
    Me.SetupLiteralScriptsTag()
End Sub

Private Sub SetupLiteralScriptsTag()
    'Build the script tags to import our JavaScript
    Dim Builder As New StringBuilder

    Builder.AppendLine(String.Format("<script type=""text/javascript"" src=""{0}""""></script>", ResolveClientUrl("~/js/jquery-1.3.2.min.js")))
    Builder.AppendLine(String.Format("<script type=""text/javascript"" src=""{0}""""></script>", ResolveClientUrl("~/js/jquery.corners.min.js")))
    Builder.AppendLine(String.Format("<script type=""text/javascript"" src=""{0}""""></script>", ResolveClientUrl("~/js/bg.pos.js")))
    Builder.AppendLine(String.Format("<script type=""text/javascript"" src=""{0}""""></script>", ResolveClientUrl("~/js/moonstone.js")))

    Me.LiteralScriptTags.Text = Builder.ToString
End Sub

This works, but I'm not on fire about it since it seems like a bit too much of a workaround for what must be an extremely common problem. Is there a better way?

+1  A: 

I use app relative syntax everywhere. It does have the drawback that if you change app name/path, then you have alot of work to do updating all of your URL's.

<script language="javascript" src="/MyAppName/Includes/MyJavascriptFile.js">

or if you were working on the root app, then:

<script language="javascript" src="/Includes/MyJavascriptFile.js">

Paul Prewett
At least the work is minimized in the masterpages scenario. It's amazing that I didn't know you could do it this way... I've been working on web apps for a very long time!
Brian MacKay
A: 

You could reference your script always from the root. IE:

<script language="javascript" src="/scripts/file.js"></script>

Any page in your app would then get the javascript properly.

Also, since you are working with ASP.NET you could make use of the "~" character for providing application based relative paths. See Rick Strahl's article on ASP.NET paths for more info.

nikmd23
The tilde only works inside of server controls. I would love to use it here though.
Brian MacKay
+6  A: 

You could add a scriptmanager to the master page and include the javascript files via that:

<asp:ScriptManager ...>
  <Scripts>
    <asp:ScriptReference Path="~/js/ddnmenu.js" />
  </Scripts>
</asp:ScriptManager>

Another advantage of that approach is that you can then add a ScriptManagerProxy control to your content pages (and user controls) to include any additional scripts.

Some third-party replacements for ASP:ScriptManager (e.g. from telerik's RadControls suite or from the Ajax control toolkit) offer even more features, such as merging all included java script files into one file (thus reducing the number of required HTTP requests to load a page).

Edit: using a ScriptManager has some other advantages e.g. you can send debug or release versions of your scripts to the browser, or culture-dependent scripts, etc. etc. Have a look at this page in MSDN for an overview.

M4N
I like this answer. I wish I could accept both of them.
Brian MacKay
Then simply accept mine ;-) - no seriously, accept whichever answer helped you the most.
M4N
A: 

I use Page.resolveUrl("~/somefile.js");

works like a champ

smercer
A: 

The Controls collection cannot be modified because the control contains code blocks

That error sounds familiar. This page helped when I fumbled my way through this one

Dan F