ChatGPT解决这个技术问题 Extra ChatGPT

从部分填充剃刀部分

我尝试这样做的主要动机是获取仅在页面底部的部分与其余 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>
}

在此示例中,视图中指定的标记被放置在部分中,但部分中的标记不是。是否可以使用 Razor 从局部视图中填充部分?如果没有,还有哪些其他方法可以获取仅页面底部的部分需要而不在全局范围内包含的 Javascript?

也许这是一个问题,因为您在部分中有另一个脚本部分.. IDK.. 您的代码有点混乱..
它不是。即使该部分被排除在视图之外,部分中的代码也不会进入最终呈现的页面。我认为 SLaks 是正确的,因为 partials 不能参与父视图的部分。

S
Shimmy Weitzhandler

我处理这个问题的方法是为 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")

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

另一个好处是它允许您清除重复的脚本请求。如果您有多个需要给定脚本的视图/部分视图,您可以放心地假设您只会输出一次


优雅而干净的解决方案。 +1
在拔掉我的大部分头发后才遇到这个解决方案 - 很好的解决方案......
我无法让这个解决方案发挥作用。似乎在任何局部视图调用 RequireScript() 之前调用了 EmitRequiredScripts()。难道我做错了什么?
布莱恩,有些事情听起来不对劲。在过去一年左右的时间里,我广泛使用了这个解决方案,并且运行良好。也许发布一个新问题,详细说明您的问题并在此处链接网址
在部署新版本的应用程序时,这是否有任何缓存破坏支持?开箱即用的 @scripts.Render() 方法在构建时生成的最后一个 URL 参数,以便在部署新版本时强制浏览器获取最新版本。
S
SLaks

部分视图不能参与其父视图的部分。


这是我怀疑的。谢谢。
@JohnBubriski Razor 2中有。不知道prev。版本。
@SLaks,为什么这是设计使然?在我的场景中,我有一个部分是横幅旋转器,我希望它的脚本/样式仅在它打开时加载,为什么内联加载它不好?
@Shimmy:您应该使用资源管理系统,例如 Cassette。
谢谢你。我会调查的。
S
Sergi Papaseit

你可以有第二个部分,它只负责注入必要的 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")
}

同样,它可能不会赢得美容奖,但它会起作用。


这与我最终所做的非常接近。它绝对不漂亮,但它确实有效。唯一的缺点是您无法通过 ajax 调用获取部分内容并包含 JS。我认为从长远来看,我将最终使用 jQuery 模板进行重构,并且只从我的控制器发送 JSON,而不是在服务器端构建 html。
@CraigM 那也是我要去的地方。 MVC 是合法的,但是(对我而言)使用模板客户端(我正在研究 Backbone.js)然后从 API 推/拉更有意义。
@one.beat.customer - 自从我也使用 Backbone 以来,我一直在使用 underscore 的模板,但我正在考虑从 Twitter 切换到 Hogan 库或从 Nodejitsu 切换到 Plates。两者都有相当不错的功能。
V
Vlad Rudenko

更优雅的方法是将部分视图脚本移动到单独的文件中,然后在视图的脚本部分中呈现它:

<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>

这不像其他答案中建议的某些扩展方法或插件那样自动,但它确实具有简单明了的优点。它喜欢它。
R
Russ Cam

安装 Forloop.HtmlHelpers nuget 包 - 它添加了一些帮助程序来管理部分视图和编辑器模板中的脚本。

在您的布局中的某处,您需要调用

@Html.RenderScripts()

这将是页面中输出任何脚本文件和脚本块的位置,因此我建议将其放在布局中的主要脚本之后和脚本部分(如果有的话)之后。

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

@Html.RenderScripts(Scripts.Render)

以便此方法用于写出脚本文件。

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

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

如果多次添加,帮助程序确保只呈现一个脚本文件引用,并且它还确保脚本文件以预期的顺序呈现,即

布局部分和模板(按照它们在视图中出现的顺序,从上到下)


J
Jean

[更新版本] 在@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 美分,这是一个旧帖子,但仍然相关,所以这里是贝尔先生解决方案的升级更新,它适用于 ASP.Net Core。

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

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; }
    }
}

谢谢你,兄弟!这应该得到更多的支持,因为它比 6 岁的答案更相关。
此外,是否可以修改这些扩展以允许输入脚本部分? @ 或类似部分的内容?否则我仍然需要一个小的 JS 脚本来使用服务器端模型变量初始化另一个脚本:/
@Necroqubus 你可以检查更新的版本,但是我还没有测试过:)
好的,我会尝试为您测试它。我希望它适用于 ASP.NET Core 1.0 MVC。对于上下文,我有多个级别的嵌套部分,并希望它们的脚本在页脚处呈现。
不需要 <text>,只需将其添加为字符串(如果您愿意,您仍然可以为多行添加 @"" 前缀),并且没有 <script> 标记
S
Sebastian

对于那些寻找 aspnet core 2.0 版本的人:

    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using Microsoft.AspNetCore.Html;
    using Microsoft.AspNetCore.Http;

    public static class HttpContextAccessorExtensions
    {
        public static string RequireScript(this IHttpContextAccessor htmlContextAccessor, string path, int priority = 1)
        {
            var requiredScripts = htmlContextAccessor.HttpContext.Items["RequiredScripts"] as List<ResourceInclude>;
            if (requiredScripts == null) htmlContextAccessor.HttpContext.Items["RequiredScripts"] = requiredScripts = new List<ResourceInclude>();
            if (requiredScripts.All(i => i.Path != path)) requiredScripts.Add(new ResourceInclude() { Path = path, Priority = priority });
            return null;
        }

        public static HtmlString EmitRequiredScripts(this IHttpContextAccessor htmlContextAccessor)
        {
            var requiredScripts = htmlContextAccessor.HttpContext.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; }
        }
    }

在脚本渲染部分调用后添加到您的布局:

@HttpContextAccessor.EmitRequiredScripts()

在你的部分观点中:

@inject IHttpContextAccessor HttpContextAccessor

...

@HttpContextAccessor.RequireScript("/scripts/moment.min.js")

K
KyleMit

您可以创建一个新的 Layout 页面并将 PartialView 包装在负责呈现内容以及任何库部分的完整视图中。

例如,假设我有以下代码:

HomeController.cs

[HttpGet]
public ActionResult About()
{
    var vm = new AboutViewModel();
    return View("About", vm);
}

呈现整页视图时,通常通过合并两个文件来呈现:

About.cshtml

@model AboutViewModel

@{
    ViewBag.Title = "About CSHN";
}

<h3>@ViewBag.Title</h3>

@section Styles {
    <style> /* style info here */ </style>
}

@section Scripts {
    <script> /* script info here */ </script>
}

_Layout.cshtml (或在 _ViewStart 中指定或在页面中覆盖的任何内容)

<!DOCTYPE html>

<html>
<head>
    @RenderSection("Styles", false)
    <title>@ViewBag.Title</title>
</head>
<body>
    @RenderBody()

    @RenderSection("scripts", false)
</body>
</html>

现在,假设您想将 About.cshtml 渲染为 局部视图,或许作为响应 AJAX 调用的模式窗口。此处的目标是仅返回关于页面、脚本和所有内容中指定的内容,而不包含 _Layout.cshtml 主布局中包含的所有膨胀(如完整的 <html> 文档)。

您可以这样尝试,但它不会附带任何部分块:

return PartialView("About", vm);

相反,添加一个更简单的布局页面,如下所示:

_PartialLayout.cshtml

<div>
    @RenderBody()
    @RenderSection("Styles", false)
    @RenderSection("scripts", false)
</div>

或者支持这样的模式窗口:

_ModalLayout.cshtml

<div class="modal modal-page fade" tabindex="-1" role="dialog" >
    <div class="modal-dialog modal-lg" role="document">
        <div class="modal-content">

            <div class="modal-header">
                <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
                <h4 class="modal-title">@ViewBag.Title</h4>
            </div>

            <div class="modal-body">

                @RenderBody()
                @RenderSection("Styles", false)
                @RenderSection("scripts", false)

            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-inverse" data-dismiss="modal">Dismiss</button>
            </div>
        </div>
    </div>
</div>

然后您可以在此控制器或任何其他要同时呈现视图的内容和脚本的处理程序中specify a custom Master View

[HttpGet]
public ActionResult About()
{
    var vm = new AboutViewModel();
    return !Request.IsAjaxRequest()
              ? View("About", vm)
              : View("About", "~/Views/Shared/_ModalLayout.cshtml", vm);
}

H
Harris Yer

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

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("~/bundles/fileupload/bootstrap/BasicPlusUI/js");

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


C
Community

使用答案 https://stackoverflow.com/a/18790222/1037948 中的 @using(Html.Delayed()){ ...your content... } 扩展来稍后在页面中呈现任何内容(脚本或只是 HTML)。内部 Queue 应确保正确排序。


E
Evert

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


J
Jonathan

这是我对常见问题“如何将部分从部分视图注入到主视图或 asp.net mvc 的主布局视图?”的解决方案。如果您通过关键字“section + partial”在 stackoverflow 上进行搜索,您将获得相当多的相关问题列表,并给出答案,但通过剃须刀引擎语法,它们对我来说似乎都不优雅。所以我只是看一下 Razor 引擎,看看是否有更好的解决方案来解决这个问题。

幸运的是,我发现 Razor 引擎如何编译视图模板文件(*.cshtml、*.vbhtml)让我很感兴趣。 (我稍后会解释),下面是我的解决方案代码,我认为它在使用上非常简单和优雅。

namespace System.Web.Mvc.Html
{
    public static class HtmlHelperExtensions
    {
        /// <summary>
        /// 确保所有视图,包括分部视图(PartialView)中的节(Section)定义被按照先后顺序追加到最终文档输出流中
        /// </summary>
        public static MvcHtmlString EnsureSection(this HtmlHelper helper)
        {
            var wp = (WebViewPage)helper.ViewDataContainer;
            Dictionary<string, WebPages.SectionWriter> sw = (Dictionary<string, WebPages.SectionWriter>)typeof(WebPages.WebPageBase).GetProperty("SectionWriters", Reflection.BindingFlags.NonPublic | Reflection.BindingFlags.Instance).GetValue(wp);
            if (!helper.ViewContext.HttpContext.Items.Contains("SectionWriter"))
            {
                Dictionary<string, Stack<WebPages.SectionWriter>> qss = new Dictionary<string, Stack<WebPages.SectionWriter>>();
                helper.ViewContext.HttpContext.Items["SectionWriter"] = qss;
            }
            var eqs = (Dictionary<string, Stack<WebPages.SectionWriter>>)helper.ViewContext.HttpContext.Items["SectionWriter"];
            foreach (var kp in sw)
            {
                if (!eqs.ContainsKey(kp.Key)) eqs[kp.Key] = new Stack<WebPages.SectionWriter>();
                eqs[kp.Key].Push(kp.Value);
            }
            return MvcHtmlString.Create("");
        }

        /// <summary>
        /// 在文档流中渲染指定的节(Section)
        /// </summary>
        public static MvcHtmlString RenderSectionEx(this HtmlHelper helper, string section, bool required = false)
        {
            if (helper.ViewContext.HttpContext.Items.Contains("SectionWriter"))
            {
                Dictionary<string, Stack<WebPages.SectionWriter>> qss = (Dictionary<string, Stack<WebPages.SectionWriter>>)helper.ViewContext.HttpContext.Items["SectionWriter"];
                if (qss.ContainsKey(section))
                {
                    var wp = (WebViewPage)helper.ViewDataContainer;
                    var qs = qss[section];
                    while (qs.Count > 0)
                    {
                        var sw = qs.Pop();
                        var os = ((WebViewPage)sw.Target).OutputStack;
                        if (os.Count == 0) os.Push(wp.Output);
                        sw.Invoke();
                    }
                }
                else if (!qss.ContainsKey(section) && required)
                {
                    throw new Exception(string.Format("'{0}' section is not defined.", section));
                }
            }
            return MvcHtmlString.Create("");
        }
    }
}

用法:使用代码也很简单,看起来和平时几乎一样的风格。它还支持嵌套局部视图的任何级别。 IE。我有一个视图模板链:_ViewStart.cshtml->layout.cshtml->index.cshtml->[head.cshtml,foot.cshtml]->ad.cshtml。

在 layout.cshtml 中,我们有:

@ViewBag.Title - @ViewBag.WebSetting.Site.WebName @Html.RenderSectionEx("Head") @RenderBody() @Html.RenderSectionEx("Foot")

在 index.cshtml 中,我们有:

@{ ViewBag.Title = "首页"; } @Html.Partial("head")

.......
@Html.Partial("foot")

在 head.cshtml 中,我们将拥有以下代码:

@section Head{ }

......
@Html.EnsureSection()

在 foot.cshtml 或 ad.cshtml 中也是如此,您仍然可以在其中定义 Head 或 Foot 部分,确保在局部视图文件的末尾调用一次 @Html.EnsureSection()。这就是摆脱 asp mvc 中的问题所需要做的一切。

我只是分享我的代码片段,以便其他人可以使用它。如果您觉得它有用,请不要犹豫,为我的帖子评分。 :)