tags:

views:

2361

answers:

7

I have tried to use ASP.NET MVC for a while, then I face a problem that I don't want to include all of my js and css in master page. But how can I register it in head of master page from my specific view?

A: 

technically you should be putting all your js at the bottom of the page for the best performance.

I think the only way you could do this though would be to include the javascript in the VIewData and have the ViewData displayed on the masterpage (not a great solution).

Kyle West
+7  A: 

The default master page template includes a Content PlaceHolder for the head. If it doesn't you can easily add one:

<head runat="server">
    <title></title>
    <asp:ContentPlaceHolder ID="head" runat="server" />
</head>

Your views can then put anything they want in the head:

<asp:Content ID="Content1" ContentPlaceHolderID="head" runat="server">
    <script src="Scripts/myScripts.js" type="text/javascript"></script>
    <link href="Styles/myStyles.css" rel="stylesheet" type="text/css" />
</asp:Content>
sliderhouserules
+3  A: 

It doesn't look like there's a simple option 'built-in' to the ASP.NET MVC framework just yet. If you are using a user control (.ascx), which you may be if you are creating self-contained controls which also want to manage their own JavaScript requirements, then you can't even use the placeholders to help you out.

In the end I created a helper class and in it there are a couple of methods:

private static SortedList<int, string> GetRegisteredScriptIncludes()
{
    var registeredScriptIncludes = System.Web.HttpContext.Current.Items["RegisteredScriptIncludes"] as SortedList<int, string>;

    if (registeredScriptIncludes == null)
    {
     registeredScriptIncludes = new SortedList<int, string>();
     System.Web.HttpContext.Current.Items["RegisteredScriptIncludes"] = registeredScriptIncludes;
    }

    return registeredScriptIncludes;
}

public static void RegisterScriptInclude(this HtmlHelper htmlhelper, string script)
{
    var registeredScriptIncludes = GetRegisteredScriptIncludes();
    if (!registeredScriptIncludes.ContainsValue(script))
    {
     registeredScriptIncludes.Add(registeredScriptIncludes.Count, script);
    }
}

public static string RenderScripts(this HtmlHelper htmlhelper)
{
    var registeredScriptIncludes = GetRegisteredScriptIncludes();
    var scripts = new StringBuilder();
    foreach (string script in registeredScriptIncludes.Values)
    {
        scripts.AppendLine("<script src='" + script + "' type='text/javascript'></script>");
    }
    return scripts.ToString();
}

That's a basic form of it anyway to try and show the way it works. It could be enhanced in many ways, but at the moment it just filters out duplicate script insert requests for you. Whenever you want to add a new script in the ascx (or aspx for that matter) you can do it this way:

<%
    Html.RegisterScriptInclude(Url.Content("~/Scripts/MapLayers/MapLayer.js"));
    Html.RegisterScriptInclude(Url.Content("~/Scripts/MapLayers/Vehicles.js"));
%>

Then you need to remember to output them once you're done. This is achieved by making the following call at the place in your page where you want to output the script tags:

<%=Html.RenderScripts() %>

Seems to work so far for me. I did half expect to have rendering issues depending at what point RenderScripts was called, especially if not all of the RegisterScriptIncludes had been called yet, but so far it seems to do the job. If you render the scripts last then you should have no problems.

Jason
+2  A: 

@Jason: WARNING, you shouldn't be using static variables like this... In a web context static variables are shared across all users and all page requests. I am amazed that you haven't run into trouble with your code. The principle is fine but the code is wrong and will give you trouble. In this case you should be using System.Web.HttpContext.Current.Items. See http://www.hanselman.com/blog/ATaleOfTwoTechniquesTheThreadStaticAttributeAndSystemWebHttpContextCurrentItems.aspx for more.

vdh_ant
Thanks for the heads-up - you learn something new every day! I've changed the code in line with your comments.
Jason
The static property in question is a reference to HttpContext.Items (which is specific to the request context, per your link, not a static member variable. It's relatively safe, unless multiple threads are created manually that are accessing this static member, in which case a lock on the current context.items would be appropriate (same for the dictionary, or use a ConcurrentDictionary).
Tracker1
+1  A: 

Here is a solution similar to the one Jason gave, but takes vdh_ant's comments into consideration:

http://frugalcoder.us/post/2009/06/29/Handling-Scripts-in-ASPNet-MVC.aspx

Adam Albrecht
A: 

MVC Futures now has built in helpers for this...

1.<head>
2.    <title><asp:ContentPlaceHolder ID="TitleContent" runat="server" /></title>
3.    <%= Html.Css("BlueTheme/site.css") %>
4.    <%= Html.Script("jquery-1.3.2.js") %>
5.</head>

More information here: http://blog.osbornm.com/archive/2009/10/12/mvc-script-css-helpers.aspx

Matthew M. Osborn
A: 

Mathew,

I took a look at your blog on Html.Css and Html.Script helpers. I don't mean to be critical but I do not see any mention in the blog about how the Css and Script helpers would address the problem discussed here. The problem here is one of needing to "registered" script references at any point during the rendering process, and possibly multiple times (in the case of an template or partial view that is used several times and registers its own scripts), and then outputting the aggregate results, sans duplicates, in a single location.

If your solution addresses this, please correct me with some clarification.

--Regards, Ken

Kenneth Baltrinic