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


324

我在我的部分中定义了此部分 _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局部视图的部分中注入一些内容。

我怎样才能做到这一点?


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

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

1
@RussCam NuGet软件包不是解决方案,可能是该软件包的代码。
马克西姆六世。

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

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

Answers:


235

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


445
但是,如果脚本非常特定于部分脚本怎么办?在局部视图而不是视图中定义它在逻辑上是不合理的吗?
耶斯(Jez)2012年

43
为什么是设计使然?
Shimmy Weitzhandler,2012年

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

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

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

83

这是一个很受欢迎的问题,因此我将发布解决方案。
我遇到了同样的问题,尽管它不是理想的,但我认为它实际上工作得很好,并且不会部分依赖于视图。
我的情况是,动作本身可以访问,但也可以嵌入到视图中-谷歌地图。

在我的_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标记仅在剃刀视图引擎找到它的第一次出现时才呈现。

这使我可以将所有内容分开-这对我来说是一种很好的解决方案,其他人可能对此有疑问,但是确实解决了“设计使然”的漏洞。


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

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

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

非常正确,如果有改进,我总是很乐意接受建设性的反馈意见,并更改我自己的代码并回答:)
dan richardson

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

40

通过该线程中的解决方案,我提出了以下可能过于复杂的解决方案,该解决方案使您可以延迟在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
哇,我对代码的理解甚至很复杂,但是想出解决方案却需要+1
Rameez Ahmed Sayad 2014年

@RameezAhmedSayad你是对的-即使我对我要说的用法感到困惑也回到了这里。更新答案...
drzaus

进一步说明-之所以有两个“名称”,是因为如果您只希望一次渲染它需要参数中的唯一键isOnlyOne,但是仅当您希望按名称在特定位置渲染它时,才提供标识符,否则将被丢弃在Html.RenderDelayed()
drzaus

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

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

16

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

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

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


9

如果你有正当理由需要运行一些jspartial,这里是你如何能做到这一点,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

8

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


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

5

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

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


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

6
JavaScript也可以是View的责任。
凯尔门(Kelmen),

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

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

3

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


2

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

迄今为止,我从来没有尝试过从局部角度进行这项工作,但是应该做到这一点。


刚刚尝试过;可悲的是不工作(创造了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");}
JohnLBevan

2

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

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

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

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

朱利奥·斯派德


2

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

1

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

这是在部分视图中插入节的样子:

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

然后在页面中插入部分视图...

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

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

请享用!


1
您能取一个完整的项目链接吗?
Ehsan Zargar Ershadi

1

冥王星的想法更好:

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

部分(_BackendSearchForm.cshtml):

@{ RenderScriptsInBasePage(scripts()); }

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

布局页面:

@RenderSection("scripts", required: false)

1

这为我工作,使我可以将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">
}

0

我解决了一条完全不同的路线(因为我很着急,不想实现新的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")
dan richardson

不,其想法是允许将脚本与html定义在同一文档中,但是我同意这不是理想的选择。
里克·洛夫

0

我有一个类似的问题,我在主页上有以下内容:

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

0

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

@RenderSection("scripts", required: false) 

和您的index.cshtml:

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

它将遍及您的所有局部视图。它对我有用


0

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

-1

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

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


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

-3

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

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

还请注意,MVC 6将具有所谓的“视图组件”,即使MVC期货也具有类似的功能,Telerik也支持这种功能。


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

-3

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.