ChatGPT解决这个技术问题 Extra ChatGPT

使用 Razor 视图引擎将内容从局部视图 ASP.NET MVC 3 注入特定部分

我在我的 _Layout.cshtml 中定义了此部分

@RenderSection("Scripts", false)

我可以很容易地从一个视图中使用它:

@section Scripts { 
    @*Stuff comes here*@
}

我正在努力解决的是如何从局部视图中将一些内容注入到本节中。

假设这是我的视图页面:

@section Scripts { 

    <script>
        //code comes here
    </script>
}

<div>
    poo bar poo
</div>

<div>
  @Html.Partial("_myPartial")
</div>

我需要在 _myPartial 部分视图的 Scripts 部分中注入一些内容。

我怎样才能做到这一点?

对于以后来这里的任何人 - 有一个用于处理此问题的 nuget 包:nuget.org/packages/Forloop.HtmlHelpers
@RussCam 你应该回答这个问题。 +1 nuget 包解决了 OP 遇到的确切问题。
@RussCam NuGet 包不是解决方案,包的代码可能是。
@马克西姆维。好吧,我编写了 nuget 包并且无意将其删除,因此与其在此处重复代码 (bitbucket.org/forloop/forloop-htmlhelpers/src) 或 wiki (bitbucket.org/forloop/forloop-htmlhelpers/wiki/Home),不如将其链接作为评论保留在堆栈溢出,海事组织。
这是另一个看起来非常不错的解决方案:stackoverflow.com/questions/5355427/…

C
Community

部分在局部视图中不起作用,这是设计使然。您可以使用 some custom helpers 来实现类似的行为,但老实说,包含必要的脚本是视图的责任,而不是部分的责任。我建议使用主视图的 @scripts 部分来执行此操作,而不是让部分担心脚本。


但是,如果脚本非常特定于部分怎么办?在局部而不是视图中定义它是否具有逻辑意义?
@Darin:我不同意。 DRY 原理呢?我不喜欢重复自己,即使它只是脚本引用。
@fretje,每个人都有权就这个话题发表意见。我尊重你的。在我的回答中,我表达了我的观点并链接到一个可以让你完成这项任务的答案。但我也强调了我会针对这种情况推荐和做的事情。
这对我来说完全没有意义。当然,部分视图应该并且应该具有关联的逻辑,因此是特定于它们的 javascript 文件,而不是它们的父视图。
支持@JoshNoe 和其他人——“小部件”(显示 + 丰富的交互)是与相关 javascript 紧密耦合的局部视图的完美示例。按照设计,我不应该在不同的地方写两个包含语句来获得完整的功能,因为显示永远不会没有伴随的交互,并且交互永远不会出现在其他地方。
U
Uwe Keim

这是一个非常受欢迎的问题,所以我将发布我的解决方案。

我遇到了同样的问题,虽然它并不理想,但我认为它实际上工作得很好,并且不会使部分依赖于视图。

我的场景是一个动作可以自己访问,但也可以嵌入到一个视图中——一个谷歌地图。

在我的 _layout 中,我有:

@RenderSection("body_scripts", false)

在我的 index 视图中,我有:

@Html.Partial("Clients")
@section body_scripts
{
    @Html.Partial("Clients_Scripts")
}

在我的 clients 视图中,我有(所有地图和 assoc.html):

@section body_scripts
{
    @Html.Partial("Clients_Scripts")
}

我的 Clients_Scripts 视图包含要呈现到页面上的 javascript。

这样,我的脚本就被隔离了,并且可以在需要的地方呈现到页面中,body_scripts 标记仅在 razor 视图引擎找到它的第一次出现时呈现。

这让我把所有东西都分开了——这是一个对我来说效果很好的解决方案,其他人可能会遇到问题,但它确实修补了“按设计”的漏洞。


我不是反对你的人,但我会说我不太喜欢这个解决方案,因为它仍然将视图特定的脚本与视图本身分开。
其他 20 个人和我有不同的看法。您仍然可以在单独的文件中拥有与视图直接相关的脚本,如果您没有将脚本与视图一起包含,则会出现编程错误。将其放在单独的文件中可以将交互与演示分开,并且可以从单独的文件中获得大量其他好处。
你完全正确。我实际上完全同意并且个人更喜欢这种方法。对我来说,真正的问题是我的同事们都在为这种分离而苦苦挣扎。不过,这是一个领域问题。我认为这种方法是理想的,尤其是当您考虑到 JavaScript 构建过程时。我将继续努力教育我的同事使用这种方法,并完全支持它。不过,我确实认为您的答案可以改进。不过,您无需提及“ 20人同意”。仅仅因为答案很受欢迎,并不总是意味着它是正确的。在这种情况下是对的。
非常正确,我总是很乐意接受建设性的反馈并修改我自己的代码和答案,如果有改进的话:)
该解决方案还有一个额外的好处,即仍然能够执行您期望在典型视图中能够执行的所有 MVC 式内容,例如能够对传入的模型进行 JSON 编码并使用 Url 生成 URL。行动。这种方法是设置 AngularJS 控制器的一种优雅方式——每个局部视图都可以代表 Angular 模块中的一个单独的控制器。好干净!
C
Community

this thread 中的解决方案中,我想出了以下可能过于复杂的解决方案,它可以让您延迟在 using 块中呈现任何 html(也包括脚本)。

用法

创建“部分”

典型场景:在局部视图中,无论局部视图在页面中重复多少次,都只包含一次块:@using (Html.Delayed(isOnlyOne: "some unique name for this section")) { } 在局部视图中,每次使用局部时都包含块:@using (Html.Delayed()) { show me multiple times, @Model.Whatever }局部视图,无论局部重复多少次,只包含一次块,但稍后按名称具体呈现它 when-i-call-you: @using (Html.Delayed("when-i-call-you", isOnlyOne: "不同的唯一名称")) { 按名称显示一次 @Model.First().Value }

渲染“部分”

(即在父视图中显示延迟部分)

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


}

哇,我理解代码甚至很复杂,但是 +1 提出了一个解决方案
@RameezAhmedSayad 你是对的——回到这里,即使我对我的意思是如何使用它感到困惑。更新答案...
并进一步澄清 - 有两个“名称”的原因是,如果您只希望它在需要参数 isOnlyOne 中的唯一键时呈现,但只有当您想按名称在特定位置呈现它时,您才提供标识符,否则将在 Html.RenderDelayed() 处转储。
我个人认为没有必要购买麻烦并使用这种方法,部分视图中的部分根本不需要,因为它可以被消除,并且脚本可以在不定义部分的情况下进入那里。这是因为这是在外部渲染的,如果您看到渲染页面的代码,您只会注意到局部视图的代码在那里不可见。因此,如果这是更好的组织等问题,那根本不会产生任何影响。
@Transcendent“辩论”已经在对已接受答案的评论中开始stackoverflow.com/a/7556594/1037948
S
Serj Sagan

如果您确实有从 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>

我试过@drzaus,它需要'SeeIfReady',否则它不起作用。
a
archil

遵循 unobtrusive 原则,“_myPartial”不需要将内容直接注入脚本部分。您可以将这些部分视图脚本添加到单独的 .js 文件中,并从父视图将它们引用到 @scripts 部分。


如果页面中根本没有呈现局部视图会发生什么?我们是否仍然在 parent 中引用那些 .js 文件并使其重载?
a
alans

OP 的目标是他想将内联脚本定义到他的部分视图中,我假设该脚本仅特定于该部分视图,并将该块包含在他的脚本部分中。

我知道他想让那个局部视图是自包含的。这个想法类似于使用 Angular 时的组件。

我的方法是将脚本原样保留在部分视图中。现在的问题是当调用部分视图时,它可能会在所有其他脚本之前执行其中的脚本(通常添加到布局页面的底部)。在这种情况下,您只需让部分视图脚本等待其他脚本。有几种方法可以做到这一点。我之前使用过的最简单的方法是在 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>

然后在我的局部视图(底部):

<script>
  (function(){
    document.querySelector('body').addEventListener('scriptsLoaded', function() {

      // .. do your thing here

    });
  })();
</script>

另一种解决方案是使用堆栈来推送所有脚本,并在最后调用每个脚本。如前所述,其他解决方案是 RequireJS/AMD 模式,它也非常有效。


很好,这很聪明。
M
Mr. Baudin

我们对 Web 的看法存在一个根本性缺陷,尤其是在使用 MVC 时。缺陷在于 JavaScript 在某种程度上是视图的责任。视图是视图,JavaScript(行为或其他)是 JavaScript。在 Silverlight 和 WPF 的 MVVM 模式中,我们面临“视图优先”或“模型优先”。在 MVC 中,我们应该始终尝试从模型的角度进行推理,而 JavaScript 在许多方面都是该模型的一部分。

我建议使用 AMD 模式(我自己喜欢 RequireJS)。将您的 JavaScript 分离到模块中,定义您的功能并从 JavaScript 挂钩到您的 html,而不是依赖视图来加载 JavaScript。这将清理您的代码,分离您的顾虑并让生活更轻松。


大概两三个月左右,我一直在使用 RequireJS,而且我认为我不会在没有 RequireJS 的情况下开发另一个 Web 应用程序。
JavaScript 也可以是 View 的职责。
使用 AMD 模式是个好主意,但我不同意你关于 JavaScript 是模型的一部分的断言。它通常用于定义视图行为,尤其是与诸如 Knockout 之类的东西结合使用时。您将模型的 JSON 表示形式转储到 JavaScript 视图中。就个人而言,我只使用闭包,即 window 对象上的自定义“命名空间”,并在任何部分之前包含库脚本。
我认为这里有一个误解。在开发大多数 Web 应用程序时,我们实际上是在开发两个应用程序:一个在服务器上运行,一个在客户端上运行。从服务器的角度来看,您发送到浏览器的任何内容都是“视图”。从这个意义上说,JavaScript 是视图的一部分。从客户端应用程序的角度来看,纯 HTML 是视图,而 JS 是与服务器 MVC 术语中的 M 和 C 平行的代码。我想这就是人们在这里不同意的原因。
通常有一小段 javascript 与视图紧密耦合,将它们放在一起在组织和维护方面都非常有意义。这是常识,但不是因为性能考虑要求我们在网页末尾而不是开头包含 javascript(这是问题的根源......我们甚至不需要特殊的“部分”用于脚本,否则)。因此,该问题与您建议的包含 javascript 的视图无关。在适当的情况下,这绝对没有错。
J
Julio Spader

您不需要在局部视图中使用部分。

包括在您的局部视图中。它在 jQuery 加载后执行该函数。您可以更改代码的 de 条件子句。

<script type="text/javascript">    
var time = setInterval(function () {
    if (window.jQuery != undefined) {
        window.clearInterval(time);

        //Begin
        $(document).ready(function () {
           //....
        });
        //End
    };
}, 10); </script>

胡里奥·斯派德


p
purvin

这对我有用,允许我将 javascript 和 html 放在同一文件中以获取部分视图。有助于思考过程以在同一局部视图文件中查看 html 和相关部分。

在使用名为“_MyPartialView.cshtml”的局部视图的视图中

<div>
    @Html.Partial("_MyPartialView",< model for partial view>,
            new ViewDataDictionary { { "Region", "HTMLSection" } } })
</div>

@section scripts{

    @Html.Partial("_MyPartialView",<model for partial view>, 
                  new ViewDataDictionary { { "Region", "ScriptSection" } })

 }

在部分视图文件中

@model SomeType

@{
    var region = ViewData["Region"] as string;
}

@if (region == "HTMLSection")
{


}

@if (region == "ScriptSection")
{
        <script type="text/javascript">
    </script">
}

I
Iridio

我能想到的第一个解决方案是使用 ViewBag 来存储必须呈现的值。

Onestly我从来没有尝试过这是否可以从部分角度来看,但它应该是imo。


刚试过;遗憾的是,这不起作用(在主页顶部创建了一个 ViewBag.RenderScripts = new List<string>();,然后称为 @Html.Partial("_CreateUpdatePartial",Model,ViewData),然后放入 @section Scripts {@foreach (string script in ViewBag.RenderScripts) Scripts.Render(script); }}。在部分视图中,我放入了 @{ViewBag.RenderScripts = ViewBag.RenderScripts ?? new List<string>();ViewBag.RenderScripts.Add("~/bundles/jquery");}
L
Lomak

您可以使用这些扩展方法:(另存为 PartialWithScript.cs)

namespace System.Web.Mvc.Html
{
    public static class PartialWithScript
    {
        public static void RenderPartialWithScript(this HtmlHelper htmlHelper, string partialViewName)
        {
            if (htmlHelper.ViewBag.ScriptPartials == null)
            {
                htmlHelper.ViewBag.ScriptPartials = new List<string>();
            }

            if (!htmlHelper.ViewBag.ScriptPartials.Contains(partialViewName))
            {
                htmlHelper.ViewBag.ScriptPartials.Add(partialViewName);
            }

            htmlHelper.ViewBag.ScriptPartialHtml = true;
            htmlHelper.RenderPartial(partialViewName);
        }

        public static void RenderPartialScripts(this HtmlHelper htmlHelper)
        {
            if (htmlHelper.ViewBag.ScriptPartials != null)
            {
                htmlHelper.ViewBag.ScriptPartialHtml = false;
                foreach (string partial in htmlHelper.ViewBag.ScriptPartials)
                {
                    htmlHelper.RenderPartial(partial);
                }
            }
        }
    }
}

像这样使用:

部分示例: (_MyPartial.cshtml) 将 html 放在 if 中,将 js 放在 else 中。

@if (ViewBag.ScriptPartialHtml ?? true)
    <p>I has htmls</p>
}
else {
    <script type="text/javascript">
        alert('I has javascripts');
    </script>
}

在您的 _Layout.cshtml 中,或者您希望渲染部分脚本的任何地方,放置以下内容(一次):它将仅在此位置呈现当前页面上所有部分的 javascript。

@{ Html.RenderPartialScripts(); }

然后要使用您的部分,只需执行以下操作:它将仅在此位置呈现 html。

@{Html.RenderPartialWithScript("~/Views/MyController/_MyPartial.cshtml");}

P
Pluto

有一种方法可以在局部视图中插入部分,尽管它并不漂亮。您需要从父视图访问两个变量。由于部分视图的部分目的是创建该部分,因此需要这些变量是有意义的。

下面是在局部视图中插入一个部分的样子:

@model KeyValuePair<WebPageBase, HtmlHelper>
@{
    Model.Key.DefineSection("SectionNameGoesHere", () =>
    {
        Model.Value.ViewContext.Writer.Write("Test");
    });
}

并在插入部分视图的页面中......

@Html.Partial(new KeyValuePair<WebPageBase, HtmlHelper>(this, Html))

您还可以使用此技术在任何类中以编程方式定义节的内容。

享受!


您能否提供一个完整工作项目的链接?
P
PaulSanS

冥王星的想法以更好的方式:

CustomWebViewPage.cs:

    public abstract class CustomWebViewPage<TModel> : WebViewPage<TModel> {

    public IHtmlString PartialWithScripts(string partialViewName, object model) {
        return Html.Partial(partialViewName: partialViewName, model: model, viewData: new ViewDataDictionary { ["view"] = this, ["html"] = Html });
    }

    public void RenderScriptsInBasePage(HelperResult scripts) {
        var parentView = ViewBag.view as WebPageBase;
        var parentHtml = ViewBag.html as HtmlHelper;
        parentView.DefineSection("scripts", () => {
            parentHtml.ViewContext.Writer.Write(scripts.ToHtmlString());
        });
    }
}

视图\web.config:

<pages pageBaseType="Web.Helpers.CustomWebViewPage">

看法:

@PartialWithScripts("_BackendSearchForm")

部分(_BackendSearchForm.cshtml):

@{ RenderScriptsInBasePage(scripts()); }

@helper scripts() {
<script>
    //code will be rendered in a "scripts" section of the Layout page
</script>
}

布局页面:

@RenderSection("scripts", required: false)

B
BlackjacketMack

使用 Mvc Core,您可以创建一个整洁的 TagHelper scripts,如下所示。这可以很容易地变成一个 section 标记,您也可以在其中给它一个名称(或者名称取自派生类型)。请注意,需要为 IHttpContextAccessor 设置依赖注入。

添加脚本时(例如在部分中)

<scripts>
    <script type="text/javascript">
        //anything here
    </script>
</scripts>

输出脚本时(例如在布局文件中)

<scripts render="true"></scripts>

代码

public class ScriptsTagHelper : TagHelper
    {
        private static readonly object ITEMSKEY = new Object();

        private IDictionary<object, object> _items => _httpContextAccessor?.HttpContext?.Items;

        private IHttpContextAccessor _httpContextAccessor;

        public ScriptsTagHelper(IHttpContextAccessor httpContextAccessor)
        {
            _httpContextAccessor = httpContextAccessor;
        }

        public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
        {
            var attribute = (TagHelperAttribute)null;
            context.AllAttributes.TryGetAttribute("render",out attribute);

            var render = false;

            if(attribute != null)
            {
                render = Convert.ToBoolean(attribute.Value.ToString());
            }

            if (render)
            {
                if (_items.ContainsKey(ITEMSKEY))
                {
                    var scripts = _items[ITEMSKEY] as List<HtmlString>;

                    var content = String.Concat(scripts);

                    output.Content.SetHtmlContent(content);
                }
            }
            else
            {
                List<HtmlString> list = null;

                if (!_items.ContainsKey(ITEMSKEY))
                {
                    list = new List<HtmlString>();
                    _items[ITEMSKEY] = list;
                }

                list = _items[ITEMSKEY] as List<HtmlString>;

                var content = await output.GetChildContentAsync();

                list.Add(new HtmlString(content.GetContent()));
            }
        }
    }

P
Practical Programmer

我今天遇到了这个问题。我将添加一个使用 <script defer> 的解决方法,因为我没有看到其他答案提到它。

//on a JS file somewhere (i.e partial-view-caller.js)
(() => <your partial view script>)();

//in your Partial View
<script src="~/partial-view-caller.js" defer></script>

//you can actually just straight call your partial view script living in an external file - I just prefer having an initialization method :)

上面的代码是我对这个问题所做的quick post的摘录。


R
Rick Love

我解决了这个完全不同的路线(因为我很着急,不想实现一个新的 HtmlHelper):

我将部分视图包裹在一个大的 if-else 语句中:

@if ((bool)ViewData["ShouldRenderScripts"] == true){
// Scripts
}else{
// Html
}

然后,我使用自定义 ViewData 调用了 Partial 两次:

@Html.Partial("MyPartialView", Model, 
    new ViewDataDictionary { { "ShouldRenderScripts", false } })

@section scripts{
    @Html.Partial("MyPartialView", Model, 
        new ViewDataDictionary { { "ShouldRenderScripts", true } })
}

当然,整个想法是部分视图的消费者不需要知道它必须包含脚本,这有点问题吗?否则你也可以直接说@Html.Partial("MyPartialViewScripts")
不,这个想法是允许在与 html 相同的文档中定义脚本,但我同意这并不理想。
J
John M

我有一个类似的问题,我有一个母版页如下:

@section Scripts {
<script>
    $(document).ready(function () {
        ...
    });
</script>
}

...

@Html.Partial("_Charts", Model)

但部分视图依赖于脚本部分中的一些 JavaScript。我通过将部分视图编码为 JSON,将其加载到 JavaScript 变量中,然后使用它来填充 div 来解决它,所以:

@{
    var partial = Html.Raw(Json.Encode(new { html = Html.Partial("_Charts", Model).ToString() }));
}

@section Scripts {
<script>
    $(document).ready(function () {
        ...
        var partial = @partial;
        $('#partial').html(partial.html);
    });
</script>
}

<div id="partial"></div>

IMO 你应该通过将你的 JS 移动到一个单独的文件中来解决这个问题。
R
RogerEdward

您可以选择使用您的 Folder/index.cshtml 作为母版页,然后添加部分脚本。然后,在您的布局中,您有:

@RenderSection("scripts", required: false) 

和你的 index.cshtml:

@section scripts{
     @Scripts.Render("~/Scripts/file.js")
}

它会处理你所有的部分视图。它对我有用


u
user3717478

我的解决方案是从布局页面加载脚本。然后在 javacript 中,检查 parial 视图中是否存在元素之一。如果该元素存在,则 javascript 知道该部分已包含在内。

$(document).ready(function () {
    var joinButton = $("#join");
    if (joinButton.length != 0) {
        // the partial is present
        // execute the relevant code
    }
});

C
CShark

好吧,我猜其他海报为您提供了一种在您的部分中直接包含@section 的方法(通过使用第 3 方 html 助手)。

但是,我认为,如果您的脚本与您的部分紧密耦合,只需将您的 javascript 直接放在您的部分内的内联 <script> 标记中并完成它(请注意脚本重复如果您打算在单个视图中多次使用局部);


这通常并不理想,因为 jQuery 等的加载会在内联脚本之后发生......但对于本机代码,我想这很好。
u
user4298890

假设您有一个名为 _contact.cshtml 的局部视图,您的联系人可以是法律(姓名)或物理主体(名字、姓氏)。您的视图应该注意渲染的内容以及可以使用 javascript 实现的内容。因此可能需要延迟渲染和 JS 内视图。

我认为,如何省略它的唯一方法是,当我们创建一种不显眼的方式来处理此类 UI 问题时。

另请注意,MVC 6 将有一个所谓的视图组件,甚至 MVC 期货也有一些类似的东西,Telerik 也支持这样的东西......


迟到了 3 年,我认为这根本不能回答这个问题?你想在这里说什么? 3 年后用未来技术的推测性特征回答问题并不是真正的答案或特别有用
a
anaximander

我刚刚在我的局部视图中添加了这段代码并解决了这个问题,虽然不是很干净,但它可以工作。您必须确保您正在渲染的对象的 Id。

<script>
    $(document).ready(function () {
        $("#Profile_ProfileID").selectmenu({ icons: { button: 'ui-icon-circle-arrow-s' } });
        $("#TitleID_FK").selectmenu({ icons: { button: 'ui-icon-circle-arrow-s' } });
        $("#CityID_FK").selectmenu({ icons: { button: 'ui-icon-circle-arrow-s' } });
        $("#GenderID_FK").selectmenu({ icons: { button: 'ui-icon-circle-arrow-s' } });
        $("#PackageID_FK").selectmenu({ icons: { button: 'ui-icon-circle-arrow-s' } });
    });
</script>

P
Pouria Jafari

我有类似的问题用这个解决了它:

@section ***{
@RenderSection("****", required: false)
}

我猜那是一种很好的注入方式。