Adicionar arquivos CSS ou JavaScript ao header do layout a partir de visualizações ou visualizações parciais

Cabeça de páginas de layout:

   

Uma View (AnotherView) das necessidades do aplicativo:

  

e AnotherView tem uma visão parcial (AnotherPartial) que precisa:

  

Pergunta: Como podemos adicionar esses links de arquivos CSS AnotherView e AnotherPartial links para Layout head ?

O RenderSection não é uma boa ideia porque o AnotherPage pode ter mais de um Parcial. Adicionar todo o CSS à cabeça não é útil porque mudará dinâmicamente (depende de Anotherpages).

Layout:

    @ViewBag.Title    @if (IsSectionDefined("AddToHead")) { @RenderSection("AddToHead", required: false) } @RenderSection("AddToHeadAnotherWay", required: false)  

Visão:

 @model ProjectsExt.Models.DirectoryObject @section AddToHead{  } 

Atualização : exemplo básico disponível em https://github.com/speier/mvcassetshelper

Estamos usando a implementação a seguir para adicionar arquivos JS e CSS à página de layout.

Visualizar ou parcial:

 @{ Html.Assets().Styles.Add("/Dashboard/Content/Dashboard.css"); Html.Assets().Scripts.Add("/Dashboard/Scripts/Dashboard.js"); } 

Página de layout:

  @Html.Assets().Styles.Render()   ... @Html.Assets().Scripts.Render()  

Extensão HtmlHelper:

 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 _items; public ItemRegistrar(string format) { _format = format; _items = new List(); } 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 = ""; public const string ScriptFormat = ""; } 

Infelizmente, por padrão, isso não é possível usar a section como outro usuário sugeriu, já que uma section está disponível apenas para o child imediato de uma View .

O que funciona, no entanto, é implementar e redefinir a section em todas as visualizações , significando:

 section Head { @RenderSection("Head", false) } 

Desta forma, cada visão pode implementar uma seção de header, não apenas as crianças imediatas. Isso só funciona parcialmente, especialmente com várias parciais que os problemas começam (como você mencionou na sua pergunta).

Portanto, a única solução real para o seu problema é usar o ViewBag . O melhor provavelmente seria uma coleção separada (lista) para CSS e scripts. Para que isso funcione, você precisa garantir que a List utilizada seja inicializada antes que qualquer uma das visualizações seja executada. Então você pode fazer coisas como esta no topo de cada view / partial (sem se importar se o valor Scripts ou Styles for null:

 ViewBag.Scripts.Add("myscript.js"); ViewBag.Styles.Add("mystyle.css"); 

No layout, você pode percorrer as collections e adicionar os estilos com base nos valores da List .

 @foreach (var script in ViewBag.Scripts) {  } @foreach (var style in ViewBag.Styles) {  } 

Eu acho feio, mas é a única coisa que funciona.

*** ViewBag.Styles.Reverse() .

Desta forma, o estilo mais externo é adicionado primeiro, o que é inline com a forma como as folhas de estilo CSS funcionam de qualquer maneira.

Eu tentei resolver esse problema.

Minha resposta está aqui.

“DynamicHeader” – http://dynamicheader.codeplex.com/ , https://nuget.org/packages/DynamicHeader

Por exemplo, _Layout.cshtml é:

  @Html.DynamicHeader()  ... 

E você pode registrar os arquivos .js e .css em “DynamicHeader” onde desejar.

Por exemplo, o bloco de código em AnotherPartial.cshtm é:

 @{ DynamicHeader.AddSyleSheet("~/Content/themes/base/AnotherPartial.css"); DynamicHeader.AddScript("~/some/myscript.js"); } 

Então, finalmente, a saída HTML é:

     ... 

Eu tive um problema semelhante, e acabei aplicando a excelente resposta de Kalman com o código abaixo (não tão arrumado, mas possivelmente mais expansível):

 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 _styleRefs = new List(); public AssetsHelper AddStyle(string stylesheet) { _styleRefs.Add(stylesheet); return this; } private readonly List _scriptRefs = new List(); 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 Styles { get; private set; } internal List Scripts { get; private set; } internal List RequiredLibraries { get; private set; } public Component() { Styles = new List(); Scripts = new List(); RequiredLibraries = new List(); } 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 _allLibraries = new Dictionary(); private List _usedLibraries = new List(); internal IEnumerable UsedScripts() { SetOrder(); var returnVal = new List(); foreach (var key in _usedLibraries) { returnVal.AddRange(from s in _allLibraries[key].Scripts where IncludesCurrentBrowser(s.ServeTo) select s.Url); } return returnVal; } internal IEnumerable UsedStyles() { SetOrder(); var returnVal = new List(); 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 WalkLibraryTree(List libraryNames) { var returnList = new List(libraryNames); int counter = 0; foreach (string libraryName in libraryNames) { WalkLibraryTree(libraryName, ref returnList, ref counter); } return returnList; } private void WalkLibraryTree(string libraryName, ref List 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 _items; private readonly UrlHelper _urlHelper; public ItemRegistrar(string format, UrlHelper urlHelper) { _format = format; _items = new List(); _urlHelper = urlHelper; } internal void Add(IEnumerable 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 = ""; public const string ScriptFormat = ""; } } 

O projeto contém um método estático AssignAllResources:

 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"); 

na página _layout

 @{ var assets = Html.Assets(); CurrentResources.AssignAllResources(assets); Html.Assets().RenderStyles() }  ... @Html.Assets().RenderScripts()  

e na (s) parcial (es) e vistas

 Html.Assets().Libraries.Uses("DataTables"); Html.Assets().AddScript("~/Scripts/emailGridUtilities.js"); 

Você pode definir a seção pelo método RenderSection no layout.

Layout

   @RenderSection("heads", required: false)  

Então você pode include seus arquivos css na área de seção em sua visão, exceto a visão parcial .

A seção funciona na visualização, mas não funciona na exibição parcial por design .

  @section heads {  } 

Se você realmente quiser usar a área de seção na visão parcial, você pode seguir o artigo para redefinir o método RenderSection.

Razor, layouts nesteds e seções redefinidas – Marcin em ASP.NET

Experimente a solução pronta para uso (ASP.NET MVC 4 ou posterior):

 @{ var bundle = BundleTable.Bundles.GetRegisteredBundles().First(b => b.Path == "~/js"); bundle.Include("~/Scripts/myFile.js"); } 

Para aqueles de nós usando a ASP.NET MVC 4 – isso pode ser útil.

Primeiro, adicionei uma class BundleConfig na pasta App_Start.

Aqui está o meu código que eu usei para criá-lo:

 using System.Web.Optimization; public class BundleConfig { public static void RegisterBundles(BundleCollection bundles) { bundles.Add(new StyleBundle("~/Content/css").Include("~/Content/SiteMaster.css")); } } 

Em segundo lugar, registrei a class BundleConfig no arquivo Global.asax:

 protected void Application_Start() { BundleConfig.RegisterBundles(BundleTable.Bundles); } 

Em terceiro lugar, adicionei ajudantes de estilo ao meu arquivo CSS:

 /* Styles for validation helpers */ .field-validation-error { color: red; font-weight: bold; } .field-validation-valid { display: none; } input.input-validation-error { border: 1px solid #e80c4d; } input[type="checkbox"].input-validation-error { border: 0 none; } .validation-summary-errors { color: #e80c4d; font-weight: bold; font-size: 1.1em; } .validation-summary-valid { display: none; } 

Finalmente eu usei esta syntax em qualquer View:

 @Styles.Render("~/Content/css") 

Aqui está um plugin do NuGet chamado Cassette , que, entre outras coisas, fornece a capacidade de referenciar scripts e estilos em partials.

Embora existam várias configurações disponíveis para este plugin, o que o torna altamente flexível. Aqui está a maneira mais simples de referenciar arquivos de script ou folha de estilo:

 Bundles.Reference("scripts/app"); 

Segundo a documentação :

Chamadas para Reference podem aparecer em qualquer lugar em uma página, layout ou vista parcial.

O argumento do caminho pode ser um dos seguintes:

  • Um caminho de pacote
  • Um caminho de recurso – o pacote inteiro que contém esse recurso é referenciado
  • Um URL

Eu escrevi um wrapper fácil que permite registrar estilos e scripts em cada visão parcial dinamicamente na tag head.

Ele é baseado no DynamicHeader jsakamoto colocado, mas tem algumas melhorias e ajustes de desempenho.

É muito fácil de usar e versátil.

O uso:

 @{ DynamicHeader.AddStyleSheet("/Content/Css/footer.css", ResourceType.Layout); DynamicHeader.AddStyleSheet("/Content/Css/controls.css", ResourceType.Infrastructure); DynamicHeader.AddScript("/Content/Js/Controls.js", ResourceType.Infrastructure); DynamicHeader.AddStyleSheet("/Content/Css/homepage.css"); } 

Você pode encontrar o código completo, explicações e exemplos dentro: Adicionar estilos e scripts dinamicamente para marcar a cabeça