Razor View Engine ile ASP.NET MVC 3 kısmi görünümden belirli bölümlere içerik ekleme


324

Bu bölümde tanımlanmış _Layout.cshtml

@RenderSection("Scripts", false)

Bir görünümden kolayca kullanabilirim:

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

Burada mücadele ettiğim şey, kısmi bir bakış açısıyla bu bölümün içine bazı içeriklerin nasıl enjekte edileceğidir.

Bunun benim görünüm sayfam olduğunu varsayalım:

@section Scripts { 

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

<div>
    poo bar poo
</div>

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

Kısmi görünümden Scriptsbölüm içine bazı içerikler enjekte etmem gerekiyor _myPartial.

Bunu nasıl yapabilirim?


17
buna daha sonra gelen herkes için - bunu ele almak için bir nuget paketi var: nuget.org/packages/Forloop.HtmlHelpers
Russ Cam

@RussCam bu soruya cevap vermelisiniz. +1 nuget paketi OP'nin sahip olduğu problemi çözer.
Carrie Kendall

1
@RussCam NuGet paketi bir çözüm değil, paketin kodu olabilir.
Maksim Vi.

8
@MaksimVi. peki, nuget paketini yazdım ve kodu kaldırmak gibi bir niyetim yok, bu yüzden kodu tekrarlamak yerine ( bitbucket.org/forloop/forloop-htmlhelpers/src ) veya wiki ( bitbucket.org/forloop/forloop-htmlhelpers/wiki) / Home ) burada, bir yorum olarak ona bağlantı, yığın akışı, IMO ruhu içinde tutulur.
Russ Cam

İşte çok hoş görünen başka bir çözüm: stackoverflow.com/questions/5355427/…
jkokorian

Yanıtlar:


235

Bölümler kısmi görünümlerde çalışmaz ve bu tasarım gereğidir. Benzer davranışları elde etmek için bazı özel yardımcıları kullanabilirsiniz, ancak dürüst olmak gerekirse, kısmi sorumluluğun değil gerekli komut dosyalarının dahil edilmesi görünümün sorumluluğundadır. Bunu yapmak için ana görünümün @scripts bölümünü kullanmanızı ve komut dosyaları hakkında kısmi endişelenmemeyi öneririm.


445
Peki senaryo kısmi olana çok özelse? Görüşte değil, kısmi olarak tanımlanması mantıklı değil mi?
Jez

43
Neden tasarım gereği?
Shimmy Weitzhandler

56
@Darin: Kabul etmiyorum. KURU prensibi ne olacak? Sadece senaryo referansları olsa bile kendimi tekrarlamak istemiyorum.
fretje

14
@fretje, herkes konuyla ilgili fikrini ifade etme hakkına sahiptir. Sana saygı duyuyorum. Cevabımda benimkini ifade ettim ve bu görevi yerine getirmenizi sağlayacak bir cevaba bağlandım. Ama aynı zamanda bu durum için ne önereceğimi ve ne yapacağımı da vurguladım.
Darin Dimitrov

33
@JoshNoe ve geri kalanı - bir "widget" (ekran + zengin etkileşim) ilişkili javascript ile sıkıca bağlı kısmi görünüm mükemmel bir örneğidir. Tasarım gereği , tam işlevsellik elde etmek için farklı yerlerde iki içerme ifadesi yazmak zorunda olmamalıyım, çünkü ekran asla eşlik eden etkileşim olmadan olmayacak ve etkileşim asla başka bir yerde görünmeyecek.
drzaus

83

Bu oldukça popüler bir soru, bu yüzden çözümümü göndereceğim.
Aynı sorunu yaşadım ve ideal olmasa da, aslında oldukça iyi çalıştığını ve kısmi görünüme bağımlı hale getirmediğini düşünüyorum.
Benim senaryom, bir eylemin kendi başına erişilebilir olduğu, ancak aynı zamanda bir google haritası olan bir görünüme de gömülebilmesiydi.

Benim _layoutvar:

@RenderSection("body_scripts", false)

Benim görüşüme indexgöre:

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

Benim görüşüme clientsgöre (tüm harita ve assoc. Html):

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

Benim Clients_Scriptsgörünümüm, sayfada oluşturulacak javascript'i içeriyor

Bu şekilde komut dosyam yalıtılır ve gerektiğinde sayfaya eklenebilir, body_scriptsetiket yalnızca ustura görüntüleme motorunun bulduğu ilk durumda oluşturulur.

Bu, her şeyi ayırmamı sağlıyor - benim için oldukça iyi çalışan bir çözüm, diğerleri bununla ilgili sorunlar yaşayabilir, ancak "tasarımla" deliğini yayar.


2
Size oy veren tek kişi ben değildim, ama bu çözümü gerçekten sevmediğimi söyleyeceğim çünkü hala görünüme özgü komut dosyalarını görünümün kendisinden ayırıyor.
ezmek

3
20 kişi daha farklı bir görüşe sahip. Yine de ayrı bir dosyadaki bir görünümle doğrudan ilişkili komut dosyalarınız olabilir, komut dosyanızı görünümünüzle birlikte eklemezseniz bir programlama hatasıdır. Ayrı bir dosyaya sahip olmak etkileşimi sunumdan ayırır ve ayrı bir dosyada olmasının getirdiği diğer faydaların bolluğuna izin verir.
dan richardson

1
Tamamen haklısın. Aslında bu yöntemi tamamen kendim kabul ediyorum ve tercih ediyorum. Benim için asıl sorun, meslektaşlarımın bu kadar ayrılıkla mücadele etmesi. Yine de bu bir alan adı problemi. Bence bu yöntem ideal, özellikle bir JavaScript derleme sürecini hesaba kattığınızda. Bu yöntemi kullanarak meslektaşlarımı eğitmeye ve tamamen desteklemeye devam edeceğim. Yine de cevabınızın geliştirilebileceğini düşünüyorum. Gerçi "20 kişi katılıyorum" bahsetmenize gerek yoktu. Bir cevap popüler olduğu için, her zaman doğru olduğu anlamına gelmez. Bu durumda doğru.
ezmek

Çok doğru, ve her zaman yapıcı geribildirimi kabul etmekten ve kendi kodumu değiştirmekten ve sahip olduğum bir gelişme varsa cevap vermekten mutluluk duyuyorum :)
dan richardson

1
Bu çözüm, tipik bir Görünümde yapmayı beklediğiniz tüm MVC-ish öğelerini yine de yapabilmenin ek bir avantajına sahiptir, örneğin JSON'da geçirilen bir Modeli kodlayabilir ve URL kullanarak URL'ler oluşturabilirsiniz. Aksiyon. Bu yaklaşım bu durumda AngularJS denetleyicilerinizi kurmanın zarif bir yoludur - her kısmi görünüm Açısal modülde ayrı bir denetleyiciyi temsil edebilir. Çok temiz!
Dan

40

İçinde çözümlerden Bu konuya , sana kullanarak blok içinde (çok komut) herhangi bir html render gecikme sağlar aşağıdaki muhtemelen overcomplicated çözüm geldi.

KULLANIM

"Bölüm" oluştur

  1. Tipik senaryo: Kısmi görünümde, kısmi görünümün sayfada kaç kez tekrarlandığına bakılmaksızın bloğu yalnızca bir kez ekleyin:

    @using (Html.Delayed(isOnlyOne: "some unique name for this section")) {
        <script>
            someInlineScript();
        </script>
    }
  2. Kısmi görünümde, kısmi her kullanıldığında bloğu ekleyin:

    @using (Html.Delayed()) {
        <b>show me multiple times, @Model.Whatever</b>
    }
  3. Kısmi bir görünümde, kısmi kaç kez tekrarlanırsa yapılsın, bloğu yalnızca bir kez dahil edin, ancak daha sonra özel olarak adıyla oluşturun 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>
    }

"Bölümleri" oluştur

(örneğin gecikmeli bölümü üst görünümde görüntüleme)

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

KOD

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
Vay canına kodu anlamak benim için bile karmaşık, ama +1 bir çözüm bulmak için
Rameez Ahmed Sayad

@RameezAhmedSayad haklısın - buraya nasıl geleceğimi bile kafam karıştı. Cevabı güncelleme ...
drzaus

Ve daha fazla açıklığa kavuşturmak için - iki "isim" olmasının nedeni, parametrede benzersiz bir anahtara ihtiyaç duyduktan sonra yalnızca görüntülenmesini istiyorsanız isOnlyOne, ancak yalnızca adıyla belirli bir yerde göstermek istiyorsanız tanımlayıcıyı sağlamanızdır, aksi taktirde terk edilir Html.RenderDelayed().
drzaus

Şahsen, sorun satın almak ve bu yaklaşımı kullanmak için herhangi bir ihtiyaç olacağını düşünmüyorum, kısmi görünümlerde bölüm, ortadan kaldırılabileceği için gerekli değildir ve komut dosyaları bir bölüm tanımlamadan oraya gidebilir. Bunun nedeni harici olarak işlenmiş olmasıdır ve işlenen sayfanın kodunu görürseniz, kısmi görünüm kodunun orada görünmediğini fark edersiniz. Eğer bu daha iyi bir organizasyon meselesi ise, bunun hiçbir etkisi olmayacaktır.
Aşkın

@Transcendent "tartışma" kabul edilmiş cevap yorumlarda zaten başladı stackoverflow.com/a/7556594/1037948
drzaus

16

Bu problemi yaşadım ve bu tekniği kullandım .

Onun çok esnek buldum en iyi çözüm.

Ayrıca oy lütfen buraya kümülatif bölüm beyanı desteği eklemek için


9

Bazı çalıştırmak için meşru bir ihtiyaç yoksa jsbir gelen partial, burada yapabileceğini, nasıl olduğunu jQuerygereklidir:

<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'u denedim, 'SeeIfReady' gerekiyor ya da çalışmıyor.
Cacho Santa

8

Göze batmayan prensibi takiben, "_myPartial" ifadesinin içeriği doğrudan komut dosyaları bölümüne enjekte etmesi pek gerekli değildir. Bu kısmi görünüm komut dosyalarını ayrı bir .jsdosyaya ekleyebilir ve üst görünümden @ komut dosyaları bölümüne başvurabilirsiniz.


10
Kısmi görünüm sayfada hiç oluşturulmamışsa ne olur? Yine de üstteki bu .js dosyalarına referans veriyor ve aşırı yüklüyor mu?
Murali Murugesan

5

Özellikle MVC kullanırken web hakkında düşünme şeklimizde temel bir kusur vardır. Kusur, JavaScript'in bir şekilde görünümün sorumluluğu olmasıdır. Görünüm bir görünümdür, JavaScript (davranışsal veya başka türlü) JavaScript'tir. Silverlight ve WPF'nin MVVM modelinde "önce görüntüle" veya "önce model" ile karşı karşıyayız. MVC'de her zaman modelin bakış açısından düşünmeye çalışmalıyız ve JavaScript birçok açıdan bu modelin bir parçasıdır.

AMD modelini kullanmanızı öneririm (kendimi RequireJS gibi ). JavaScript'inizi modüller halinde ayırın, işlevselliğinizi tanımlayın ve JavaScript'i yüklemek için bir görünüme güvenmek yerine JavaScript'ten html'nize bağlanın. Bu, kodunuzu temizleyecek, endişelerinizi ayıracak ve hayatı bir araya getirerek hayatınızı kolaylaştıracaktır.


İki ya da üç ay gibi, RequireJS kullanıyorum ve RequireJS olmadan başka bir web uygulaması geliştireceğimi sanmıyorum.
tugberk

6
JavaScript aynı zamanda Görüntüleme sorumluluğu da olabilir.
Kelmen

1
AMD modelini kullanmak iyi bir fikirdir, ancak JavaScript'in modelin bir parçası olduğu iddiasına katılmıyorum. Görüntüleme davranışı, özellikle Nakavt gibi bir şeyle birleştiğinde tanımlanır. Modelinizin bir JSON temsilini JavaScript Görünümünüze dökersiniz. Şahsen ben sadece kapaklar, windownesne üzerinde özel bir "ad alanı" kullanın ve herhangi bir kısmi önce kütüphane komut dosyaları dahil.
ezmek

Bence burada bir yanlış anlaşılma var. Çoğu web uygulamasını geliştirirken, aslında iki uygulama geliştiriyoruz : biri sunucuda ve diğeri istemci üzerinde çalışıyor. Sunucunun bakış açısından, tarayıcıya gönderdiğiniz her şey "görünüm" dür. Bu anlamda, JavaScript görünümün bir parçasıdır. İstemci uygulaması açısından bakıldığında, saf HTML görünümdür ve JS, sunucunun MVC terimlerinde M ve C ile paralel olan koddur. Bence insanlar burada aynı fikirde değiller.
TheAgent

3

OP'nin amacı, bu komut dosyasının yalnızca o Kısmi Görünüm'e özgü olduğunu ve bu bloğun komut dosyası bölümüne dahil olmasını sağladığımı kendi Kısmi Görünümüne satır içi komut dosyaları tanımlamak istemesidir.

Anlıyorum ki Kısmi Görüş kendi içinde kalsın. Fikir, Angular kullanılırken bileşenlere benzer.

Benim yolum komut dosyalarını Kısmi Görünüm içinde olduğu gibi tutmak olacaktır. Şimdi sorun Kısmi Görünüm çağrılırken, diğer tüm komut dosyalarından önce (genellikle yerleşim sayfasının altına eklenir) komut dosyasını yürütebilir. Bu durumda, yalnızca Kısmi Görünüm komut dosyasının diğer komut dosyalarını beklemesini sağlayın. Bunu yapmanın birkaç yolu vardır. Daha önce kullandığım en basit olay, bir etkinlik kullanıyor body.

Düzenimde, altta şöyle bir şey olurdu:

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

Sonra Kısmi Görünümümde (altta):

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

      // .. do your thing here

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

Başka bir çözüm, tüm komut dosyalarınızı itmek ve sonunda her birini çağırmak için bir yığın kullanmaktır. Daha önce de belirtildiği gibi diğer çözüm, gerçekten de iyi çalışan RequireJS / AMD modelidir.


2

Aklıma gelen ilk çözüm, görüntülenmesi gereken değerleri saklamak için ViewBag kullanmaktır.

Onestly asla bu kısmi bir bakış açısıyla çalışır, ama imo gerekir.


Denedim; ne yazık ki bu işe yaramaz ( ViewBag.RenderScripts = new List<string>();ana sayfanın üstünde bir oluşturulan , sonra çağırdı @Html.Partial("_CreateUpdatePartial",Model,ViewData), sonra koymak @section Scripts {@foreach (string script in ViewBag.RenderScripts) Scripts.Render(script); }}. - Kısmi görünümde ben koydum @{ViewBag.RenderScripts = ViewBag.RenderScripts ?? new List<string>();ViewBag.RenderScripts.Add("~/bundles/jquery");}.
JohnLBevan

2

Kısmi görünümde bölümleri kullanmanıza gerek yoktur.

Kısmi Görünümünüze ekleyin. JQuery yüklendikten sonra işlevi yürütür. Kodunuz için koşul koşulunu değiştirebilirsiniz.

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

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

Julio Spader


2

Şu Uzantı Yöntemlerini kullanabilirsiniz : (PartialWithScript.cs Olarak Kaydet)

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

Bunun gibi kullanın:

Kısmi örnek: (_MyPartial.cshtml) html'yi if'ye ve js'yi diğer kutusuna koyun.

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

_Layout.cshtml dosyanızda veya kısmi komut dosyalarının komut dosyalarının oluşturulmasını istediğiniz her yerde aşağıdakileri (bir kez) koyun: Bu konumdaki geçerli sayfadaki tüm kısayolların yalnızca javascript'ini oluşturur.

@{ Html.RenderPartialScripts(); }

Sonra kısmi kullanmak için, sadece bunu yapın: Bu yerde sadece html oluşturur.

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

1

Kısmi görünümlere bölüm eklemenin bir yolu var, ancak hoş değil. Üst görünümden iki değişkene erişiminizin olması gerekir. Kısmi görünümünüzün amacının bir kısmı bu bölümü oluşturmak olduğundan, bu değişkenleri zorunlu kılmak mantıklıdır.

Kısmi görünümde bir bölüm eklemek gibi görünüyor:

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

Ve sayfaya kısmi görünüm ekleme ...

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

Bu tekniği, herhangi bir sınıfta bir bölümün içeriğini programlı olarak tanımlamak için de kullanabilirsiniz.

Zevk almak!


1
Lütfen ve tamamen çalışan bir projeye link verebilir misiniz?
Ehsan Zargar Ershadi

1

Plüton'un fikri daha güzel bir şekilde:

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

Görünümler \ web.config:

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

Görünüm:

@PartialWithScripts("_BackendSearchForm")

Kısmi (_BackendSearchForm.cshtml):

@{ RenderScriptsInBasePage(scripts()); }

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

Düzen sayfası:

@RenderSection("scripts", required: false)

1

Bu benim için aynı dosyada kısmi görünüm için javascript ve html birlikte bulmak için izin çalıştı. Aynı kısmi görünüm dosyasında html ve ilgili kısmı görmek için düşünce sürecine yardımcı olur.


"_MyPartialView.cshtml" adlı Kısmi Görünüm kullanan Görünüm'de

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

 }

Kısmi Görünüm dosyasında

@model SomeType

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

@if (region == "HTMLSection")
{


}

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

0

Bu tamamen farklı bir rota çözdüm (çünkü acelem vardı ve yeni bir HtmlHelper uygulamak istemedim):

Kısmi Görünümümü büyük bir if-else ifadesinde tamamladım:

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

Sonra, özel bir ViewData ile iki kez kısmi çağırdı:

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

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

Şüphesiz bütün fikir, kısmi görüşün tüketicisinin senaryoları içermesi gerektiğini bilmesine gerek olmamasıdır, bu da bir çeşit konudur? Aksi takdirde sadece söyleyebilirsin @Html.Partial("MyPartialViewScripts")
dan richardson

Hayır, fikir komut dosyalarının html ile aynı belgede tanımlanmasına izin vermektir, ancak bunun ideal olmadığını kabul ediyorum.
Rick Love

0

Benzer bir sorun yaşadım, burada bir ana sayfam vardı:

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

...

@Html.Partial("_Charts", Model)

ancak kısmi görünüm Komut Dosyaları bölümündeki bazı JavaScript'lere bağlıydı. Kısmi görünümü JSON olarak kodlayarak, bir JavaScript değişkenine yükleyerek ve daha sonra bir div doldurmak için bunu kullanarak çözdüm:

@{
    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'nizi ayrı bir dosyaya taşıyarak bunu çözmeliydiniz.
Worthy7

0

özellikle, bir Klasör / index.cshtml dosyanızı bir ana sayfa olarak kullanabilir ve ardından bölüm komut dosyaları ekleyebilirsiniz. Sonra, düzeninizde:

@RenderSection("scripts", required: false) 

ve index.cshtml:

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

ve tüm kısmi görüşleriniz üzerinde çalışacaktır. Benim için çalışıyor


0

Mvc Core kullanarak scriptsaşağıda görüldüğü gibi düzenli bir TagHelper oluşturabilirsiniz . Bu, sectionbir ad verdiğiniz bir etikete kolayca dönüştürülebilir (veya ad, türetilmiş türden alınır). Bağımlılık enjeksiyonunun ayarlanması gerektiğini unutmayın IHttpContextAccessor.

Komut dosyaları eklerken (örn. Kısmi olarak)

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

Komut dosyalarını çıktısı alırken (örneğin bir düzen dosyasında)

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

kod

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

Geçen gün neredeyse özdeş bir sorunla karşılaştım, ancak kısmi görünüm bir AJAX isteğine bir yanıttı. Benim durumumda, kısmi aslında tam bir sayfaydı, ancak diğer sayfalardan kısmi olarak erişilebilir olmasını istedim.

Bölümleri kısmi olarak oluşturmak istiyorsanız, en temiz çözüm yeni bir düzen oluşturmak ve bir ViewBag değişkeni kullanmaktır. Bu ile çalışmaz @Html.Partial()ya da yeni <partial></partial>, AJAX kullanın.

Ana görünüm (başka bir yerde kısmi olarak görüntülenmesini istediğiniz):

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

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

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

Denetleyici:

public IActionResult GetPartial() {

    ViewBag.Partial = true;

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

_layoutPartial.cshtml (yeni):

@RenderSection("Scripts")
@RenderBody()

Ardından sayfanızda AJAX kullanın.

Sayfayı ana düzende (kısmi değil) oluşturmak istiyorsanız, ayarlamayın ViewBag.Partial = true. HTML yardımcısına gerek yoktur.


-1

Sanırım diğer posterler kısmi bölümünüze doğrudan bir bölüm eklemek için bir yol sağladı (3. taraf html yardımcılarını kullanarak).

Ancak, betiğinizin kısmi ile sıkı bir şekilde eşleşmesi durumunda, javascriptinizi doğrudan<script> kısmi bir satır içi etiketin içine koyun ve bununla yapın (kısmi birden fazla kez kullanmayı düşünüyorsanız, komut dosyası çoğaltmasına dikkat edin) tek bir görünümde);


1
Bu genellikle ideal değildir çünkü jQuery vb. Yükleme satır içi komut dosyalarından sonra gerçekleşir ... ancak yerel kod için iyi olduğunu düşünüyorum.
Worthy7

-3

_contact.cshtml adında kısmi bir görünümünüz olduğunu varsayalım, kişiniz yasal (ad) veya fiziksel bir konu (ad, soyadı) olabilir. sizin görüşünüz neyin yaratıldığına ve javascript ile elde edilebileceğine dikkat etmelidir. bu nedenle gecikmeli oluşturma ve iç görünümde JS gerekebilir.

bence, nasıl göz ardı edilebileceğinin tek yolu, bu tür kullanıcı arayüzü endişelerini ele almanın göze batmayan bir yolunu oluşturmaktır.

Ayrıca MVC 6'nın View Component olarak adlandırılacağını, MVC vadeli işlemlerinin bile benzer şeyler olduğunu ve Telerik'in de böyle bir şeyi desteklediğini unutmayın ...


1
3 yıl geç ve bunun soruyu bile cevapladığını sanmıyorum? Burada ne demeye çalışıyorsun? 3 yıl sonra geleceğin teknolojilerinin spekülatif özellikleriyle bir soruyu cevaplamak gerçekten bir cevap veya özellikle yararlı değil
dan richardson

-3

Ben sadece kısmi görünümüme bu kodu ekledim ve çok temiz olmasa da, sorunu çözdü, çalışıyor. Oluşturduğunuz nesnelerin kimliklerinin olduğundan emin olmalısınız.

<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

Ben benzer bir sorun bu ile çözüldü vardı:

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

Enjekte etmenin güzel bir yolu sanırım.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.