tug*_*erk 316 asp.net asp.net-mvc partial-views razor asp.net-mvc-3
我在本节中定义了此部分 _Layout.cshtml
@RenderSection("Scripts", false)
Run Code Online (Sandbox Code Playgroud)
我可以从视图中轻松使用它:
@section Scripts {
@*Stuff comes here*@
}
Run Code Online (Sandbox Code Playgroud)
我正在努力的是如何从局部视图中获取本节内注入的一些内容.
我们假设这是我的视图页面:
@section Scripts {
<script>
//code comes here
</script>
}
<div>
poo bar poo
</div>
<div>
@Html.Partial("_myPartial")
</div>
Run Code Online (Sandbox Code Playgroud)
我需要Scripts从_myPartial局部视图中注入部分内容.
我怎样才能做到这一点?
Dar*_*rov 229
部分在部分视图中不起作用,这是设计的.您可以使用一些自定义帮助程序来实现类似的行为,但老实说,视图的责任是包含必要的脚本,而不是部分责任.我建议使用主视图的@scripts部分来做到这一点,而不要让部分人担心脚本.
dan*_*son 82
这是一个非常受欢迎的问题,所以我会发布我的解决方案.
我有同样的问题,虽然它不理想,我认为它实际上运作良好,并没有使部分依赖于视图.
我的情况是,一个动作本身可以访问,但也可以嵌入到视图中 - 谷歌地图.
在我的_layout身上:
@RenderSection("body_scripts", false)
Run Code Online (Sandbox Code Playgroud)
在我index看来,我有:
@Html.Partial("Clients")
@section body_scripts
{
@Html.Partial("Clients_Scripts")
}
Run Code Online (Sandbox Code Playgroud)
在我clients看来,我有(所有的地图和assoc .html):
@section body_scripts
{
@Html.Partial("Clients_Scripts")
}
Run Code Online (Sandbox Code Playgroud)
我的Clients_Scripts视图包含要在页面上呈现的javascript
这样我的脚本就被隔离了,并且可以在需要的地方呈现到页面中,body_scripts标签只在剃刀视图引擎找到它的第一次出现时呈现.
这让我可以把所有东西分开 - 这是一个对我来说很有效的解决方案,其他人可能会遇到问题,但它确实修补了"按设计"的漏洞.
drz*_*aus 39
从这个线程的解决方案中,我提出了以下可能过于复杂的解决方案,它允许您延迟在使用块中呈现任何html(脚本).
典型场景:在局部视图中,无论页面中重复部分视图多少次,都只包括一次块:
@using (Html.Delayed(isOnlyOne: "some unique name for this section")) {
<script>
someInlineScript();
</script>
}
Run Code Online (Sandbox Code Playgroud)在局部视图中,每次使用部分时都包括块:
@using (Html.Delayed()) {
<b>show me multiple times, @Model.Whatever</b>
}
Run Code Online (Sandbox Code Playgroud)在局部视图中,无论部分重复多少次,都只包括块一次,但稍后通过名称具体呈现它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>
}
Run Code Online (Sandbox Code Playgroud)(即在父视图中显示延迟部分)
@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`
Run Code Online (Sandbox Code Playgroud)
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));
}
}
Run Code Online (Sandbox Code Playgroud)
如果你确实有合法的需要js从a 运行一些partial,这是你可以做到的,这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>
Run Code Online (Sandbox Code Playgroud)
OP 的目标是他想将内联脚本定义到他的部分视图中,我假设该脚本仅特定于该部分视图,并将该块包含在他的脚本部分中。
我知道他想要让部分视图自包含。这个想法类似于使用 Angular 时的组件。
我的方法是将脚本按原样保留在部分视图中。现在的问题是在调用 Partial View 时,它可能会在所有其他脚本(通常添加到布局页面的底部)之前在那里执行脚本。在这种情况下,您只需让部分视图脚本等待其他脚本。有几种方法可以做到这一点。我以前使用过的最简单的方法是在body。
在我的布局上,我会在底部有这样的东西:
// global scripts
<script src="js/jquery.min.js"></script>
// view scripts
@RenderSection("scripts", false)
// then finally trigger partial view scripts
<script>
(function(){
document.querySelector('body').dispatchEvent(new Event('scriptsLoaded'));
})();
</script>
Run Code Online (Sandbox Code Playgroud)
然后在我的部分视图(在底部):
<script>
(function(){
document.querySelector('body').addEventListener('scriptsLoaded', function() {
// .. do your thing here
});
})();
</script>
Run Code Online (Sandbox Code Playgroud)
另一种解决方案是使用堆栈推送所有脚本,并在最后调用每个脚本。如前所述,其他解决方案是 RequireJS/AMD 模式,它也非常有效。
我们对Web的思考方式存在一个根本缺陷,尤其是在使用MVC时。缺陷在于,JavaScript是视图的责任。视图是视图,JavaScript(行为或其他)是JavaScript。在Silverlight和WPF的MVVM模式中,我们面临的是“视图优先”或“模型优先”。在MVC中,我们应始终尝试从模型的角度进行推理,而JavaScript在许多方面都是该模型的一部分。
我建议使用AMD模式(我自己喜欢RequireJS)。将模块中的JavaScript分开,定义功能并从JavaScript插入html,而不是依赖于视图来加载JavaScript。这将清理您的代码,分离您的关注点,并使生活更加轻松。
| 归档时间: |
|
| 查看次数: |
192735 次 |
| 最近记录: |