Iniezione di contenuti in sezioni specifiche da una vista parziale ASP.NET MVC 3 con Razor View Engine


324

Ho questa sezione definita nel mio _Layout.cshtml

@RenderSection("Scripts", false)

Posso facilmente usarlo da una vista:

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

Ciò con cui sto lottando è come ottenere alcuni contenuti iniettati all'interno di questa sezione da una vista parziale.

Supponiamo che questa sia la mia pagina di visualizzazione:

@section Scripts { 

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

<div>
    poo bar poo
</div>

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

Devo iniettare del contenuto all'interno della Scriptssezione da _myPartialuna vista parziale.

Come posso fare questo?


17
per chiunque verrà in seguito - esiste un pacchetto nuget per la gestione di questo: nuget.org/packages/Forloop.HtmlHelpers
Russ Cam

@RussCam dovresti rispondere a questa domanda. +1 il pacchetto nuget risolve il problema esatto che OP sta avendo.
Carrie Kendall,

1
@RussCam Il pacchetto NuGet non è una soluzione, potrebbe essere il codice del pacchetto.
Maksim Vi.

8
@MaksimVi. bene, ho scritto il pacchetto nuget e non ho intenzione di eliminarlo, quindi piuttosto che ripetere il codice ( bitbucket.org/forloop/forloop-htmlhelpers/src ) o il wiki ( bitbucket.org/forloop/forloop-htmlhelpers/wiki / Home ) qui, un collegamento ad esso come un commento viene mantenuto nello spirito di StackOverflow, IMO.
Russ Cam

Ecco un'altra soluzione che sembra molto bello: stackoverflow.com/questions/5355427/...
jkokorian

Risposte:


235

Le sezioni non funzionano in viste parziali e questo è in base alla progettazione. È possibile utilizzare alcuni helper personalizzati per ottenere un comportamento simile, ma onestamente è responsabilità della vista includere gli script necessari, non la responsabilità parziale. Consiglierei di usare la sezione @scripts della vista principale per farlo e di non preoccupare i parziali degli script.


445
Ma cosa succede se la sceneggiatura è molto specifica per il parziale? Non ha senso logico che sia definito nella vista parziale e non?
Jez,

43
Perché è progettato?
Shimmy Weitzhandler,

56
@Darin: non sono d'accordo. Che dire del principio DRY? Non mi piace ripetermi, anche se si tratta solo di riferimenti di script.
fretje,

14
@fretje, ognuno ha il diritto di esprimere la propria opinione sull'argomento. Rispetto il tuo. Nella mia risposta ho espresso il mio e collegato a una risposta che ti consentirebbe di raggiungere questo obiettivo. Ma ho anche messo in evidenza ciò che consiglierei e farei per questa situazione.
Darin Dimitrov,

33
il distacco di @JoshNoe e il resto - un "widget" (display + interazione avanzata) è un esempio perfetto di una vista parziale strettamente accoppiata al javascript associato. In base alla progettazione , non avrei dovuto scrivere due dichiarazioni include in luoghi diversi per ottenere la piena funzionalità, poiché il display non sarà mai privo dell'interazione con l'operatore e l'interazione non verrà mai visualizzata altrove.
drzaus,

83

Questa è una domanda abbastanza popolare, quindi posterò la mia soluzione.
Ho avuto lo stesso problema e sebbene non sia l'ideale, penso che in realtà funzioni abbastanza bene e non faccia dipendere parzialmente la vista.
Il mio scenario era che un'azione era accessibile da sola ma poteva anche essere incorporata in una vista: una mappa di google.

Nel mio _layoutho:

@RenderSection("body_scripts", false)

Dal mio punto di indexvista ho:

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

Dal mio punto di clientsvista ho (tutta la mappa e assoc. Html):

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

La mia Clients_Scriptsvista contiene il javascript da renderizzare sulla pagina

In questo modo il mio script è isolato e può essere renderizzato nella pagina dove richiesto, con il body_scriptstag che viene visualizzato solo alla prima occorrenza in cui il motore di visualizzazione del rasoio lo trova.

Questo mi permette di avere tutto separato - è una soluzione che funziona abbastanza bene per me, altri potrebbero avere problemi con esso, ma corregge il buco "dal design".


2
Non sono stato io a votarti negativamente, ma dirò che questa soluzione non mi piace molto perché separa ancora gli script specifici della vista dalla vista stessa.
schiaccia il

3
Altre 20 persone e io abbiamo un'opinione diversa. Puoi comunque avere script direttamente correlati a una vista che si trovano in un file separato, è un errore di programmazione se non includi lo script insieme alla tua vista. La sua presenza in un file separato separa l'interazione dalla presentazione e consente numerosi altri vantaggi dal fatto che si trova in un file separato.
Dan Richardson,

1
Hai perfettamente ragione. In realtà sono completamente d'accordo e preferisco questo metodo personalmente. Il vero problema per me è che i miei colleghi lottano con questa separazione. Questo è un problema di dominio, però. Penso che questo metodo sia l'ideale, soprattutto quando si tiene conto di un processo di generazione di JavaScript. Continuerò a lavorare per educare i miei colleghi all'uso di questo metodo e lo supporterò completamente. Penso che la tua risposta potrebbe essere migliorata, però. Tuttavia, non è stato necessario menzionare le "20 persone d'accordo". Solo perché una risposta è popolare, non significa sempre che sia giusta. In questo caso è giusto.
schiaccia il

È vero, e sono sempre felice di accettare feedback costruttivi, modificare il mio codice e rispondere se c'è un miglioramento da ottenere :)
dan richardson,

1
Questa soluzione ha l'ulteriore vantaggio di essere ancora in grado di fare tutte le cose MVC che ti aspetteresti di poter fare in una vista tipica, come la possibilità di JSON codificare un modello passato e generare URL usando Url. Azione. Questo approccio è quindi un modo elegante per configurare i controller AngularJS: ogni vista parziale può rappresentare un controller separato nel modulo Angular. Così pulito!
Dan,

40

Dalle soluzioni in questo thread , ho trovato la seguente soluzione probabilmente troppo complicata che ti consente di ritardare il rendering di qualsiasi html (anche script) all'interno di un blocco usando.

USO

Crea la "sezione"

  1. Scenario tipico: in una vista parziale, includere il blocco solo una volta, indipendentemente da quante volte la vista parziale viene ripetuta nella pagina:

    @using (Html.Delayed(isOnlyOne: "some unique name for this section")) {
        <script>
            someInlineScript();
        </script>
    }
  2. In una vista parziale, includere il blocco per ogni volta che si utilizza il parziale:

    @using (Html.Delayed()) {
        <b>show me multiple times, @Model.Whatever</b>
    }
  3. In una vista parziale, includere il blocco solo una volta, indipendentemente dal numero di volte in cui il parziale viene ripetuto, ma successivamente renderlo specificamente per nome 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>
    }

Rendering delle "sezioni"

(ad es. visualizzare la sezione ritardata in una vista principale)

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

CODICE

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
Wow, è anche complicato per me capire il codice, ma +1 per aver trovato una soluzione
Rameez Ahmed Sayad,

@RameezAhmedSayad hai ragione - tornando qui sono anche confuso da come intendevo dire come usarlo. Aggiornamento della risposta ...
drzaus,

E per chiarire ulteriormente: la ragione per cui ci sono due "nomi" è che se vuoi renderlo solo una volta ha bisogno della chiave univoca nel parametro isOnlyOne, ma solo se vuoi renderlo in una posizione specifica per nome, fornisci l'identificatore, altrimenti viene scaricato Html.RenderDelayed().
drzaus,

Personalmente non penso che ci sarebbe alcuna necessità di acquistare il problema e utilizzare questo approccio, la sezione in viste parziali non è semplicemente necessaria in quanto può essere eliminata e gli script possono andare lì senza definire una sezione. È perché è reso esternamente e se vedi il codice per la pagina renderizzata ti accorgi semplicemente che il codice per la vista parziale non è visibile al suo interno. Quindi, se si tratta di una migliore organizzazione, ecc., Ciò non avrà alcun effetto.
Trascendente il

@Transcendent il "dibattito" è già stato avviato in sede di commento la risposta accettata stackoverflow.com/a/7556594/1037948
drzaus

16

Ho avuto questo problema e ho usato questa tecnica.

È la migliore soluzione che ho trovato molto flessibile.

Inoltre si prega di votare qui per aggiungere il supporto per la dichiarazione sezione cumulativa


9

Se hai un legittimo bisogno di eseguire alcuni jsda un partial, ecco come è possibile farlo, jQueryè necessario:

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

Ho provato @drzaus, ha bisogno di "SeeIfReady" o non funziona.
Cacho Santa,

8

Seguendo il principio discreto , non è necessario che "_myPartial" inserisca il contenuto direttamente nella sezione degli script. È possibile aggiungere quegli script della vista parziale in un .jsfile separato e fare riferimento a loro nella sezione @scripts dalla vista principale.


10
Cosa succederebbe se la vista parziale non fosse visualizzata nella pagina? Facciamo ancora riferimento a quei file .js in parent e lo facciamo sovraccaricare?
Murali Murugesan,

5

C'è un difetto fondamentale nel modo in cui pensiamo al web, specialmente quando si usa MVC. Il difetto è che JavaScript è in qualche modo la responsabilità della vista. Una vista è una vista, JavaScript (comportamentale o meno) è JavaScript. Nel modello MVVM di Silverlight e WPF ci troviamo di fronte a "visualizza prima" o "prima modello". In MVC dovremmo sempre cercare di ragionare dal punto di vista del modello e JavaScript fa parte di questo modello in molti modi.

Suggerirei di utilizzare il modello AMD (a me stesso piace RequireJS ). Separa il tuo JavaScript in moduli, definisci la tua funzionalità e aggancia il tuo html da JavaScript invece di fare affidamento su una vista per caricare JavaScript. Questo pulirà il tuo codice, separerà le tue preoccupazioni e renderà la vita più facile tutto in un colpo solo.


Per circa due o tre mesi circa, sto usando RequireJS e non credo che svilupperò mai un'altra applicazione web senza RequireJS.
Tugberk,

6
Anche JavaScript può essere la responsabilità di Visualizza.
Kelmen,

1
L'uso del modello AMD è una buona idea, ma non sono d'accordo con la tua affermazione che JavaScript fa parte del modello. Spesso è necessario definire il comportamento di visualizzazione, soprattutto se associato a qualcosa come Knockout. Dump di una rappresentazione JSON del modello nella vista JavaScript. Personalmente, utilizzo solo chiusure, uno "spazio dei nomi" personalizzato windowsull'oggetto e includo gli script di libreria prima di qualsiasi parziale.
schiaccia il

Penso che ci sia un malinteso qui. Quando sviluppiamo la maggior parte delle app Web, stiamo effettivamente sviluppando due applicazioni: una che gira sul server e una che gira sul client. Dal punto di vista del server, tutto ciò che si invia al browser è la "vista". In tal senso, JavaScript fa parte della vista. Dal punto di vista dell'app client, HTML puro è view e JS è codice che mette in parallelo M e C in termini MVC del server. Penso che sia per questo che le persone non sono d'accordo qui.
TheAgent

3

L'obiettivo dell'OP è che egli voglia definire gli script incorporati nella sua Vista parziale, che presumo che questo script sia specifico solo per quella Vista parziale e che quel blocco sia incluso nella sua sezione degli script.

Capisco che vuole che quella visione parziale sia autonoma. L'idea è simile ai componenti quando si utilizza Angular.

Il mio modo sarebbe quello di mantenere gli script all'interno della Vista parziale così com'è. Ora il problema è che quando si chiama Vista parziale, è possibile eseguire lo script lì dentro prima di tutti gli altri script (che in genere viene aggiunto alla fine della pagina di layout). In tal caso, hai solo lo script Vista parziale in attesa degli altri script. Esistono diversi modi per farlo. Il più semplice, che ho usato prima, sta usando un evento su body.

Sul mio layout, avrei qualcosa sul fondo in questo modo:

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

Quindi nella mia vista parziale (in basso):

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

      // .. do your thing here

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

Un'altra soluzione sta usando uno stack per inviare tutti gli script e chiamarli alla fine. Un'altra soluzione, come già menzionato, è il modello RequireJS / AMD, che funziona anche molto bene.


2

La prima soluzione che mi viene in mente è usare ViewBag per memorizzare i valori che devono essere resi.

Onestamente non ho mai provato se questo funzionasse da una vista parziale, ma dovrebbe imo.


Ho appena provato; purtroppo non funziona (creato a ViewBag.RenderScripts = new List<string>();nella parte superiore della pagina principale, quindi chiamato @Html.Partial("_CreateUpdatePartial",Model,ViewData), quindi messo @section Scripts {@foreach (string script in ViewBag.RenderScripts) Scripts.Render(script); }}. In Vista parziale ho messo @{ViewBag.RenderScripts = ViewBag.RenderScripts ?? new List<string>();ViewBag.RenderScripts.Add("~/bundles/jquery");}.
JohnLBevan,

2

Non è necessario utilizzare le sezioni in vista parziale.

Includi nella vista parziale. Esegue la funzione dopo aver caricato jQuery. Puoi modificare la clausola de condition per il tuo codice.

<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

È possibile utilizzare questi metodi di estensione : (Salva come 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);
                }
            }
        }
    }
}

Usa così:

Esempio parziale: (_MyPartial.cshtml) Inserisci html nell'if e js nell'altro.

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

Nel tuo _Layout.cshtml, o ovunque tu voglia eseguire il rendering degli script dai parziali, inserisci quanto segue (una volta): Verrà visualizzato solo il javascript di tutti i parziali nella pagina corrente in questa posizione.

@{ Html.RenderPartialScripts(); }

Quindi per usare il tuo parziale, fai semplicemente questo: visualizzerà solo l'html in questa posizione.

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

1

C'è un modo per inserire sezioni in viste parziali, anche se non è carino. Devi avere accesso a due variabili dalla vista padre. Poiché parte dello scopo stesso della vista parziale è creare quella sezione, ha senso richiedere queste variabili.

Ecco come appare inserire una sezione nella vista parziale:

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

E nella pagina che inserisce la vista parziale ...

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

È inoltre possibile utilizzare questa tecnica per definire il contenuto di una sezione a livello di codice in qualsiasi classe.

Godere!


1
Potete per favore e un link a un progetto pienamente funzionante?
Ehsan Zargar Ershadi,

1

L'idea di Plutone in un modo migliore:

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

Vista \ web.config:

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

Visualizza:

@PartialWithScripts("_BackendSearchForm")

Parziale (_BackendSearchForm.cshtml):

@{ RenderScriptsInBasePage(scripts()); }

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

Pagina layout:

@RenderSection("scripts", required: false)

1

Questo ha funzionato per me permettendomi di localizzare javascript e html per una vista parziale nello stesso file. Aiuta con il processo di pensiero per vedere HTML e la parte correlata nello stesso file di vista parziale.


Nella vista che utilizza la vista parziale denominata "_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" } })

 }

Nel file della vista parziale

@model SomeType

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

@if (region == "HTMLSection")
{


}

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

0

Ho risolto questo percorso completamente diverso (perché avevo fretta e non volevo implementare un nuovo HtmlHelper):

Ho racchiuso la mia vista parziale in una grande dichiarazione if-else:

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

Quindi, ho chiamato il Parziale due volte con un ViewData personalizzato:

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

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

Sicuramente l'intera idea è che il consumatore della vista parziale non dovrebbe aver bisogno di sapere che deve includere script, questo è un po 'il problema? Altrimenti potresti anche solo dire @Html.Partial("MyPartialViewScripts")
dan richardson,

No, l'idea è quella di consentire agli script di essere definiti nello stesso documento dell'html, ma sono d'accordo che non sia l'ideale.
Rick Love,

0

Ho avuto un problema simile, dove avevo una pagina principale come segue:

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

...

@Html.Partial("_Charts", Model)

ma la vista parziale dipendeva da alcuni JavaScript nella sezione Script. L'ho risolto codificando la vista parziale come JSON, caricandola in una variabile JavaScript e quindi utilizzandola per popolare un div, quindi:

@{
    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 avresti dovuto risolvere questo problema spostando il tuo JS in un file separato.
Worthy7

0

in modo selettivo, è possibile utilizzare una cartella / index.cshtml come pagina principale, quindi aggiungere gli script di sezione. Quindi, nel tuo layout hai:

@RenderSection("scripts", required: false) 

e il tuo index.cshtml:

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

e funzionerà su tutte le tue visualizzazioni parziali. Funziona per me


0

Usando Mvc Core puoi creare un TagHelper ordinato scriptscome mostrato di seguito. Questo potrebbe essere facilmente trasformato in un sectiontag in cui si assegna anche un nome (o il nome viene preso dal tipo derivato). Si noti che è necessario configurare l'iniezione di dipendenza IHttpContextAccessor.

Quando si aggiungono script (ad es. In parte)

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

All'uscita degli script (ad es. In un file di layout)

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

Codice

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

L'altro giorno ho riscontrato un problema quasi identico, tranne per il fatto che la vista parziale era una risposta a una richiesta AJAX. Nella mia situazione, il parziale era in realtà una pagina intera, ma volevo che fosse accessibile come parziale da altre pagine.

Se si desidera eseguire il rendering parziale delle sezioni, la soluzione più pulita è quella di creare un nuovo layout e utilizzare una variabile ViewBag. Questo non funziona con @Html.Partial()o con il nuovo <partial></partial>, usa AJAX.

Vista principale (che si desidera visualizzare come parziale altrove):

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

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

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

controller:

public IActionResult GetPartial() {

    ViewBag.Partial = true;

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

_layoutPartial.cshtml (nuovo):

@RenderSection("Scripts")
@RenderBody()

Quindi utilizzare AJAX nella tua pagina.

Se si desidera eseguire il rendering della pagina nel layout principale (non parziale), non impostare ViewBag.Partial = true. Non sono necessari helper HTML.


-1

Bene, immagino che gli altri poster ti abbiano fornito un modo per includere direttamente una sezione @ all'interno del tuo parziale (utilizzando helper HTML di terze parti).

Ma, suppongo che, se il tuo script è strettamente accoppiato al tuo parziale, inserisci il tuo javascript direttamente all'interno di un <script>tag inline all'interno del tuo parziale ed eseguilo (fai solo attenzione alla duplicazione dello script se intendi utilizzare il parziale più di una volta in un'unica vista);


1
Questo di solito non è l'ideale perché il caricamento di jQuery ecc. Avverrebbe dopo gli script inline ... ma per il codice nativo credo che vada bene.
Worthy7

-3

supponi di avere una visione parziale chiamata _contact.cshtml, il tuo contatto può essere un soggetto legale (nome) o fisico (nome, cognome). il tuo punto di vista dovrebbe occuparsi di ciò che è reso e che può essere ottenuto con JavaScript. pertanto potrebbe essere necessario un rendering ritardato e JS in vista.

l'unico modo in cui penso, come può essere omesso, è quando creiamo un modo discreto di gestire tali problemi dell'interfaccia utente.

nota anche che MVC 6 avrà un cosiddetto View Component, anche i futures MVC avevano cose simili e Telerik supporta anche una cosa del genere ...


1
3 anni di ritardo, e non penso che questo risponda alla domanda? Cosa stai cercando di dire qui? Rispondere a una domanda 3 anni dopo con le caratteristiche speculative delle tecnologie future non è davvero una risposta o particolarmente utile
dan richardson

-3

Ho appena aggiunto questo codice sulla mia vista parziale e risolto il problema, anche se non molto pulito, funziona. Devi assicurarti che gli ID degli oggetti che stai rappresentando.

<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

Ho avuto il problema simile risolto con questo:

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

Questo è un bel modo di iniettare i guesse.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.