Menyuntikkan konten ke bagian tertentu dari tampilan sebagian ASP.NET MVC 3 dengan Razor View Engine

324

Saya memiliki bagian ini didefinisikan di blog saya _Layout.cshtml

@RenderSection("Scripts", false)

Saya dapat dengan mudah menggunakannya dari tampilan:

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

Yang saya perjuangkan adalah bagaimana cara agar beberapa konten disuntikkan di dalam bagian ini dari tampilan sebagian.

Mari kita asumsikan ini adalah halaman tampilan saya:

@section Scripts { 

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

<div>
    poo bar poo
</div>

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

Saya perlu menyuntikkan beberapa konten di dalam Scriptsbagian dari _myPartialtampilan sebagian.

Bagaimana saya bisa melakukan ini?

tugberk
sumber
17
untuk siapa pun yang datang ke sini nanti - ada paket nuget untuk menangani ini: nuget.org/packages/Forloop.HtmlHelpers
Russ Cam
@RussCam Anda harus menjawab pertanyaan ini. Memberi +1 pada paket nuget memecahkan masalah yang dihadapi OP.
Carrie Kendall
1
Paket @RussCam NuGet bukan solusi, kode paket mungkin.
Maksim Vi.
8
@MaksimVi. baik, saya menulis paket nuget dan tidak memiliki niat untuk menurunkannya, jadi daripada mengulangi kode ( bitbucket.org/forloop/forloop-htmlhelpers/src ) atau wiki ( bitbucket.org/forloop/forloop-htmlhelpers/wiki / Beranda ) di sini, tautan ke sana sebagai komentar disimpan dalam semangat stackoverflow, IMO.
Russ Cam
Inilah solusi lain yang tampaknya sangat bagus: stackoverflow.com/questions/5355427/…
jkokorian

Jawaban:

235

Bagian tidak berfungsi dalam tampilan sebagian dan itu berdasarkan desain. Anda dapat menggunakan beberapa pembantu kustom untuk mencapai perilaku yang serupa, tetapi jujur ​​itu tanggung jawab pandangan untuk menyertakan skrip yang diperlukan, bukan tanggung jawab parsial. Saya akan merekomendasikan menggunakan bagian @script dari tampilan utama untuk melakukan itu dan tidak perlu khawatir tentang skrip.

Darin Dimitrov
sumber
445
Tetapi bagaimana jika skrip sangat spesifik untuk parsial? Tidakkah masuk akal untuk mendefinisikannya secara parsial, dan bukan pandangan?
Jez
43
Mengapa karena desain?
Shimmy Weitzhandler
56
@ Darin: Saya tidak setuju. Bagaimana dengan prinsip KERING? Saya tidak suka mengulang sendiri, meskipun hanya referensi skrip.
fretje
14
@Fretje, setiap orang memiliki hak untuk mengekspresikan pendapatnya tentang topik tersebut. Saya menghargai milikmu. Dalam jawaban saya, saya telah menyatakan milik saya dan menautkan ke jawaban yang akan memungkinkan Anda untuk mencapai tugas ini. Tetapi saya juga menyoroti apa yang akan saya rekomendasikan dan lakukan untuk situasi ini.
Darin Dimitrov
33
seconding @JoshNoe dan yang lainnya - "widget" (tampilan + interaksi kaya) adalah contoh sempurna dari tampilan parsial yang digabungkan dengan javascript terkait. Dengan desain saya tidak harus menulis dua pernyataan termasuk di tempat yang berbeda untuk mendapatkan fungsionalitas penuh, karena tampilan tidak akan pernah tanpa interaksi yang menyertainya, dan interaksi tidak akan pernah muncul di tempat lain.
drzaus
83

Ini pertanyaan yang cukup populer, jadi saya akan posting solusi saya.
Saya memiliki masalah yang sama dan meskipun tidak ideal, saya pikir itu benar-benar berfungsi dengan baik dan tidak membuat sebagian bergantung pada pandangan.
Skenario saya adalah bahwa suatu tindakan dapat diakses dengan sendirinya tetapi juga dapat disematkan ke tampilan - peta google.

Di saya, _layoutsaya punya:

@RenderSection("body_scripts", false)

Dalam indexpandangan saya, saya memiliki:

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

Dalam clientspandangan saya, saya punya (semua peta dan asosiasi html):

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

Saya Clients_ScriptsTampilan berisi javascript yang akan ditampilkan ke halaman

Dengan cara ini skrip saya diisolasi dan dapat dirender ke dalam halaman di mana diperlukan, dengan body_scripts tag hanya diberikan pada kejadian pertama yang ditemukan oleh mesin tampilan silet.

Itu membuat saya memiliki segalanya yang terpisah - ini adalah solusi yang bekerja cukup baik bagi saya, orang lain mungkin memiliki masalah dengan itu, tetapi itu menambal lubang "dengan desain".

dan richardson
sumber
2
Saya bukan orang yang memilih Anda, tetapi saya akan mengatakan bahwa saya tidak terlalu menyukai solusi ini karena masih memisahkan tampilan skrip tertentu dari tampilan itu sendiri.
naksir
3
20 orang lain dan saya punya pendapat berbeda. Anda masih dapat memiliki skrip yang terkait langsung dengan tampilan yang ada di file terpisah, ini merupakan kesalahan pemrograman jika Anda tidak menyertakan skrip bersama dengan tampilan Anda. Setelah itu dalam file terpisah memisahkan interaksi dari presentasi dan memungkinkan banyak manfaat lain dari itu berada dalam file terpisah.
dan richards
1
Anda sepenuhnya benar. Saya sebenarnya sepenuhnya setuju dan lebih suka metode ini secara pribadi. Masalah sebenarnya bagi saya adalah bahwa kolega saya berjuang dengan banyak pemisahan ini. Tapi itu masalah domain. Saya pikir metode ini sangat ideal, terutama sekali Anda faktor dalam proses membangun JavaScript. Saya akan terus berupaya mendidik kolega saya agar menggunakan metode ini, dan sepenuhnya mendukungnya. Saya pikir jawaban Anda dapat ditingkatkan. Anda tidak perlu menyebutkan "20 orang setuju". Hanya karena jawaban itu populer, tidak selalu berarti itu benar. Dalam hal ini benar.
naksir
Sangat benar, dan saya selalu senang menerima umpan balik yang membangun dan mengubah kode saya sendiri dan menjawab jika ada perbaikan yang bisa didapat :)
dan richardson
1
Solusi ini memiliki manfaat tambahan karena masih dapat melakukan semua hal MVC-ish yang Anda harapkan dapat dilakukan dalam tampilan yang khas, seperti mampu menyandikan JSON yang diteruskan dalam Model dan menghasilkan URL menggunakan Url. Tindakan. Pendekatan ini kemudian merupakan cara yang elegan untuk mengatur pengontrol AngularJS Anda - setiap tampilan parsial dapat mewakili pengontrol terpisah dalam modul Angular. Begitu bersih!
Dan
40

Dari solusi di utas ini , saya datang dengan solusi yang mungkin rumit berikut ini yang memungkinkan Anda menunda rendering html (skrip juga) dalam blok menggunakan.

PEMAKAIAN

Buat "bagian"

  1. Skenario umum: Dalam tampilan parsial, hanya sertakan blok satu kali tidak peduli berapa kali tampilan parsial diulang dalam halaman:

    @using (Html.Delayed(isOnlyOne: "some unique name for this section")) {
        <script>
            someInlineScript();
        </script>
    }
  2. Dalam tampilan parsial, sertakan blok untuk setiap kali parsial digunakan:

    @using (Html.Delayed()) {
        <b>show me multiple times, @Model.Whatever</b>
    }
  3. Dalam tampilan parsial, hanya sertakan blok sekali tidak peduli berapa kali parsial diulang, tetapi kemudian render secara khusus dengan nama 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>
    }

Render "bagian"

(yaitu menampilkan bagian yang tertunda dalam tampilan induk)

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

KODE

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


}
drzaus
sumber
1
Wow bahkan rumit bagi saya untuk memahami kode, tetapi +1 untuk datang dengan solusi
Rameez Ahmed Sayad
@RameezAhmedSayad Anda benar - kembali ke sini bahkan saya bingung dengan bagaimana saya mengatakan bagaimana menggunakannya. Memperbarui jawabannya ...
drzaus
Dan untuk mengklarifikasi lebih lanjut - alasan ada dua "nama" adalah bahwa jika Anda hanya ingin diberikan saat diperlukan kunci unik dalam parameter isOnlyOne, tetapi hanya jika Anda ingin merendernya di lokasi tertentu dengan nama yang Anda berikan pengidentifikasi, kalau tidak akan dibuang di Html.RenderDelayed().
drzaus
Saya pribadi tidak berpikir akan ada kebutuhan untuk membeli masalah dan menggunakan pendekatan ini, bagian dalam pandangan parsial sama sekali tidak diperlukan karena dapat dihilangkan, dan skrip dapat pergi ke sana tanpa mendefinisikan bagian. Itu karena itu diberikan secara eksternal dan jika Anda melihat kode untuk halaman yang diberikan Anda hanya melihat kode untuk tampilan parsial tidak terlihat di sana. Jadi jika itu masalah organisasi yang lebih baik, dll, itu tidak akan berpengaruh sama sekali.
Transenden
@ Transenden "debat" telah dimulai di komentar pada jawaban yang diterima stackoverflow.com/a/7556594/1037948
drzaus
16

Saya punya masalah ini dan menggunakan teknik ini .

Ini solusi terbaik yang saya temukan yang sangat fleksibel.

Juga silahkan memilih di sini untuk menambahkan dukungan untuk kumulatif bagian deklarasi

Saya Anak Laki-Laki
sumber
9

Jika Anda memiliki kebutuhan yang sah untuk menjalankan beberapa jsdari partial, inilah cara Anda dapat melakukannya, jQuerydiperlukan:

<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>
Serj Sagan
sumber
Saya mencoba @drzaus, perlu 'SeeIfReady' atau tidak berfungsi.
Cacho Santa
8

Mengikuti prinsip yang tidak mencolok , itu tidak cukup diperlukan untuk "_myPartial" untuk menyuntikkan konten langsung ke bagian skrip. Anda dapat menambahkan skrip tampilan parsial tersebut ke .jsfile terpisah dan merujuknya ke bagian skrip @ dari tampilan induk.

Archil
sumber
10
Apa yang akan terjadi jika tampilan sebagian sama sekali tidak ditampilkan di halaman? Apakah kita masih mereferensikan file .js tersebut di induk dan membuatnya kelebihan?
Murali Murugesan
5

Ada kesalahan mendasar dalam cara kita berpikir tentang web, terutama ketika menggunakan MVC. Kekurangannya adalah bahwa JavaScript merupakan tanggung jawab pandangan. Tampilan adalah tampilan, JavaScript (perilaku atau sebaliknya) adalah JavaScript. Dalam pola MVVM Silverlight dan WPF, kita dihadapkan dengan "lihat dulu" atau "model dulu". Dalam MVC kita harus selalu mencoba memberi alasan dari sudut pandang model dan JavaScript adalah bagian dari model ini dalam banyak hal.

Saya akan menyarankan menggunakan pola AMD (saya sendiri suka RequireJS ). Pisahkan JavaScript Anda dalam modul, tentukan fungsionalitas Anda dan kaitkan ke html Anda dari JavaScript alih-alih mengandalkan tampilan untuk memuat JavaScript. Ini akan membersihkan kode Anda, memisahkan masalah Anda dan membuat hidup lebih mudah dalam satu gerakan.

Tuan Baudin
sumber
Untuk sekitar dua atau tiga bulan, saya menggunakan RequireJS dan saya pikir saya tidak akan pernah mengembangkan aplikasi web lain tanpa RequireJS.
tugberk
6
JavaScript juga bisa menjadi tanggung jawab Lihat.
Kelmen
1
Menggunakan pola AMD adalah ide yang bagus, tetapi saya tidak setuju dengan pernyataan Anda bahwa JavaScript adalah bagian dari model. Seringkali untuk mendefinisikan perilaku Tampilan, terutama ketika digabungkan dengan sesuatu seperti Knockout. Anda membuang representasi JSON dari model Anda ke dalam Tampilan JavaScript Anda. Secara pribadi, saya hanya menggunakan closure, "namespace" khusus pada windowobjek, dan menyertakan skrip perpustakaan sebelum parsial.
naksir
Saya pikir ada kesalahpahaman di sini. Saat mengembangkan sebagian besar aplikasi web, kami sebenarnya mengembangkan dua aplikasi: satu yang berjalan di server dan satu yang berjalan di klien. Dari perspektif server, apa pun yang Anda kirim ke browser adalah "view". Dalam hal itu, JavaScript adalah bagian dari tampilan. Dari perspektif aplikasi klien, HTML murni adalah tampilan dan JS adalah kode yang sejajar dengan M dan C dalam istilah MVC server. Saya pikir ini sebabnya orang tidak setuju di sini.
TheAgent
3

Tujuan OP adalah bahwa ia ingin mendefinisikan skrip inline ke dalam Partial View-nya, yang saya asumsikan bahwa skrip ini hanya khusus untuk Partial View itu, dan memasukkan blok tersebut ke dalam bagian skripnya.

Saya mengerti bahwa dia ingin agar Pandangan Parsial itu menjadi mandiri. Idenya mirip dengan komponen ketika menggunakan Angular.

Cara saya adalah hanya menyimpan skrip di dalam Partial View apa adanya. Sekarang masalah dengan itu adalah saat memanggil Tampilan Parsial, mungkin mengeksekusi skrip di sana sebelum semua skrip lainnya (yang biasanya ditambahkan ke bagian bawah halaman tata letak). Dalam hal ini, Anda hanya perlu skrip Partial View menunggu skrip lain. Ada beberapa cara untuk melakukan ini. Yang paling sederhana, yang pernah saya gunakan sebelumnya, adalah menggunakan acara body.

Pada tata letak saya, saya akan memiliki sesuatu di bagian bawah seperti ini:

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

Kemudian pada Tampilan Parsial saya (di bagian bawah):

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

      // .. do your thing here

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

Solusi lain adalah menggunakan tumpukan untuk mendorong semua skrip Anda, dan memanggil masing-masing di akhir. Solusi lain, seperti yang sudah disebutkan, adalah pola RequireJS / AMD, yang bekerja dengan sangat baik juga.

alans
sumber
2

Solusi pertama yang dapat saya pikirkan, adalah menggunakan ViewBag untuk menyimpan nilai-nilai yang harus di-render.

Awalnya saya tidak pernah mencoba jika ini bekerja dari sudut pandang sebagian, tetapi harus dilakukan.

Iridio
sumber
Baru saja mencoba; sayangnya itu tidak berfungsi (dibuat ViewBag.RenderScripts = new List<string>();di bagian atas halaman utama, lalu dipanggil @Html.Partial("_CreateUpdatePartial",Model,ViewData), lalu dimasukkan @section Scripts {@foreach (string script in ViewBag.RenderScripts) Scripts.Render(script); }}. Dalam tampilan sebagian saya cantumkan @{ViewBag.RenderScripts = ViewBag.RenderScripts ?? new List<string>();ViewBag.RenderScripts.Add("~/bundles/jquery");}.
JohnLBevan
2

Anda tidak perlu menggunakan bagian dalam tampilan sebagian.

Sertakan dalam Tampilan Parsial Anda. Ini menjalankan fungsi setelah jQuery dimuat. Anda dapat mengubah ketentuan klausa untuk kode Anda.

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

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

Julio Spader

Julio Spader
sumber
2

Anda dapat menggunakan Metode Ekstensi ini : (Simpan sebagai 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);
                }
            }
        }
    }
}

Gunakan seperti ini:

Contoh parsial: (_MyPartial.cshtml) Masukkan html di if, dan js di yang lain.

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

Di _Layout.cshtml Anda, atau di mana pun Anda ingin agar skrip dari parsial ditampilkan, cantumkan yang berikut (satu kali): Hanya akan membuat javascript dari semua parsial pada halaman saat ini di lokasi ini.

@{ Html.RenderPartialScripts(); }

Kemudian untuk menggunakan parsial Anda, cukup lakukan ini: Ini hanya akan membuat html di lokasi ini.

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

Ada cara untuk menyisipkan bagian dalam tampilan sebagian, meskipun tidak cantik. Anda harus memiliki akses ke dua variabel dari Tampilan induk. Karena bagian dari sebagian pandangan Anda adalah untuk membuat bagian itu, masuk akal untuk memerlukan variabel-variabel ini.

Begini tampilannya menyisipkan bagian dalam tampilan parsial:

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

Dan di halaman memasukkan tampilan parsial ...

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

Anda juga dapat menggunakan teknik ini untuk menentukan konten suatu bagian secara terprogram dalam kelas apa pun.

Nikmati!

Pluto
sumber
1
Bisakah Anda tolong dan tautan ke proyek yang berfungsi penuh?
Ehsan Zargar Ershadi
1

Gagasan Pluto dengan cara yang lebih baik:

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

Tampilan \ web.config:

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

Melihat:

@PartialWithScripts("_BackendSearchForm")

Sebagian (_BackendSearchForm.cshtml):

@{ RenderScriptsInBasePage(scripts()); }

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

Layout page:

@RenderSection("scripts", required: false)
PaulSanS
sumber
1

Ini berfungsi untuk saya yang memungkinkan saya untuk mencari co-javascript dan html untuk tampilan parsial dalam file yang sama. Membantu dengan proses berpikir untuk melihat html dan bagian terkait dalam file tampilan parsial yang sama.


Dalam Tampilan yang menggunakan Tampilan Parsial yang disebut "_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" } })

 }

Dalam file Partial View

@model SomeType

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

@if (region == "HTMLSection")
{


}

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

Saya memecahkan masalah ini dengan rute yang sangat berbeda (karena saya sedang terburu-buru dan tidak ingin menerapkan HtmlHelper baru):

Saya membungkus Tampilan Parsial saya dalam pernyataan if-else:

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

Lalu, saya menelepon Partial dua kali dengan ViewData khusus:

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

@section scripts{
    @Html.Partial("MyPartialView", Model, 
        new ViewDataDictionary { { "ShouldRenderScripts", true } })
}
Rick Love
sumber
Tentunya seluruh idenya adalah bahwa konsumen dari pandangan parsial tidak perlu tahu bahwa itu harus menyertakan skrip, itu agak masalah? Kalau tidak, Anda bisa saja mengatakan @Html.Partial("MyPartialViewScripts")
dan richards
Tidak, idenya adalah untuk memungkinkan skrip didefinisikan dalam dokumen yang sama dengan html, tetapi saya setuju ini tidak ideal.
Rick Love
0

Saya memiliki masalah yang sama, di mana saya memiliki halaman master sebagai berikut:

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

...

@Html.Partial("_Charts", Model)

tetapi tampilan sebagian tergantung pada beberapa JavaScript di bagian Script. Saya menyelesaikannya dengan mengkodekan tampilan parsial sebagai JSON, memuatnya ke dalam variabel JavaScript dan kemudian menggunakannya untuk mengisi div, jadi:

@{
    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>
John M
sumber
IMO Anda harus menyelesaikan ini dengan memindahkan JS Anda ke file yang terpisah.
Layak 7
0

Anda dapat menggunakan Folder / index.cshtml sebagai masterpage lalu menambahkan skrip bagian. Kemudian, dalam tata letak Anda, Anda memiliki:

@RenderSection("scripts", required: false) 

dan index.cshtml Anda:

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

dan itu akan bekerja pada semua tampilan parsial Anda. Ini bekerja untuk saya

RogerEdward
sumber
0

Menggunakan Mvc Core Anda dapat membuat TagHelper rapi scriptsseperti yang terlihat di bawah ini. Ini bisa dengan mudah diubah menjadi sectiontag di mana Anda memberikan nama juga (atau nama tersebut diambil dari jenis turunannya). Perhatikan bahwa injeksi ketergantungan harus disiapkan untuk IHttpContextAccessor.

Saat menambahkan skrip (mis. Sebagian)

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

Saat mengeluarkan skrip (misalnya dalam file tata letak)

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

Kode

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

Saya mengalami masalah yang hampir identik beberapa hari yang lalu, kecuali bahwa sebagian melihat adalah respons terhadap permintaan AJAX. Dalam situasi saya, sebagian sebenarnya adalah satu halaman penuh, tetapi saya ingin itu dapat diakses sebagai bagian dari halaman lain.

Jika Anda ingin membuat bagian secara parsial, solusi terbersih adalah membuat tata letak baru dan menggunakan variabel ViewBag. Ini tidak bekerja dengan @Html.Partial()atau yang baru <partial></partial>, gunakan AJAX.

Tampilan utama (bahwa Anda ingin ditampilkan sebagian di tempat lain):

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

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

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

Pengendali:

public IActionResult GetPartial() {

    ViewBag.Partial = true;

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

_layoutPartial.cshtml (baru):

@RenderSection("Scripts")
@RenderBody()

Kemudian gunakan AJAX di halaman Anda.

Jika Anda ingin merender halaman dalam tata letak utama (bukan sebagian), maka jangan atur ViewBag.Partial = true. Tidak diperlukan bantuan HTML.

KatoFett
sumber
-1

Yah, saya kira poster-poster lain telah memberi Anda sarana untuk secara langsung memasukkan @seksi dalam sebagian Anda (dengan menggunakan bantuan html pihak ketiga).

Tapi, saya rasa, jika skrip Anda benar-benar digabungkan ke parsial Anda, cukup masukkan javascript Anda langsung di dalam <script>tag inline di dalam parsial Anda dan selesai dengan itu (hanya berhati-hati dari duplikasi skrip jika Anda berniat menggunakan parsial lebih dari sekali dalam satu tampilan);

CShark
sumber
1
Ini biasanya tidak ideal karena pemuatan jQuery dll akan terjadi setelah skrip inline ... tetapi untuk kode asli saya kira itu baik-baik saja.
Layak 7
-3

anggap Anda memiliki tampilan sebagian yang disebut _contact.cshtml, kontak Anda dapat berupa subjek hukum (nama) atau fisik (nama depan, nama belakang). tampilan Anda harus memperhatikan apa yang diberikan dan yang dapat dicapai dengan javascript. jadi rendering yang tertunda dan tampilan JS inside mungkin diperlukan.

satu-satunya cara saya berpikir, bagaimana ini dapat dinonaktifkan, adalah ketika kita menciptakan cara yang tidak mencolok dalam menangani masalah UI tersebut.

juga perhatikan bahwa MVC 6 akan memiliki apa yang disebut View Component, bahkan MVC futures memiliki beberapa hal serupa dan Telerik juga mendukung hal semacam itu ...

pengguna4298890
sumber
1
3 tahun terlambat, dan saya pikir ini bahkan tidak menjawab pertanyaan sama sekali? Apa yang ingin kamu katakan di sini? Menjawab pertanyaan 3 tahun kemudian dengan fitur spekulatif teknologi masa depan tidak benar-benar jawaban atau sangat membantu
dan richards
-3

Saya baru saja menambahkan kode ini pada tampilan parsial saya dan menyelesaikan masalah, meskipun tidak terlalu bersih, ia berfungsi. Anda harus memastikan Id objek yang Anda render.

<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>
luis
sumber
-5

Saya memiliki masalah serupa yang diselesaikan dengan ini:

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

Itu cara yang bagus untuk menyuntikkan.

Pouria Jafari
sumber