【问题标题】:Injecting content into specific sections from a partial view ASP.NET MVC 3 with Razor View Engine使用 Razor 视图引擎将内容从局部视图 ASP.NET MVC 3 注入特定部分
【发布时间】:2011-11-25 06:20:48
【问题描述】:

我在_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 部分中注入一些内容。

我该怎么做?

【问题讨论】:

标签: asp.net asp.net-mvc asp.net-mvc-3 razor partial-views


【解决方案1】:

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

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

【讨论】:

  • 刚试过;遗憾的是这不起作用(在主页顶部创建了一个ViewBag.RenderScripts = new List&lt;string&gt;();,然后调用@Html.Partial("_CreateUpdatePartial",Model,ViewData),然后输入@section Scripts {@foreach (string script in ViewBag.RenderScripts) Scripts.Render(script); }}。在部分视图中,我输入了@{ViewBag.RenderScripts = ViewBag.RenderScripts ?? new List&lt;string&gt;();ViewBag.RenderScripts.Add("~/bundles/jquery");}
【解决方案2】:

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

【讨论】:

  • 如果页面中根本没有呈现局部视图会发生什么?我们是否仍然在 parent 中引用那些 .js 文件并使其重载?
【解决方案3】:

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

【讨论】:

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

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

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

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

在我的_layout 我有:

@RenderSection("body_scripts", false)

在我的index 视图中,我有:

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

在我的clients 视图中,我拥有(所有地图和关联的html):

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

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

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

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

【讨论】:

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

我们对 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 的视图无关。在适当的情况下,这绝对没有错。
【解决方案6】:

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

用法

创建“部分”

  1. 典型场景:在局部视图中,无论局部视图在页面中重复多少次,都只包含一次块:

    @using (Html.Delayed(isOnlyOne: "some unique name for this section")) {
        <script>
            someInlineScript();
        </script>
    }
    
  2. 在局部视图中,每次使用局部时都包含块:

    @using (Html.Delayed()) {
        <b>show me multiple times, @Model.Whatever</b>
    }
    
  3. 在局部视图中,无论重复多少次,只包含一次块,但稍后会通过名称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));
    }


}

【讨论】:

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

如果您确实有从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',否则它不起作用。
【解决方案8】:

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

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

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

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

胡里奥·斯派德

【讨论】:

    【解决方案9】:

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

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

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

    并在页面中插入局部视图...

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

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

    享受吧!

    【讨论】:

    • 能否请您提供一个完整工作项目的链接?
    【解决方案10】:

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

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

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

    【讨论】:

    • 晚了 3 年,我认为这根本无法回答问题?你想在这里说什么? 3 年后用未来技术的推测性特征回答问题并不是真正的答案或特别有用
    【解决方案11】:

    我解决了这个完全不同的路线(因为我很着急,不想实现一个新的 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 相同的文档中定义脚本,但我同意这并不理想。
    【解决方案12】:

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

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

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

    【讨论】:

      【解决方案13】:

      您可以使用这些扩展方法:(另存为 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");}
      

      【讨论】:

        【解决方案14】:

        我遇到了类似的问题,我的母版页如下:

        @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 移动到一个单独的文件中来解决这个问题。
        【解决方案15】:

        好吧,我猜其他发帖者已经为您提供了一种在您的部分内容中直接包含 @section 的方法(通过使用 3rd 方 html 帮助程序)。

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

        【讨论】:

        • 这通常并不理想,因为 jQuery 等的加载会在内联脚本之后发生......但对于本机代码,我想这很好。
        【解决方案16】:

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

        @RenderSection("scripts", required: false) 
        

        还有你的 index.cshtml:

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

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

        【讨论】:

          【解决方案17】:

          冥王星的想法更好:

          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)
          

          【讨论】:

            【解决方案18】:

            这对我有用,允许我将 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">
            }
            

            【讨论】:

              【解决方案19】:

              使用 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()));
                          }
                      }
                  }
              

              【讨论】:

                【解决方案20】:

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

                【讨论】:

                  【解决方案21】:

                  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 模式,它也非常有效。

                  【讨论】:

                  • 很好,这很聪明。
                  【解决方案22】:

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

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

                  【讨论】:

                    【解决方案23】:

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

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

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 1970-01-01
                      • 2011-05-02
                      • 1970-01-01
                      相关资源
                      最近更新 更多