Generally, as a practice I tend to use as few script and CSS files as possible. Occasionally, a page may have specific JavaScript or vastly different styles that would be unreasonable to include in a main bundle. I have created extension methods that register page specific script and style bundles.
How it Works
During application start-up, script and style files located in the asset folders are validated and added into a bundle:
/// <summary> /// Registers script bundles for views. /// </summary> /// <param name="bundle">The BundleCollection to add script bundles to.</param> public static void RegisterViewScriptBundles(this BundleCollection bundle) { bundle.RegisterViewBundles(".js"); } /// <summary> /// Registers style bundles for views. /// </summary> /// <param name="bundle">The BundleCollection to add style bundles to.</param> public static void RegisterViewStyleBundles(this BundleCollection bundle) { bundle.RegisterViewBundles(".css"); } /// <summary> /// Registers script or style bundles for views. /// </summary> /// <param name="bundle">The BundleCollection to add script or style bundles to.</param> /// <param name="extension">The file extension to use to add files.</param> /// <exception cref="ArgumentException"></exception> private static void RegisterViewBundles(this BundleCollection bundle, string extension) { //defines a path to the assets folder: const string virtualPath = "~/Content/Assets"; bool java; switch (extension.ToLower()) { case ".js": //JavaScript file java = true; break; case ".css": //Cascading Style Sheet file java = false; break; default: throw new ArgumentException("Invalid extension specified: '" + extension + "'."); } string path = HostingEnvironment.MapPath(virtualPath); //get file path of asset folder if (Directory.Exists(path)) //if asset folder exists { string viewsDirectory = HostingEnvironment.MapPath("~/Views"); //get views directory foreach (string viewPath in Directory.GetDirectories(path)) //get all asset controller file paths { string controllerName = (new DirectoryInfo(viewPath)).Name; //get the controller name string controllerDirectory = Path.Combine(viewsDirectory, controllerName); //get the controller directory containing views if (Directory.Exists(controllerDirectory)) //if controller directory exists { //validate each JavaScript or CSS file looking for a corresponding view foreach (string filePath in Directory.GetFiles(viewPath, "*" + extension)) //get all java or css files for the view { string viewFileName = Path.GetFileName(filePath); //get view file name string viewName = Path.GetFileNameWithoutExtension(viewFileName); //get view name string viewFilePath = Path.Combine(controllerDirectory, viewName); //get view file path if (File.Exists(viewFilePath + ".cshtml") || File.Exists(viewFilePath + ".aspx")) //if view file exists { string actualVirtualPropPath = virtualPath + "/" + controllerName + "/" + viewFileName; //setup virtual path to asset if (java) //if java, register ScriptBundle bundle.Add(new ScriptBundle("~/bundles/assets/" + controllerName + "/" + viewName).Include(actualVirtualPropPath)); else //otherwise register StyleBundle bundle.Add(new StyleBundle("~/Content/Assets/" + controllerName + "/" + viewName).Include(actualVirtualPropPath)); } } } } } }
To render page specific scripts and styles, the following extension methods attempt to locate a bundle from the controller and view name:
/// <summary> /// Renders any view specific script bundle for the current page. /// </summary> /// <param name="page">The page to render scripts for.</param> /// <returns>A HTML string containing the link tag or tags for the bundle.</returns> public static IHtmlString RenderViewScriptBundle(this WebViewPage page) { return page.RenderViewScriptBundle(false); } /// <summary> /// Renders any view specific script bundle for the current page. /// </summary> /// <param name="page">The page to render scripts for.</param> /// <param name="force_bundle">true to force render bundle; otherwise, false.</param> /// <returns>A HTML string containing the link tag or tags for the bundle.</returns> public static IHtmlString RenderViewScriptBundle(this WebViewPage page, bool force_bundle) { //get the view path: string viewPath = ((BuildManagerCompiledView)page.ViewContext.View).ViewPath; //get the controller: string controller = (new DirectoryInfo(Path.GetDirectoryName(viewPath)).Name); //get the view name: string view = Path.GetFileNameWithoutExtension(viewPath); //create the script bundle virtual path: string bundleVirtualPath = "~/bundles/assets/" + controller + "/" + view; //locate any available bundle: string bundle = BundleTable.Bundles.ResolveBundleUrl(bundleVirtualPath, true); if (bundle != null) //if bundle exists return Scripts.Render(force_bundle ? bundle : bundleVirtualPath); return MvcHtmlString.Empty; } /// <summary> /// Renders any view specific style bundle for the current page. /// </summary> /// <param name="page">The page to render styles for.</param> /// <returns>A HTML string containing the link tag or tags for the bundle.</returns> public static IHtmlString RenderViewStyleBundle(this WebViewPage page) { return page.RenderViewStyleBundle(false); } /// <summary> /// Renders any view specific style bundle for the current page. /// </summary> /// <param name="page">The page to render styles for.</param> /// <param name="force_bundle">true to force render bundle; otherwise, false.</param> /// <returns>A HTML string containing the link tag or tags for the bundle.</returns> public static IHtmlString RenderViewStyleBundle(this WebViewPage page, bool force_bundle) { //get the view path: string viewPath = ((BuildManagerCompiledView)page.ViewContext.View).ViewPath; //get the controller: string controller = (new DirectoryInfo(Path.GetDirectoryName(viewPath)).Name); //get the view name: string view = Path.GetFileNameWithoutExtension(viewPath); //create the style bundle virtual path: string bundleVirtualPath = "~/Content/Assets/" + controller + "/" + view; //locate any available bundle: string bundle = BundleTable.Bundles.ResolveBundleUrl(bundleVirtualPath, true); if (bundle != null) //if bundle exists return Styles.Render(force_bundle ? bundle : bundleVirtualPath); return MvcHtmlString.Empty; }
These extension methods handle registering and rendering style and script bundles.
Using
Page specific scripts or styles should be named as their corresponding view in a controller sub-directory in Content/Assets. e.g. "~/Content/Assets/Home/Index.js".
In the BundleConfig.RegisterBundles method, call the register extension methods to add page specific bundles:
public class BundleConfig { public static void RegisterBundles(BundleCollection bundles) { ... //register page scripts: bundles.RegisterViewScriptBundles(); //register page styles: bundles.RegisterViewStyleBundles(); } }
In the layout file for a site, include a call to RenderViewStyleBundle in the head section and RenderViewScriptBundle after other scripts or bundles are rendered as shown below:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width" /> <title>@ViewBag.Title</title> @Styles.Render("~/Content/css") @Scripts.Render("~/bundles/modernizr") @* renders page specific style bundle: *@ @this.RenderViewStyleBundle() </head> <body> @RenderBody() @Scripts.Render("~/bundles/jquery") @RenderSection("scripts", required: false) @* renders page specific script bundle: *@ @this.RenderViewScriptBundle() </body> </html>
Caveats
Registering script or style files for views located in the Shared view folder must be placed in a corresponding Shared folder in assets.
Source Code
Download BundleCollectionExtensions and WebViewPageExtensions Classes