我在_Layout.cshtml
定义了此部分 javascript
@RenderSection("Scripts", false)
我能够很容易地从视图中使用它: html
@section Scripts { @*Stuff comes here*@ }
我正在努力的是如何从局部视图中将一些内容注入到本节中。 java
假设这是个人视图页面: mvc
@section Scripts { <script> //code comes here </script> } <div> poo bar poo </div> <div> @Html.Partial("_myPartial") </div>
我须要从_myPartial
局部视图的“ Scripts
部分中注入一些内容。 app
我怎样才能作到这一点? ide
这是一个很受欢迎的问题,因此我将发布解决方案。
我遇到了一样的问题,尽管它不是理想的,但我认为它实际上工做得很好,而且不会部分依赖视图。
个人状况是,动做自己能够访问,但也能够嵌入到视图中-谷歌地图。 wordpress
在个人_layout
我有: 函数
@RenderSection("body_scripts", false)
在个人index
视图中,我有: oop
@Html.Partial("Clients") @section body_scripts { @Html.Partial("Clients_Scripts") }
在个人clients
视图中,我拥有(全部地图和assoc。html): ui
@section body_scripts { @Html.Partial("Clients_Scripts") }
个人Clients_Scripts
视图包含要呈现到页面上的javascript
这样,个人脚本就被隔离了,而且能够在须要时呈现到页面中,而body_scripts
标签仅在剃刀视图引擎找到它的第一次出现时才呈现。
这使我能够将全部东西分开-这对我来讲是一种很好的解决方案,其余人可能对此有疑问,可是确实解决了“设计使然”的漏洞。
咱们对Web的思考方式存在一个根本缺陷,尤为是在使用MVC时。 缺陷在于,JavaScript是视图的责任。 视图是视图,JavaScript(行为或其余)是JavaScript。 在Silverlight和WPF的MVVM模式中,咱们面临的是“视图优先”或“模型优先”。 在MVC中,咱们应始终尝试从模型的角度进行推理,而JavaScript在许多方面都是该模型的一部分。
我建议使用AMD模式(我本身喜欢RequireJS )。 将JavaScript分红模块,定义功能并从JavaScript插入html,而不是依赖于视图来加载JavaScript。 这将清理您的代码,分散您的顾虑,并使生活变得一路顺风。
从该线程的解决方案中,我提出了如下可能过于复杂的解决方案,该解决方案使您能够延迟在using块中呈现任何html(脚本)。
典型场景:在部分视图中,不管页面中重复执行该部分视图多少次,都只包含一次块:
@using (Html.Delayed(isOnlyOne: "some unique name for this section")) { <script> someInlineScript(); </script> }
在局部视图中,每次使用局部时都要包含块:
@using (Html.Delayed()) { <b>show me multiple times, @Model.Whatever</b> }
在局部视图中,不管局部重复了多少次,都只包含一次该块,但稍后会经过名称when-i-call-you
专门呈现它:
@using (Html.Delayed("when-i-call-you", isOnlyOne: "different unique name")) { <b>show me once by name</b> <span>@Model.First().Value</span> }
(即在父视图中显示延迟的部分)
@Html.RenderDelayed(); // writes unnamed sections (#1 and #2, excluding #3) @Html.RenderDelayed("when-i-call-you", false); // writes the specified block, and ignore the `isOnlyOne` setting so we can dump it again @Html.RenderDelayed("when-i-call-you"); // render the specified block by name @Html.RenderDelayed("when-i-call-you"); // since it was "popped" in the last call, won't render anything due to `isOnlyOne` provided in `Html.Delayed`
public static class HtmlRenderExtensions { /// <summary> /// Delegate script/resource/etc injection until the end of the page /// <para>@via https://stackoverflow.com/a/14127332/1037948 and http://jadnb.wordpress.com/2011/02/16/rendering-scripts-from-partial-views-at-the-end-in-mvc/ </para> /// </summary> private class DelayedInjectionBlock : IDisposable { /// <summary> /// Unique internal storage key /// </summary> private const string CACHE_KEY = "DCCF8C78-2E36-4567-B0CF-FE052ACCE309"; // "DelayedInjectionBlocks"; /// <summary> /// Internal storage identifier for remembering unique/isOnlyOne items /// </summary> private const string UNIQUE_IDENTIFIER_KEY = CACHE_KEY; /// <summary> /// What to use as internal storage identifier if no identifier provided (since we can't use null as key) /// </summary> private const string EMPTY_IDENTIFIER = ""; /// <summary> /// Retrieve a context-aware list of cached output delegates from the given helper; uses the helper's context rather than singleton HttpContext.Current.Items /// </summary> /// <param name="helper">the helper from which we use the context</param> /// <param name="identifier">optional unique sub-identifier for a given injection block</param> /// <returns>list of delayed-execution callbacks to render internal content</returns> public static Queue<string> GetQueue(HtmlHelper helper, string identifier = null) { return _GetOrSet(helper, new Queue<string>(), identifier ?? EMPTY_IDENTIFIER); } /// <summary> /// Retrieve a context-aware list of cached output delegates from the given helper; uses the helper's context rather than singleton HttpContext.Current.Items /// </summary> /// <param name="helper">the helper from which we use the context</param> /// <param name="defaultValue">the default value to return if the cached item isn't found or isn't the expected type; can also be used to set with an arbitrary value</param> /// <param name="identifier">optional unique sub-identifier for a given injection block</param> /// <returns>list of delayed-execution callbacks to render internal content</returns> private static T _GetOrSet<T>(HtmlHelper helper, T defaultValue, string identifier = EMPTY_IDENTIFIER) where T : class { var storage = GetStorage(helper); // return the stored item, or set it if it does not exist return (T) (storage.ContainsKey(identifier) ? storage[identifier] : (storage[identifier] = defaultValue)); } /// <summary> /// Get the storage, but if it doesn't exist or isn't the expected type, then create a new "bucket" /// </summary> /// <param name="helper"></param> /// <returns></returns> public static Dictionary<string, object> GetStorage(HtmlHelper helper) { var storage = helper.ViewContext.HttpContext.Items[CACHE_KEY] as Dictionary<string, object>; if (storage == null) helper.ViewContext.HttpContext.Items[CACHE_KEY] = (storage = new Dictionary<string, object>()); return storage; } private readonly HtmlHelper helper; private readonly string identifier; private readonly string isOnlyOne; /// <summary> /// Create a new using block from the given helper (used for trapping appropriate context) /// </summary> /// <param name="helper">the helper from which we use the context</param> /// <param name="identifier">optional unique identifier to specify one or many injection blocks</param> /// <param name="isOnlyOne">extra identifier used to ensure that this item is only added once; if provided, content should only appear once in the page (i.e. only the first block called for this identifier is used)</param> public DelayedInjectionBlock(HtmlHelper helper, string identifier = null, string isOnlyOne = null) { this.helper = helper; // start a new writing context ((WebViewPage)this.helper.ViewDataContainer).OutputStack.Push(new StringWriter()); this.identifier = identifier ?? EMPTY_IDENTIFIER; this.isOnlyOne = isOnlyOne; } /// <summary> /// Append the internal content to the context's cached list of output delegates /// </summary> public void Dispose() { // render the internal content of the injection block helper // make sure to pop from the stack rather than just render from the Writer // so it will remove it from regular rendering var content = ((WebViewPage)this.helper.ViewDataContainer).OutputStack; var renderedContent = content.Count == 0 ? string.Empty : content.Pop().ToString(); // if we only want one, remove the existing var queue = GetQueue(this.helper, this.identifier); // get the index of the existing item from the alternate storage var existingIdentifiers = _GetOrSet(this.helper, new Dictionary<string, int>(), UNIQUE_IDENTIFIER_KEY); // only save the result if this isn't meant to be unique, or // if it's supposed to be unique and we haven't encountered this identifier before if( null == this.isOnlyOne || !existingIdentifiers.ContainsKey(this.isOnlyOne) ) { // remove the new writing context we created for this block // and save the output to the queue for later queue.Enqueue(renderedContent); // only remember this if supposed to if(null != this.isOnlyOne) existingIdentifiers[this.isOnlyOne] = queue.Count; // save the index, so we could remove it directly (if we want to use the last instance of the block rather than the first) } } } /// <summary> /// <para>Start a delayed-execution block of output -- this will be rendered/printed on the next call to <see cref="RenderDelayed"/>.</para> /// <para> /// <example> /// Print once in "default block" (usually rendered at end via <code>@Html.RenderDelayed()</code>). Code: /// <code> /// @using (Html.Delayed()) { /// <b>show at later</b> /// <span>@Model.Name</span> /// etc /// } /// </code> /// </example> /// </para> /// <para> /// <example> /// Print once (i.e. if within a looped partial), using identified block via <code>@Html.RenderDelayed("one-time")</code>. Code: /// <code> /// @using (Html.Delayed("one-time", isOnlyOne: "one-time")) { /// <b>show me once</b> /// <span>@Model.First().Value</span> /// } /// </code> /// </example> /// </para> /// </summary> /// <param name="helper">the helper from which we use the context</param> /// <param name="injectionBlockId">optional unique identifier to specify one or many injection blocks</param> /// <param name="isOnlyOne">extra identifier used to ensure that this item is only added once; if provided, content should only appear once in the page (i.e. only the first block called for this identifier is used)</param> /// <returns>using block to wrap delayed output</returns> public static IDisposable Delayed(this HtmlHelper helper, string injectionBlockId = null, string isOnlyOne = null) { return new DelayedInjectionBlock(helper, injectionBlockId, isOnlyOne); } /// <summary> /// Render all queued output blocks injected via <see cref="Delayed"/>. /// <para> /// <example> /// Print all delayed blocks using default identifier (i.e. not provided) /// <code> /// @using (Html.Delayed()) { /// <b>show me later</b> /// <span>@Model.Name</span> /// etc /// } /// </code> /// -- then later -- /// <code> /// @using (Html.Delayed()) { /// <b>more for later</b> /// etc /// } /// </code> /// -- then later -- /// <code> /// @Html.RenderDelayed() // will print both delayed blocks /// </code> /// </example> /// </para> /// <para> /// <example> /// Allow multiple repetitions of rendered blocks, using same <code>@Html.Delayed()...</code> as before. Code: /// <code> /// @Html.RenderDelayed(removeAfterRendering: false); /* will print */ /// @Html.RenderDelayed() /* will print again because not removed before */ /// </code> /// </example> /// </para> /// </summary> /// <param name="helper">the helper from which we use the context</param> /// <param name="injectionBlockId">optional unique identifier to specify one or many injection blocks</param> /// <param name="removeAfterRendering">only render this once</param> /// <returns>rendered output content</returns> public static MvcHtmlString RenderDelayed(this HtmlHelper helper, string injectionBlockId = null, bool removeAfterRendering = true) { var stack = DelayedInjectionBlock.GetQueue(helper, injectionBlockId); if( removeAfterRendering ) { var sb = new StringBuilder( #if DEBUG string.Format("<!-- delayed-block: {0} -->", injectionBlockId) #endif ); // .count faster than .any while (stack.Count > 0) { sb.AppendLine(stack.Dequeue()); } return MvcHtmlString.Create(sb.ToString()); } return MvcHtmlString.Create( #if DEBUG string.Format("<!-- delayed-block: {0} -->", injectionBlockId) + #endif string.Join(Environment.NewLine, stack)); } }
若是确实有须要从partial
运行一些js
,请按如下步骤操做,须要使用jQuery
:
<script type="text/javascript"> function scriptToExecute() { //The script you want to execute when page is ready. } function runWhenReady() { if (window.$) scriptToExecute(); else setTimeout(runWhenReady, 100); } runWhenReady(); </script>
您无需在局部视图中使用部分。
包括在您的局部视图中。 jQuery加载后执行该函数。 您能够更改代码的条件子句。
<script type="text/javascript"> var time = setInterval(function () { if (window.jQuery != undefined) { window.clearInterval(time); //Begin $(document).ready(function () { //.... }); //End }; }, 10); </script>
朱利奥·斯派德