从部分填充剃刀部分

我试图做到这一点的主要动机是获得只有页面底部的部分与Javascript的其余部分,而不是部分被渲染的页面中间所需的JavaScript的主要动机。

下面是我想要做的一个简单的例子:

这是在正文之前的脚本部分的布局。

<!DOCTYPE html> <html> <head> <title>@ViewBag.Title</title> <link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" /> </head> <body> @RenderBody() <script src="@Url.Content("~/Scripts/jquery-1.4.4.min.js")" type="text/javascript"></script> @RenderSection("Scripts", false) </body> </html> 

以下是使用此布局的示例视图。

 <h2>This is the view</h2> @{Html.RenderPartial("_Partial");} @section Scripts { <script type="text/javascript"> alert("I'm a view."); </script> } 

这里是从视图中呈现的部分。

 <p>This is the partial.</p> @* this never makes it into the rendered page *@ @section Scripts { <script type="text/javascript"> alert("I'm a partial."); </script> } 

在这个例子中,视图中指定的标记放置在该部分中,但部分的标记不是。 是否有可能用剃刀填充部分视图的一个部分? 如果不是的话,还有其他一些获得Javascript的方法,只有页面底部的partials才需要,而不包括全局吗?

我处理这个问题的方法是向HtmlHelper类写几个扩展方法。 这允许部分视图来说,他们需要一个脚本,然后在布局视图,写入标签我打电话给我的帮手方法发出所需的脚本

以下是辅助方法:

 public static string RequireScript(this HtmlHelper html, string path, int priority = 1) { var requiredScripts = HttpContext.Current.Items["RequiredScripts"] as List<ResourceInclude>; if (requiredScripts == null) HttpContext.Current.Items["RequiredScripts"] = requiredScripts = new List<ResourceInclude>(); if (!requiredScripts.Any(i => i.Path == path)) requiredScripts.Add(new ResourceInclude() { Path = path, Priority = priority }); return null; } public static HtmlString EmitRequiredScripts(this HtmlHelper html) { var requiredScripts = HttpContext.Current.Items["RequiredScripts"] as List<ResourceInclude>; if (requiredScripts == null) return null; StringBuilder sb = new StringBuilder(); foreach (var item in requiredScripts.OrderByDescending(i => i.Priority)) { sb.AppendFormat("<script src=\"{0}\" type=\"text/javascript\"></script>\n", item.Path); } return new HtmlString(sb.ToString()); } public class ResourceInclude { public string Path { get; set; } public int Priority { get; set; } } 

一旦你有了这个地方,你的局部视图只需要调用@Html.RequireScript("/Path/To/Script")

在布局视图的头部分中,您可以调用@Html.EmitRequiredScripts()

这个额外的好处是它允许你清除重复的脚本请求。 如果您有多个需要给定脚本的视图/部分视图,则可以安全地假定您只输出一次

部分视图不能参与他们的父视图部分。

你可以有第二个部分,只负责注入必要的JavaScript。 如果你想要在@if块周围放置几个脚本:

 @model string @if(Model == "bla") { <script type="text/javascript">...</script> } @else if(Model == "bli") { <script type="text/javascript">...</script> } 

这显然可以清理一下,但是,在视图的Scripts部分:

 @section Scripts { @Html.Partial("_Scripts", "ScriptName_For_Partial1") } 

再次,它可能不会赢得一个美容奖,但它会起作用。

更优雅的方法是将部分视图脚本移动到单独的文件中,然后在“脚本”部分中进行渲染:

 <h2>This is the view</h2> @Html.RenderPartial("_Partial") @section Scripts { @Html.RenderPartial("_PartialScripts") <script type="text/javascript"> alert("I'm a view script."); </script> } 

局部视图_ Partial.cshtml

 <p>This is the partial.</p> 

部分视图_ 包含脚本的PartialScripts.cshtml

 <script type="text/javascript"> alert("I'm a partial script!"); </script> 

安装Forloop.HtmlHelpers nuget包 – 它在部分视图和编辑器模板中添加了一些用于pipe理脚本的助手。

在你的布局的某个地方,你需要打电话

 @Html.RenderScripts() 

这将是任何脚本文件和脚本块将在页面中输出的地方,所以我build议把它放在你的主要脚本之后的布局和脚本部分(如果有的话)。

如果您使用捆绑Web优化框架,则可以使用重载

 @Html.RenderScripts(Scripts.Render) 

所以这个方法是用来写出脚本文件的。

现在,无论何时您想在视图,局部视图或模板中添加脚本文件或块,只需使用

 @using (Html.BeginScriptContext()) { Html.AddScriptFile("~/Scripts/jquery.validate.js"); Html.AddScriptBlock( @<script type="text/javascript"> $(function() { $('#someField').datepicker(); }); </script> ); } 

助手确保只添加一个脚本文件引用,如果多次添加,还可以确保脚本文件以预期顺序呈现

  1. 布局
  2. 部分和模板(按照它们在视图中显示的顺序,从上到下)

[更新版本]更新后的@Necrocubus问题包含内联脚本。

 public static class ScriptsExtensions { const string REQ_SCRIPT = "RequiredScript"; const string REQ_INLINESCRIPT = "RequiredInlineScript"; const string REQ_STYLE = "RequiredStyle"; #region Scripts /// <summary> /// Adds a script /// </summary> /// <param name="html"></param> /// <param name="path"></param> /// <param name="priority">Ordered by decreasing priority </param> /// <param name="bottom"></param> /// <param name="options"></param> /// <returns></returns> public static string RequireScript(this IHtmlHelper html, string path, int priority = 1, bool bottom=false, params string[] options) { var ctxt = html.ViewContext.HttpContext; var requiredScripts = ctxt.Items[REQ_SCRIPT] as List<ResourceToInclude>; if (requiredScripts == null) ctxt.Items[REQ_SCRIPT] = requiredScripts = new List<ResourceToInclude>(); if (!requiredScripts.Any(i => i.Path == path)) requiredScripts.Add(new ResourceToInclude() { Path = path, Priority = priority, Options = options, Type=ResourceType.Script, Bottom=bottom}); return null; } /// <summary> /// /// </summary> /// <param name="html"></param> /// <param name="script"></param> /// <param name="priority">Ordered by decreasing priority </param> /// <param name="bottom"></param> /// <returns></returns> public static string RequireInlineScript(this IHtmlHelper html, string script, int priority = 1, bool bottom = false) { var ctxt = html.ViewContext.HttpContext; var requiredScripts = ctxt.Items[REQ_INLINESCRIPT] as List<InlineResource>; if (requiredScripts == null) ctxt.Items[REQ_INLINESCRIPT] = requiredScripts = new List<InlineResource>(); requiredScripts.Add(new InlineResource() { Content=script, Priority = priority, Bottom=bottom, Type=ResourceType.Script}); return null; } /// <summary> /// Just call @Html.EmitRequiredScripts(false) /// at the end of your head tag and /// @Html.EmitRequiredScripts(true) at the end of the body if some scripts are set to be at the bottom. /// </summary> public static HtmlString EmitRequiredScripts(this IHtmlHelper html, bool bottom) { var ctxt = html.ViewContext.HttpContext; var requiredScripts = ctxt.Items[REQ_SCRIPT] as List<ResourceToInclude>; var requiredInlineScripts = ctxt.Items[REQ_INLINESCRIPT] as List<InlineResource>; var scripts = new List<Resource>(); scripts.AddRange(requiredScripts ?? new List<ResourceToInclude>()); scripts.AddRange(requiredInlineScripts ?? new List<InlineResource>()); if (scripts.Count==0) return null; StringBuilder sb = new StringBuilder(); foreach (var item in scripts.Where(s=>s.Bottom==bottom).OrderByDescending(i => i.Priority)) { sb.Append(item.ToString()); } return new HtmlString(sb.ToString()); } #endregion Scripts #region Styles /// <summary> /// /// </summary> /// <param name="html"></param> /// <param name="path"></param> /// <param name="priority">Ordered by decreasing priority </param> /// <param name="options"></param> /// <returns></returns> public static string RequireStyle(this IHtmlHelper html, string path, int priority = 1, params string[] options) { var ctxt = html.ViewContext.HttpContext; var requiredScripts = ctxt.Items[REQ_STYLE] as List<ResourceToInclude>; if (requiredScripts == null) ctxt.Items[REQ_STYLE] = requiredScripts = new List<ResourceToInclude>(); if (!requiredScripts.Any(i => i.Path == path)) requiredScripts.Add(new ResourceToInclude() { Path = path, Priority = priority, Options = options }); return null; } /// <summary> /// Just call @Html.EmitRequiredStyles() /// at the end of your head tag /// </summary> public static HtmlString EmitRequiredStyles(this IHtmlHelper html) { var ctxt = html.ViewContext.HttpContext; var requiredScripts = ctxt.Items[REQ_STYLE] as List<ResourceToInclude>; if (requiredScripts == null) return null; StringBuilder sb = new StringBuilder(); foreach (var item in requiredScripts.OrderByDescending(i => i.Priority)) { sb.Append(item.ToString()); } return new HtmlString(sb.ToString()); } #endregion Styles #region Models public class InlineResource : Resource { public string Content { get; set; } public override string ToString() { return "<script>"+Content+"</script>"; } } public class ResourceToInclude : Resource { public string Path { get; set; } public string[] Options { get; set; } public override string ToString() { switch(Type) { case ResourceType.CSS: if (Options == null || Options.Length == 0) return String.Format("<link rel=\"stylesheet\" href=\"{0}\" type=\"text/css\" />\n", Path); else return String.Format("<link rel=\"stylesheet\" href=\"{0}\" type=\"text/css\" {1} />\n", Path, String.Join(" ", Options)); default: case ResourceType.Script: if (Options == null || Options.Length == 0) return String.Format("<script src=\"{0}\" type=\"text/javascript\"></script>\n", Path); else return String.Format("<script src=\"{0}\" type=\"text/javascript\" {1}></script>\n", Path, String.Join(" ", Options)); } } } public class Resource { public ResourceType Type { get; set; } public int Priority { get; set; } public bool Bottom { get; set; } } public enum ResourceType { Script, CSS } #endregion Models } 

我的2美分,这是一个旧的职位,但仍然相关,所以这里是贝尔的解决scheme,与ASP.Net核心合作的升级更新。

它允许从导入的部分视图和子视图添加脚本和样式到主布局,并可以添加选项到脚本/样式导入(如asynchronous延期等):

 public static class ScriptsExtensions { const string REQ_SCRIPT = "RequiredScript"; const string REQ_STYLE = "RequiredStyle"; public static string RequireScript(this IHtmlHelper html, string path, int priority = 1, params string[] options) { var ctxt = html.ViewContext.HttpContext; var requiredScripts = ctxt.Items[REQ_SCRIPT] as List<ResourceInclude>; if (requiredScripts == null) ctxt.Items[REQ_SCRIPT] = requiredScripts = new List<ResourceInclude>(); if (!requiredScripts.Any(i => i.Path == path)) requiredScripts.Add(new ResourceInclude() { Path = path, Priority = priority, Options = options }); return null; } public static HtmlString EmitRequiredScripts(this IHtmlHelper html) { var ctxt = html.ViewContext.HttpContext; var requiredScripts = ctxt.Items[REQ_SCRIPT] as List<ResourceInclude>; if (requiredScripts == null) return null; StringBuilder sb = new StringBuilder(); foreach (var item in requiredScripts.OrderByDescending(i => i.Priority)) { if (item.Options == null || item.Options.Length == 0) sb.AppendFormat("<script src=\"{0}\" type=\"text/javascript\"></script>\n", item.Path); else sb.AppendFormat("<script src=\"{0}\" type=\"text/javascript\" {1}></script>\n", item.Path, String.Join(" ", item.Options)); } return new HtmlString(sb.ToString()); } public static string RequireStyle(this IHtmlHelper html, string path, int priority = 1, params string[] options) { var ctxt = html.ViewContext.HttpContext; var requiredScripts = ctxt.Items[REQ_STYLE] as List<ResourceInclude>; if (requiredScripts == null) ctxt.Items[REQ_STYLE] = requiredScripts = new List<ResourceInclude>(); if (!requiredScripts.Any(i => i.Path == path)) requiredScripts.Add(new ResourceInclude() { Path = path, Priority = priority, Options = options }); return null; } public static HtmlString EmitRequiredStyles(this IHtmlHelper html) { var ctxt = html.ViewContext.HttpContext; var requiredScripts = ctxt.Items[REQ_STYLE] as List<ResourceInclude>; if (requiredScripts == null) return null; StringBuilder sb = new StringBuilder(); foreach (var item in requiredScripts.OrderByDescending(i => i.Priority)) { if (item.Options == null || item.Options.Length == 0) sb.AppendFormat("<link rel=\"stylesheet\" href=\"{0}\" type=\"text/css\" />\n", item.Path); else sb.AppendFormat("<link rel=\"stylesheet\" href=\"{0}\" type=\"text/css\" {1} />\n", item.Path, String.Join(" ", item.Options)); } return new HtmlString(sb.ToString()); } public class ResourceInclude { public string Path { get; set; } public int Priority { get; set; } public string[] Options { get; set; } } } 

根据上面的Bell和Shimmy先生的回答,我为Bundle脚本添加了额外的function。

 using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Text; using System.Web.Mvc; namespace ABC.Utility { public static class PartialViewHelper { public static string RequireScript(this HtmlHelper html, string path, int priority = 1) { var requiredScripts = HttpContext.Current.Items["RequiredScripts"] as List<ResourceInclude>; if (requiredScripts == null) HttpContext.Current.Items["RequiredScripts"] = requiredScripts = new List<ResourceInclude>(); if (!requiredScripts.Any(i => i.Path == path)) requiredScripts.Add(new ResourceInclude() { Path = path, Priority = priority }); return null; } public static string RequireBundleStyles(this HtmlHelper html, string bundleName) { var a = System.Web.Optimization.Styles.Render(bundleName); var requiredStyles = HttpContext.Current.Items["RequiredStyles"] as IHtmlString; if (requiredStyles == null) HttpContext.Current.Items["RequiredStyles"] = requiredStyles = a; return null; } public static string RequireBundleScripts(this HtmlHelper html, string bundleName) { var a=System.Web.Optimization.Scripts.Render(bundleName); var requiredScripts = HttpContext.Current.Items["RequiredScripts"] as IHtmlString; if (requiredScripts == null) HttpContext.Current.Items["RequiredScripts"] = requiredScripts = a; return null; } public static HtmlString EmitRequiredBundleStyles(this HtmlHelper html) { var requiredStyles = HttpContext.Current.Items["RequiredStyles"] as IHtmlString; if (requiredStyles == null) return null; return MvcHtmlString.Create(requiredStyles.ToHtmlString()) ; } public static HtmlString EmitRequiredBundleScripts(this HtmlHelper html) { var requiredScripts = HttpContext.Current.Items["RequiredScripts"] as IHtmlString; if (requiredScripts == null) return null; return MvcHtmlString.Create(requiredScripts.ToHtmlString()); } public static HtmlString EmitRequiredScripts(this HtmlHelper html) { var requiredScripts = HttpContext.Current.Items["RequiredScripts"] as List<ResourceInclude>; if (requiredScripts == null) return null; StringBuilder sb = new StringBuilder(); foreach (var item in requiredScripts.OrderByDescending(i => i.Priority)) { sb.AppendFormat("<script src=\"{0}\" type=\"text/javascript\"></script>\n", item.Path); } return new HtmlString(sb.ToString()); } public class ResourceInclude { public string Path { get; set; } public int Priority { get; set; } } }//end class }// end namespace 

在PartialView上的示例: – @ Html.RequireBundleStyles(“〜/ bundles / fileupload / bootstrap / BasicPlusUI / css”); @ Html.RequireBundleScripts( “〜/捆绑/file upload/引导/ BasicPlusUI / JS”);

在MasterPage上的示例: – @ Html.EmitRequiredBundleStyles()

使用@using(Html.Delayed()){ ...your content... }扩展来稍后在页面中呈现任何内容(脚本或HTML) 。 内部Queue应确保正确的sorting。

此function也在ClientDependency.Core.Mvc.dll中实现。 它提供了html助手:@ Html.RequiresJs和@ Html.RenderJsHere()。 Nuget包:ClientDependency-Mvc