我已经在我的_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>


我需要从Scripts局部视图的_myPartial部分中插入一些内容。

我该怎么做?

评论

对于以后再来的人-有一个处理此问题的nuget软件包:nuget.org/packages/Forloop.HtmlHelpers

@RussCam您应该回答这个问题。 +1 nuget包解决了OP所遇到的确切问题。

@RussCam NuGet软件包不是解决方案,可能是该软件包的代码。

@MaksimVi。好吧,我写了nuget软件包,无意删除它,所以不要重复代码(bitbucket.org/forloop/forloop-htmlhelpers/src)或Wiki(bitbucket.org/forloop/forloop-htmlhelpers/wiki / Home),作为注释的链接一直保持在stackoverflow IMO的精神之内。

这是另一个看起来非常不错的解决方案:stackoverflow.com/questions/5355427/…

#1 楼

部分无法在局部视图中使用,那是设计使然。您可以使用一些自定义助手来实现类似的行为,但是老实说,包括必要的脚本是视图的责任,而不是部分的责任。我建议使用主视图的@scripts部分来执行此操作,而不必担心脚本的局部问题。

评论


但是,如果脚本非常特定于部分脚本怎么办?在局部视图而不是视图中定义它不是逻辑上的意义吗?

–杰兹
2012年10月25日14:57

为什么是设计使然?

– Shimmy Weitzhandler
2012年11月26日10:12

@Darin:我不同意。 DRY原理呢?我不想重复自己,即使只是脚本参考。

–fretje
2012年12月7日15:56

@fretje,每个人都有权表达他对该主题的看法。我尊重你的在我的回答中,我表达了我的观点,并链接到可以帮助您完成此任务的答案。但我也强调了针对这种情况的建议和建议。

–达琳·迪米特洛夫(Darin Dimitrov)
2012年12月7日在16:41



其次是@JoshNoe和其余部分-一个“小部件”(显示+丰富的交互)是紧密关联于相关javascript的部分视图的完美示例。通过设计,我不必在不同的地方编写两个include语句即可获得全部功能,因为在没有伴随的交互的情况下显示永远不会消失,并且交互也永远不会在其他地方出现。

–drzaus
2013年9月12日18:51



#2 楼

这是一个很受欢迎的问题,所以我将发布解决方案。
我遇到了同样的问题,虽然它不是理想的,但我认为它实际上工作得很好,并且不会部分依赖于视图。
我的情况是,动作本身可以访问,但也可以嵌入到视图中-谷歌地图。
在我的_layout中,我有:
@RenderSection("body_scripts", false)

我拥有的index视图:
我拥有的clients视图(所有地图和assoc。html):
@Html.Partial("Clients")
@section body_scripts
{
    @Html.Partial("Clients_Scripts")
}

我的Clients_Scripts视图包含要呈现到页面。
这样,我的脚本就被隔离了,并且可以在需要的情况下呈现到页面中,body_scripts标签仅在剃刀视图引擎找到它的第一次出现时才呈现。
这让我拥有了一切分开-对我来说,这是一个很好的解决方案,其他人可能对此有疑问,但是确实解决了“按设计”漏洞。

评论


我并不是拒绝您投票的人,但是我会说我不太喜欢这种解决方案,因为它仍然将特定于视图的脚本与视图本身分开。

–暗恋
2015年2月13日,1:19

其他20个人和我有不同的看法。您仍然可以将与视图直接相关的脚本保存在单独的文件中,如果您不随视图一起包含脚本,则这是编程错误。将其保存在单独的文件中可将交互与演示分开,并从单独的文件中获得大量其他好处。

–丹·理查森(dan richardson)
15年2月13日在9:15

你是完全正确的。我实际上完全同意并更喜欢这种方法。对我而言,真正的问题是我的同事们在这种分离上挣扎。不过,这是一个域问题。我认为这种方法是理想的,特别是在考虑了JavaScript构建过程之后。我将继续努力教育我的同事使用这种方法,并全力支持它。我确实认为您的答案可以改善。您无需提及“ 20个人同意”。仅仅因为答案很流行,并不总是意味着答案正确。在这种情况下是正确的。

–暗恋
2015年2月13日在16:08



是的,我总是很乐意接受建设性的反馈意见,并修改自己的代码并回答是否有改进的地方:)

–丹·理查森(dan richardson)
15年2月17日在9:57

该解决方案的另一个好处是,它仍然能够完成您在典型的View中可能希望完成的所有MVC风格的工作,例如能够对传入的Model进行JSON编码并使用Url生成URL。行动。然后,这种方法是设置AngularJS控制器的一种优雅方法-每个局部视图都可以在Angular模块中代表一个单独的控制器。好干净!

–丹
16 Dec 16'在2:43

#3 楼

从该线程的解决方案中,我提出了以下可能过于复杂的解决方案,该解决方案使您可以延迟在using块中呈现任何html(脚本)。

USAGE

创建“部分”



典型情况:在局部视图中,无论页面中重复执行局部视图多少次,都只包含一次块:

@using (Html.Delayed(isOnlyOne: "some unique name for this section")) {
    <script>
        someInlineScript();
    </script>
}



在局部视图中,每次使用局部时都包括该块:

@using (Html.Delayed()) {
    <b>show me multiple times, @Model.Whatever</b>
}


/>
在局部视图中,无论局部重复了多少次,都仅包含该块一次,但后来用名称when-i-call-you专门对其进行渲染:

@using (Html.Delayed("when-i-call-you", isOnlyOne: "different unique name")) {
    <b>show me once by name</b>
    <span>@Model.First().Value</span>
}



呈现“节”

(即在父视图中显示延迟的节)

@Html.RenderDelayed(); // writes unnamed sections (#1 and #2, excluding #3)
@Html.RenderDelayed("when-i-call-you", false); // writes the specified block, and ignore the `isOnlyOne` setting so we can dump it again
@Html.RenderDelayed("when-i-call-you"); // render the specified block by name
@Html.RenderDelayed("when-i-call-you"); // since it was "popped" in the last call, won't render anything due to `isOnlyOne` provided in `Html.Delayed`


CODE

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

–拉梅斯·艾哈迈德·沙耶德(Rameez Ahmed Sayad)
14-10-19在21:53

@RameezAhmedSayad你是对的-即使我对如何使用它感到困惑也回到了这里。正在更新答案...

–drzaus
15年8月14日在17:23

进一步说明-有两个“名称”的原因是,如果只希望一次渲染它需要参数isOnlyOne中的唯一键,但是仅当您希望按名称在特定位置渲染它时,才提供标识符,否则会将其转储到Html.RenderDelayed()。

–drzaus
15年8月14日在17:46

我个人认为不需要购买麻烦并使用这种方法,完全不需要部分视图中的该部分,因为可以消除它,并且脚本可以在不定义部分的情况下到达该部分。这是因为它是从外部渲染的,如果您看到渲染页面的代码,您只会注意到部分视图的代码在此处不可见。因此,如果这是更好的组织等问题,那将完全没有任何效果。

–超然
2015年12月28日15:01



@transcendent的“辩论”已经在接受的答案的注释中开始stackoverflow.com/a/7556594/1037948

–drzaus
15年12月28日在16:08

#4 楼

我遇到了这个问题,并使用了这项技术。

它是我发现的最好的解决方案,它非常灵活。

也请在这里投票以添加对累积节声明的支持

#5 楼

如果您确实有必要从js运行一些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>


评论


我尝试了@drzaus,它需要'SeeIfReady'或不起作用。

– Cacho Santa
2014年6月3日22:16

#6 楼

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

评论


如果在页面中根本没有呈现部分视图,将会发生什么?我们是否仍在父级中引用这些.js文件并使其过载?

– Murali Murugesan
13年2月10日在17:15

#7 楼

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

我建议使用AMD模式(我自己喜欢RequireJS)。将模块中的JavaScript分开,定义功能并从JavaScript插入html,而不是依赖于视图来加载JavaScript。这将清理代码,使您的关注点分离,并使生活变得一帆风顺。

评论


在大约两三个月左右的时间内,我正在使用RequireJS,而且我认为没有RequireJS不会开发另一个Web应用程序。

–tugberk
13年6月18日在10:00

JavaScript也可以是View的责任。

–凯尔门
13年7月13日在4:39

使用AMD模式是一个好主意,但我不同意您的说法JavaScript是该模型的一部分。通常是定义View行为,尤其是在与诸如Knockout之类的东西结合使用时。您将模型的JSON表示转储到JavaScript视图中。就我个人而言,我只是在窗口对象上使用闭包(自定义“命名空间”),并在任何部分之前包含库脚本。

–暗恋
2015年2月13日在1:22



我认为这是一个误会。在开发大多数Web应用程序时,实际上是在开发两个应用程序:一个在服务器上运行,另一个在客户端上运行。从服务器的角度来看,您发送到浏览器的所有内容都是“视图”。从这个意义上讲,JavaScript是视图的一部分。从客户端应用程序的角度来看,纯HTML是视图,而JS是与服务器的MVC术语并行的M和C。我认为这就是人们在这里不同意的原因。

– TheAgent
19年9月9日在14:47

通常,有很多JavaScript小段与视图紧密结合,并且在组织上和维护上将它们组合在一起都是很有意义的。这将是常识,但并非如此,原因是出于性能方面的考虑,我们在网页的末尾而不是开头包含了javascript(这是问题的根源……我们甚至不需要特殊的“部分中的脚本)。因此,此问题与您建议的包含javascript的视图无关。在正确的情况下,这绝对没有错。

– BVernon
10月30日21:18

#8 楼

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

我知道他想让部分视图自成一体。这个想法类似于使用Angular时的组件。

我的方法是将脚本原样保留在Partial View中。现在,问题在于调用Partial View时,它可能在所有其他脚本(通常添加到布局页面底部)之前在其中执行脚本。在这种情况下,您只需让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>


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

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

      // .. do your thing here

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


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

评论


很好,这很聪明。

–埃里克·朗斯特里特
9月1日下午2:29

#9 楼

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

我从来没有尝试过从局部角度来看这项工作,但是应该可以。

评论


刚刚尝试过;遗憾的是,这不起作用(在主页顶部创建了ViewBag.RenderScripts = new List ();,然后称为@ Html.Partial(“ _ CreateUpdatePartial”,Model,ViewData),然后放置了@section脚本{@foreach(ViewBag.RenderScripts中的字符串脚本)Scripts.Render(script);}}。在部分视图中,我将@ {ViewBag.RenderScripts = ViewBag.RenderScripts ?? new List (); ViewBag.RenderScripts.Add (“〜/ bundles / jquery”);}。

–JohnLBevan
2014年8月29日上午11:55

#10 楼

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

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

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

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


Julio Spader

#11 楼

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


#12 楼

这对我来说很有效,允许我将javascript和html放在同一文件中以进行局部查看。帮助思考过程在同一局部视图文件中查看html和相关部分。


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

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


#13 楼

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

在部分视图中插入部分的样子如下:

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


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

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


还可以使用此技术来定义以下内容:在任何班级中以编程方式编写一个节。

享受吧!

评论


您能取一个完整的项目链接吗?

– Ehsan Zargar Ershadi
18年1月11日在9:08

#14 楼

冥王星的想法更好:

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


Views \ web.config:

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


视图:

@PartialWithScripts("_BackendSearchForm")



@{ RenderScriptsInBasePage(scripts()); }

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


版面配置页:

@RenderSection("scripts", required: false)


#15 楼

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

我将Partial View包裹在一个大的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”)

–丹·理查森(dan richardson)
15年2月24日在11:38

不,这样做的想法是允许在与html相同的文档中定义脚本,但是我同意这不是理想的选择。

–Rick Love
15年2月24日在21:20

#16 楼

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

@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移到单独的文件中来解决此问题。

–值得
16年8月2日,0:59

#17 楼

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

@RenderSection("scripts", required: false) 


和index.cshtml:

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


及其将处理您所有的局部视图。对我有用

#18 楼

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


#19 楼

前几天,我遇到了几乎相同的问题,只是部分视图是对AJAX请求的响应。在我的情况下,部分实际上是一个完整页面,但是我希望可以将其作为其他页面的一部分来访问。

如果要在部分中呈现节,则最干净的解决方案是创建新布局并使用ViewBag变量。这不适用于@Html.Partial()或新的<partial></partial>,请使用AJAX。

主视图(您想要在其他地方渲染为局部视图):

@if(ViewBag.Partial == true) {
    Layout = "_layoutPartial";
}

<div>
    [...]
</div>    

@section Scripts {
    <script type="text/javascript">
        [...]
    </script>
}


控制器:

public IActionResult GetPartial() {

    ViewBag.Partial = true;

    //Do not return PartialView!
    return View("/path/to/view")
}



@RenderSection("Scripts")
@RenderBody()


然后使用AJAX在页面中。

如果要在主布局(而不是部分布局)中呈现页面,则不要设置ViewBag.Partial = true。无需HTML帮助程序。

#20 楼

我今天有这个问题。我将添加一个使用<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 :)

上面的代码摘自我对此问题的快速帖子摘录。

#21 楼

好吧,我想其他发布者已经为您提供了一种在部分代码中直接包含@section的方法(通过使用第三方html帮助器)。

但是,我认为,如果您的脚本很严格耦合到您的局部,只需将您的javascript直接放在局部中的内联<script>标记内并完成操作(如果打算在单个视图中多次使用局部,请小心脚本重复);

评论


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

–值得
16年8月2日,0:58

#22 楼

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

我认为唯一可以忽略的方法是当我们创建一种处理此类UI问题的简单方法。

还请注意,MVC 6将具有一个所谓的“视图组件”,即使MVC期货也有类似的东西,Telerik也支持这种东西...

评论


迟到了3年,我认为这根本无法回答问题吗?您想在这里说什么?三年后用未来技术的投机功能回答一个问题并不是真正的答案或特别有帮助

–丹·理查森(dan richardson)
2014年12月8日上午11:30

#23 楼

我刚刚将此代码添加到我的局部视图中,并解决了该问题,尽管它不是很干净,但是可以正常工作。您必须确保要渲染的对象的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>


#24 楼

我也有类似的问题通过以下方法解决了:

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


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