JavaScript functions can be bound to elements on the partial view; the partial view is rendered at the same time as the parent view. This happens when loading the partial view with a @Html.
Yes, even in Partial View also we can set Layout property which turns Partial View as normal View with Layout .
You should use partial views in two primary cases: When you need to reuse a similar "group of components" in multiple locations in a website (e.g. a "login form" might be used in different places in the website).
Layout:
<html>
<head>
<meta charset="utf-8" />
<title>@ViewBag.Title</title>
<link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />
<script src="@Url.Content("~/Scripts/jquery-1.6.2.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/modernizr-2.0.6-development-only.js")" type="text/javascript"></script>
@if (IsSectionDefined("AddToHead"))
{
@RenderSection("AddToHead", required: false)
}
@RenderSection("AddToHeadAnotherWay", required: false)
</head>
View:
@model ProjectsExt.Models.DirectoryObject
@section AddToHead{
<link href="@Url.Content("~/Content/Upload.css")" rel="stylesheet" type="text/css" />
}
Update: basic example available at https://github.com/speier/mvcassetshelper
We are using the following implementation to add JS and CSS files into the layout page.
View or PartialView:
@{
Html.Assets().Styles.Add("/Dashboard/Content/Dashboard.css");
Html.Assets().Scripts.Add("/Dashboard/Scripts/Dashboard.js");
}
Layout page:
<head>
@Html.Assets().Styles.Render()
</head>
<body>
...
@Html.Assets().Scripts.Render()
</body>
HtmlHelper extension:
public static class HtmlHelperExtensions
{
public static AssetsHelper Assets(this HtmlHelper htmlHelper)
{
return AssetsHelper.GetInstance(htmlHelper);
}
}
public class AssetsHelper
{
public static AssetsHelper GetInstance(HtmlHelper htmlHelper)
{
var instanceKey = "AssetsHelperInstance";
var context = htmlHelper.ViewContext.HttpContext;
if (context == null) return null;
var assetsHelper = (AssetsHelper)context.Items[instanceKey];
if (assetsHelper == null)
context.Items.Add(instanceKey, assetsHelper = new AssetsHelper());
return assetsHelper;
}
public ItemRegistrar Styles { get; private set; }
public ItemRegistrar Scripts { get; private set; }
public AssetsHelper()
{
Styles = new ItemRegistrar(ItemRegistrarFormatters.StyleFormat);
Scripts = new ItemRegistrar(ItemRegistrarFormatters.ScriptFormat);
}
}
public class ItemRegistrar
{
private readonly string _format;
private readonly IList<string> _items;
public ItemRegistrar(string format)
{
_format = format;
_items = new List<string>();
}
public ItemRegistrar Add(string url)
{
if (!_items.Contains(url))
_items.Add(url);
return this;
}
public IHtmlString Render()
{
var sb = new StringBuilder();
foreach (var item in _items)
{
var fmt = string.Format(_format, item);
sb.AppendLine(fmt);
}
return new HtmlString(sb.ToString());
}
}
public class ItemRegistrarFormatters
{
public const string StyleFormat = "<link href=\"{0}\" rel=\"stylesheet\" type=\"text/css\" />";
public const string ScriptFormat = "<script src=\"{0}\" type=\"text/javascript\"></script>";
}
You can define the section by RenderSection method in layout.
Layout
<head>
<link href="@Url.Content("~/Content/themes/base/Site.css")"
rel="stylesheet" type="text/css" />
@RenderSection("heads", required: false)
</head>
Then you can include your css files in section area in your view except partial view.
The section work in view, but not work in partial view by design.
<!--your code -->
@section heads
{
<link href="@Url.Content("~/Content/themes/base/AnotherPage.css")"
rel="stylesheet" type="text/css" />
}
If you really want to using section area in partial view, you can follow the article to redefine RenderSection method.
Razor, Nested Layouts and Redefined Sections – Marcin On ASP.NET
Sadly, this is not possible by default to use section
as another user suggested, since a section
is only available to the immediate child
of a View
.
What works however is implementing and redefining the section
in every view, meaning:
section Head
{
@RenderSection("Head", false)
}
This way every view can implement a head section, not just the immediate children. This only works partly though, especially with multiple partials the troubles begin (as you have mentioned in your question).
So the only real solution to your problem is using the ViewBag
. The best would probably be a seperate collection (list) for CSS and scripts. For this to work, you need to ensure that the List
used is initialized before any of the views are executed. Then you can can do things like this in the top of every view/partial (without caring if the Scripts
or Styles
value is null:
ViewBag.Scripts.Add("myscript.js");
ViewBag.Styles.Add("mystyle.css");
In the layout you can then loop through the collections and add the styles based on the values in the List
.
@foreach (var script in ViewBag.Scripts)
{
<script type="text/javascript" src="@script"></script>
}
@foreach (var style in ViewBag.Styles)
{
<link href="@style" rel="stylesheet" type="text/css" />
}
I think it's ugly, but it's the only thing that works.
******UPDATE****
Since it starts executing the inner views first and working its way out to the layout and CSS styles are cascading, it would probably make sense to reverse the style list via ViewBag.Styles.Reverse()
.
This way the most outer style is added first, which is inline with how CSS style sheets work anyway.
I tried to solve this issue.
My answer is here.
"DynamicHeader" - http://dynamicheader.codeplex.com/, https://nuget.org/packages/DynamicHeader
For example, _Layout.cshtml is:
<head>
@Html.DynamicHeader()
</head>
...
And, you can register .js and .css files to "DynamicHeader" anywhere you want.
For example, the code block in AnotherPartial.cshtml is:
@{
DynamicHeader.AddSyleSheet("~/Content/themes/base/AnotherPartial.css");
DynamicHeader.AddScript("~/some/myscript.js");
}
Result HTML output for this sample is:
<html>
<link href="/myapp/Content/themes/base/AnotherPartial.css" .../>
<script src="/myapp/some/myscript.js" ...></script>
</html>
...
I had a similar problem, and ended up applying Kalman's excellent answer with the code below (not quite as neat, but arguably more expansible):
namespace MvcHtmlHelpers
{
//http://stackoverflow.com/questions/5110028/add-css-or-js-files-to-layout-head-from-views-or-partial-views#5148224
public static partial class HtmlExtensions
{
public static AssetsHelper Assets(this HtmlHelper htmlHelper)
{
return AssetsHelper.GetInstance(htmlHelper);
}
}
public enum BrowserType { Ie6=1,Ie7=2,Ie8=4,IeLegacy=7,W3cCompliant=8,All=15}
public class AssetsHelper
{
public static AssetsHelper GetInstance(HtmlHelper htmlHelper)
{
var instanceKey = "AssetsHelperInstance";
var context = htmlHelper.ViewContext.HttpContext;
if (context == null) {return null;}
var assetsHelper = (AssetsHelper)context.Items[instanceKey];
if (assetsHelper == null){context.Items.Add(instanceKey, assetsHelper = new AssetsHelper(htmlHelper));}
return assetsHelper;
}
private readonly List<string> _styleRefs = new List<string>();
public AssetsHelper AddStyle(string stylesheet)
{
_styleRefs.Add(stylesheet);
return this;
}
private readonly List<string> _scriptRefs = new List<string>();
public AssetsHelper AddScript(string scriptfile)
{
_scriptRefs.Add(scriptfile);
return this;
}
public IHtmlString RenderStyles()
{
ItemRegistrar styles = new ItemRegistrar(ItemRegistrarFormatters.StyleFormat,_urlHelper);
styles.Add(Libraries.UsedStyles());
styles.Add(_styleRefs);
return styles.Render();
}
public IHtmlString RenderScripts()
{
ItemRegistrar scripts = new ItemRegistrar(ItemRegistrarFormatters.ScriptFormat, _urlHelper);
scripts.Add(Libraries.UsedScripts());
scripts.Add(_scriptRefs);
return scripts.Render();
}
public LibraryRegistrar Libraries { get; private set; }
private UrlHelper _urlHelper;
public AssetsHelper(HtmlHelper htmlHelper)
{
_urlHelper = new UrlHelper(htmlHelper.ViewContext.RequestContext);
Libraries = new LibraryRegistrar();
}
}
public class LibraryRegistrar
{
public class Component
{
internal class HtmlReference
{
internal string Url { get; set; }
internal BrowserType ServeTo { get; set; }
}
internal List<HtmlReference> Styles { get; private set; }
internal List<HtmlReference> Scripts { get; private set; }
internal List<string> RequiredLibraries { get; private set; }
public Component()
{
Styles = new List<HtmlReference>();
Scripts = new List<HtmlReference>();
RequiredLibraries = new List<string>();
}
public Component Requires(params string[] libraryNames)
{
foreach (var lib in libraryNames)
{
if (!RequiredLibraries.Contains(lib))
{ RequiredLibraries.Add(lib); }
}
return this;
}
public Component AddStyle(string url, BrowserType serveTo = BrowserType.All)
{
Styles.Add(new HtmlReference { Url = url, ServeTo=serveTo });
return this;
}
public Component AddScript(string url, BrowserType serveTo = BrowserType.All)
{
Scripts.Add(new HtmlReference { Url = url, ServeTo = serveTo });
return this;
}
}
private readonly Dictionary<string, Component> _allLibraries = new Dictionary<string, Component>();
private List<string> _usedLibraries = new List<string>();
internal IEnumerable<string> UsedScripts()
{
SetOrder();
var returnVal = new List<string>();
foreach (var key in _usedLibraries)
{
returnVal.AddRange(from s in _allLibraries[key].Scripts
where IncludesCurrentBrowser(s.ServeTo)
select s.Url);
}
return returnVal;
}
internal IEnumerable<string> UsedStyles()
{
SetOrder();
var returnVal = new List<string>();
foreach (var key in _usedLibraries)
{
returnVal.AddRange(from s in _allLibraries[key].Styles
where IncludesCurrentBrowser(s.ServeTo)
select s.Url);
}
return returnVal;
}
public void Uses(params string[] libraryNames)
{
foreach (var name in libraryNames)
{
if (!_usedLibraries.Contains(name)){_usedLibraries.Add(name);}
}
}
public bool IsUsing(string libraryName)
{
SetOrder();
return _usedLibraries.Contains(libraryName);
}
private List<string> WalkLibraryTree(List<string> libraryNames)
{
var returnList = new List<string>(libraryNames);
int counter = 0;
foreach (string libraryName in libraryNames)
{
WalkLibraryTree(libraryName, ref returnList, ref counter);
}
return returnList;
}
private void WalkLibraryTree(string libraryName, ref List<string> libBuild, ref int counter)
{
if (counter++ > 1000) { throw new System.Exception("Dependancy library appears to be in infinate loop - please check for circular reference"); }
Component library;
if (!_allLibraries.TryGetValue(libraryName, out library))
{ throw new KeyNotFoundException("Cannot find a definition for the required style/script library named: " + libraryName); }
foreach (var childLibraryName in library.RequiredLibraries)
{
int childIndex = libBuild.IndexOf(childLibraryName);
if (childIndex!=-1)
{
//child already exists, so move parent to position before child if it isn't before already
int parentIndex = libBuild.LastIndexOf(libraryName);
if (parentIndex>childIndex)
{
libBuild.RemoveAt(parentIndex);
libBuild.Insert(childIndex, libraryName);
}
}
else
{
libBuild.Add(childLibraryName);
WalkLibraryTree(childLibraryName, ref libBuild, ref counter);
}
}
return;
}
private bool _dependenciesExpanded;
private void SetOrder()
{
if (_dependenciesExpanded){return;}
_usedLibraries = WalkLibraryTree(_usedLibraries);
_usedLibraries.Reverse();
_dependenciesExpanded = true;
}
public Component this[string index]
{
get
{
if (_allLibraries.ContainsKey(index))
{ return _allLibraries[index]; }
var newComponent = new Component();
_allLibraries.Add(index, newComponent);
return newComponent;
}
}
private BrowserType _requestingBrowser;
private BrowserType RequestingBrowser
{
get
{
if (_requestingBrowser == 0)
{
var browser = HttpContext.Current.Request.Browser.Type;
if (browser.Length > 2 && browser.Substring(0, 2) == "IE")
{
switch (browser[2])
{
case '6':
_requestingBrowser = BrowserType.Ie6;
break;
case '7':
_requestingBrowser = BrowserType.Ie7;
break;
case '8':
_requestingBrowser = BrowserType.Ie8;
break;
default:
_requestingBrowser = BrowserType.W3cCompliant;
break;
}
}
else
{
_requestingBrowser = BrowserType.W3cCompliant;
}
}
return _requestingBrowser;
}
}
private bool IncludesCurrentBrowser(BrowserType browserType)
{
if (browserType == BrowserType.All) { return true; }
return (browserType & RequestingBrowser) != 0;
}
}
public class ItemRegistrar
{
private readonly string _format;
private readonly List<string> _items;
private readonly UrlHelper _urlHelper;
public ItemRegistrar(string format, UrlHelper urlHelper)
{
_format = format;
_items = new List<string>();
_urlHelper = urlHelper;
}
internal void Add(IEnumerable<string> urls)
{
foreach (string url in urls)
{
Add(url);
}
}
public ItemRegistrar Add(string url)
{
url = _urlHelper.Content(url);
if (!_items.Contains(url))
{ _items.Add( url); }
return this;
}
public IHtmlString Render()
{
var sb = new StringBuilder();
foreach (var item in _items)
{
var fmt = string.Format(_format, item);
sb.AppendLine(fmt);
}
return new HtmlString(sb.ToString());
}
}
public class ItemRegistrarFormatters
{
public const string StyleFormat = "<link href=\"{0}\" rel=\"stylesheet\" type=\"text/css\" />";
public const string ScriptFormat = "<script src=\"{0}\" type=\"text/javascript\"></script>";
}
}
The project contains a static AssignAllResources method:
assets.Libraries["jQuery"]
.AddScript("~/Scripts/jquery-1.10.0.min.js", BrowserType.IeLegacy)
.AddScript("~/Scripts//jquery-2.0.1.min.js",BrowserType.W3cCompliant);
/* NOT HOSTED YET - CHECK SOON
.AddScript("//ajax.googleapis.com/ajax/libs/jquery/2.0.1/jquery.min.js",BrowserType.W3cCompliant);
*/
assets.Libraries["jQueryUI"].Requires("jQuery")
.AddScript("//ajax.googleapis.com/ajax/libs/jqueryui/1.9.2/jquery-ui.min.js",BrowserType.Ie6)
.AddStyle("//ajax.aspnetcdn.com/ajax/jquery.ui/1.9.2/themes/eggplant/jquery-ui.css",BrowserType.Ie6)
.AddScript("//ajax.googleapis.com/ajax/libs/jqueryui/1.10.3/jquery-ui.min.js", ~BrowserType.Ie6)
.AddStyle("//ajax.aspnetcdn.com/ajax/jquery.ui/1.10.3/themes/eggplant/jquery-ui.css", ~BrowserType.Ie6);
assets.Libraries["TimePicker"].Requires("jQueryUI")
.AddScript("~/Scripts/jquery-ui-sliderAccess.min.js")
.AddScript("~/Scripts/jquery-ui-timepicker-addon-1.3.min.js")
.AddStyle("~/Content/jQueryUI/jquery-ui-timepicker-addon.css");
assets.Libraries["Validation"].Requires("jQuery")
.AddScript("//ajax.aspnetcdn.com/ajax/jquery.validate/1.11.1/jquery.validate.min.js")
.AddScript("~/Scripts/jquery.validate.unobtrusive.min.js")
.AddScript("~/Scripts/mvcfoolproof.unobtrusive.min.js")
.AddScript("~/Scripts/CustomClientValidation-1.0.0.min.js");
assets.Libraries["MyUtilityScripts"].Requires("jQuery")
.AddScript("~/Scripts/GeneralOnLoad-1.0.0.min.js");
assets.Libraries["FormTools"].Requires("Validation", "MyUtilityScripts");
assets.Libraries["AjaxFormTools"].Requires("FormTools", "jQueryUI")
.AddScript("~/Scripts/jquery.unobtrusive-ajax.min.js");
assets.Libraries["DataTables"].Requires("MyUtilityScripts")
.AddScript("//ajax.aspnetcdn.com/ajax/jquery.dataTables/1.9.4/jquery.dataTables.min.js")
.AddStyle("//ajax.aspnetcdn.com/ajax/jquery.dataTables/1.9.4/css/jquery.dataTables.css")
.AddStyle("//ajax.aspnetcdn.com/ajax/jquery.dataTables/1.9.4/css/jquery.dataTables_themeroller.css");
assets.Libraries["MvcDataTables"].Requires("DataTables", "jQueryUI")
.AddScript("~/Scripts/jquery.dataTables.columnFilter.min.js");
assets.Libraries["DummyData"].Requires("MyUtilityScripts")
.AddScript("~/Scripts/DummyData.js")
.AddStyle("~/Content/DummyData.css");
in the _layout page
@{
var assets = Html.Assets();
CurrentResources.AssignAllResources(assets);
Html.Assets().RenderStyles()
}
</head>
...
@Html.Assets().RenderScripts()
</body>
and in the partial(s) and views
Html.Assets().Libraries.Uses("DataTables");
Html.Assets().AddScript("~/Scripts/emailGridUtilities.js");
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With