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パッケージはソリューションではありません。パッケージのコードはソリューションである可能性があります。
Maksim Vi。

8
@MaksimVi。まあ、私はnugetパッケージを作成し、それを削除するつもりはありません。そのため、コード(bitbucket.org/forloop/forloop-htmlhelpers/src)またはwiki(bitbucket.org/forloop/forloop-htmlhelpers/wiki)を繰り返すのではなく/ Home)ここでは、コメントとしてのリンクは、stackoverflow、IMOの精神の範囲内にあります。
Russ Cam

これは非常に素晴らしいと思われる別の解決策です: stackoverflow.com/questions/5355427/…–
jkokorian

回答:


235

セクションは部分ビューでは機能しません。これは仕様によるものです。あなたは使用することができますいくつかのカスタムヘルパーを同様の動作を実現するために、正直なところ、それは必要なスクリプト、いない部分の責任を含めるために、ビューの責任です。そのためには、メインビューの@scriptsセクションを使用し、パーシャルがスクリプトを気にしないようにすることをお勧めします。


445
しかし、スクリプトがパーシャルに非常に固有である場合はどうでしょうか。ビューではなく、パーシャルで定義することは論理的な意味を持ちませんか?
Jez

43
なぜ仕様によるのですか?
Shimmy Weitzhandler

56
@ダーリン:同意しない。DRY原理についてはどうですか?たとえスクリプト参照だけであっても、自分自身を繰り返すのは好きではありません。
fretje

14
@fretje、誰もがトピックに関する彼の意見を表明する権利を持っています。私はあなたを尊重します。私の回答では、私のことを表現し、このタスクを達成できるようにする回答にリンクしています。しかし、私はこの状況に対して私が推奨し、何をするかについても強調しました。
Darin Dimitrov

33
@JoshNoeと残りの二番目-「ウィジェット」(表示+リッチインタラクション)は、関連するJavaScriptに密結合された部分ビューの完璧な例です。 意図的に、完全な機能を取得するために別の場所に2つのincludeステートメントを記述する必要はありません。これは、アテンダントの操作がないと表示が行われず、操作が他の場所に表示されないためです。
drzaus

83

これは非常に人気のある質問なので、解決策を投稿します。
私は同じ問題を抱えていましたが、理想的ではありませんが、実際には非常にうまく機能し、ビューに部分的に依存することはないと思います。
私のシナリオでは、アクションはそれ自体でアクセス可能でしたが、ビュー(Googleマップ)に埋め込むこともできました。

私の_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
私はあなたに反対票を投じた人ではありませんでしたが、それでもビュー固有のスクリプトをビュー自体から分離しているため、このソリューションはあまり好きではありません。
2015

3
他の20人と私は違う意見を持っています。別のファイルにあるビューに直接関連するスクリプトを引き続き使用できます。ビューにスクリプトを含めないと、プログラミングエラーになります。それを別のファイルに入れると、相互作用がプレゼンテーションから分離され、別のファイルに入れることで他の豊富な利点が得られます。
dan richardson、2015

1
あなたは完全に正しいです。私は実際に完全に同意し、この方法を個人的に好みます。私にとっての本当の問題は、私の同僚がこれほどの分離に苦労していることです。ただし、これはドメインの問題です。特にJavaScriptのビルドプロセスを考慮に入れると、この方法は理想的だと思います。私は、引き続きこの方法を使用するように同僚を教育し、完全にそれをサポートするよう取り組んでいきます。しかし、あなたの答えは改善されると思います。「20人が同意する」と言う必要はありませんでした。答えが人気があるからといって、必ずしも正しいとは限りません。この場合、それは正しいです。
つぶす

非常に真実であり、建設的なフィードバックを受け入れ、自分のコードを変更して、改善すべき点があれば答えるのはいつでも喜んでいます:)
dan richardson

1
このソリューションには、渡されたモデルをJSONエンコードしてURLを使用してURLを生成できるなど、通常のビューで実行できると期待されるすべてのMVC風の機能を実行できるという追加の利点があります。アクション。このアプローチは、AngularJSコントローラーをセットアップする洗練された方法です。各部分ビューは、Angularモジュールの個別のコントローラーを表すことができます。とてもきれい!
Dan

40

このスレッドのソリューションから usingブロック内のhtml(スクリプトも)のレンダリングを遅延させることができる、おそらく複雑すぎる次の解決策を思いつきました。

使用法

「セクション」を作成する

  1. 一般的なシナリオ:部分ビューでは、ページで部分ビューが何度繰り返されても、ブロックを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あなたは正しい-私がそれを使用する方法を言うつもりだった方法に私が混乱しているとしても、ここに戻ってきます。回答を更新しています...
drzaus

さらに明確にするために、「名前」が2つあるのは、パラメータisOnlyOneで一意のキーが必要な場合にのみレンダリングする必要がある場合に、特定の場所に名前でレンダリングする場合にのみ、識別子を提供するためです。それ以外の場合は、ダンプされHtml.RenderDelayed()ます。
drzaus

私は個人的に、トラブルを購入してこのアプローチを使用する必要はないと思います。部分ビューのセクションは削除できるので、不要であり、スクリプトはセクションを定義しなくてもそこに移動できます。それは外部的にレンダリングされたものであり、レンダリングされたページのコードを見ると、部分的なビューのコードがそこに表示されていないことに気づくだけです。したがって、それがより良い組織などの問題である場合、それはまったく効果がありません。
超絶

@Transcendent承認された回答stackoverflow.com/a/7556594/1037948の
drzaus

16

私にはこの問題があり、この手法を使用しました

私が見つけたその最高のソリューションは非常に柔軟です。

また、ここ投票 て、累積セクション宣言のサポートを追加してください


9

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

8

控えめな原則に従って、"_ myPartial"がコンテンツをスクリプトセクションに直接挿入する必要はあまりありません。これらの部分ビュースクリプトを別の.jsファイルに追加し、親ビューから@scriptsセクションで参照できます。


10
ページで部分ビューがまったくレンダリングされない場合はどうなりますか?これらの.jsファイルを親で参照し、オーバーロードさせますか?
Murali Murugesan 2013

5

特にMVCを使用する場合、Webに関する考え方に根本的な欠陥があります。欠点は、JavaScriptがどういうわけかビューの責任であることです。ビューはビューであり、JavaScript(動作またはその他)はJavaScriptです。SilverlightとWPFのMVVMパターンでは、「最初に表示」または「最初にモデル」に直面しています。MVCでは常にモデルの観点から推論する必要があり、JavaScriptは多くの点でこのモデルの一部です。

AMDパターンを使用することをお勧めします(私自身もRequireJSが好きです)。JavaScriptをモジュールに分離し、機能を定義して、JavaScriptをロードするビューに依存する代わりに、JavaScriptからHTMLにフックします。これにより、コードがクリーンアップされ、懸念が分離され、一気に生活が楽になります。


2〜3か月ほどの間、私はRequireJSを使用しており、RequireJSなしで別のWebアプリケーションを開発することはないと思います。
tugberk 2013年

6
JavaScriptもビューの責任になります。
ケルメン2013

1
AMDパターンの使用は良い考えですが、JavaScriptがモデルの一部であるというあなたの主張には同意しません。多くの場合、特にKnockoutなどと組み合わせた場合に、ビューの動作を定義する必要があります。モデルのJSON表現をJavaScriptビューにダンプします。個人的には、windowオブジェクトのカスタム「名前空間」であるクロージャーを使用し、パーシャルの前にライブラリー・スクリプトを組み込んでいます。
2015

ここでは誤解があると思います。ほとんどのWebアプリを開発する場合、実際には2つのアプリケーションを開発しています。1つはサーバーで実行し、もう1つはクライアントで実行します。サーバーの観点から見ると、ブラウザーに送信するものはすべて「ビュー」です。その意味で、JavaScriptはビューの一部です。クライアントアプリの観点から見ると、純粋なHTMLはビューであり、JSはサーバーのMVC用語でMとCに対応するコードです。これが人々がここで反対している理由だと思います。
TheAgent

3

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パターンで、これも非常にうまく機能します。


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)ifにhtmlを入れ、elseにjsを入れます。

@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

きれいではありませんが、部分ビューにセクションを挿入する方法があります。親ビューから2つの変数にアクセスする必要があります。部分ビューの目的の一部はそのセクションを作成することなので、これらの変数を要求することは理にかなっています。

部分ビューにセクションを挿入すると、次のようになります。

@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を2回呼び出しました。

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

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

確かに全体的な考えは、部分ビューの利用者がスクリプトを含める必要があることを知る必要はないということです、それはちょっと問題ですよね?それ以外の場合も同様に言うことができます @Html.Partial("MyPartialViewScripts")
ダンリチャードソン

いいえ、スクリプトを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

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

0

先日、ほぼ同じ問題が発生しましたが、部分的なビューが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")
}

_layoutPartial.cshtml(新規):

@RenderSection("Scripts")
@RenderBody()

次に、ページでAJAXを使用します。

(部分的ではなく)メインレイアウトでページをレンダリングする場合は、を設定しないでくださいViewBag.Partial = true。HTMLヘルパーは必要ありません。


-1

まあ、私は他のポスターがあなたのパーシャルに@セクションを直接含める手段を提供したと思います(サードパーティのhtmlヘルパーを使用することによって)。

ただし、スクリプトがパーシャルに密接に結合されている場合は、JavaScriptを<script>パーシャル内のインラインタグ内に直接挿入してそれを実行するだけです(パーシャルを複数回使用する場合は、スクリプトの重複に注意してください)。単一のビューで);


1
jQueryなどの読み込みはインラインスクリプトの後で行われるため、これは通常は理想的ではありませんが、ネイティブコードの場合は問題ないと思います。
Worthy7

-3

_contact.cshtmlと呼ばれる部分的なビューがあると仮定すると、連絡先は正当な名前(名前)または物理的な件名(姓、名)にすることができます。あなたのビューは何がレンダリングされるかについて注意を払うべきであり、それはjavascriptで達成することができます。そのため、遅延レンダリングとJS内部ビューが必要になる場合があります。

私が考える唯一の方法は、どのようにそれを省略できるかということですが、そのようなUIの懸念を処理する控えめな方法を作成するときです。

MVC 6にはいわゆるView Componentがあることにも注意してください。MVCフューチャーでさえ同様のものがあり、Telerikもそのようなものをサポートしています...


1
3年遅れて、これでも質問の答えにはならないと思いますか?ここで何を言おうとしていますか?3年後の将来のテクノロジーの推論的な機能で質問に答えることは、実際には答えでもなければ、特に役に立ちません
dan richardson '12 / 12/08

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

-5

私はこれで同様の問題を解決しました:

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

それは私がゲスを注入するためのかなりの方法です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.