Popolare una sezione Razor da un parziale


102

La mia motivazione principale per provare a farlo è ottenere Javascript che è richiesto solo da un parziale nella parte inferiore della pagina con il resto del Javascript e non nel mezzo della pagina in cui viene visualizzato il parziale.

Ecco un esempio semplificato di quello che sto cercando di fare:

Ecco il layout con una sezione Script subito prima del corpo.

<!DOCTYPE html>
<html>
<head>
    <title>@ViewBag.Title</title>
    <link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />    
</head>

<body>
    @RenderBody()
    <script src="@Url.Content("~/Scripts/jquery-1.4.4.min.js")" type="text/javascript"></script>
    @RenderSection("Scripts", false)
</body>
</html>

Ecco una visualizzazione di esempio utilizzando questo layout.

<h2>This is the view</h2>

@{Html.RenderPartial("_Partial");}

@section Scripts {
<script type="text/javascript">
        alert("I'm a view.");
</script>
}

Ed ecco il parziale renderizzato dalla vista.

<p>This is the partial.</p>

@* this never makes it into the rendered page *@
@section Scripts {
<script type="text/javascript">
    alert("I'm a partial."); 
</script>
}

In questo esempio, il markup specificato nella vista viene inserito nella sezione, ma il markup del partial non lo è. È possibile popolare una sezione da una vista parziale con Razor? In caso contrario, quali sono altri metodi per ottenere Javascript necessari solo ai parziali nella parte inferiore della pagina senza includerlo a livello globale?


forse è un problema perché hai un'altra sezione di script nel parziale .. IDK .. il tuo codice è un po 'confuso ..
gideon

Non è. Anche se la sezione viene lasciata fuori dalla visualizzazione, il codice nel partial non entra nella pagina renderizzata finale. Penso che SLaks sia corretto in quanto i parziali non possono partecipare alle sezioni della vista genitore.
Craig M

Risposte:


78

Il modo in cui ho affrontato questo problema è scrivere un paio di metodi di estensione nella classe HtmlHelper. Ciò consente alle viste parziali di dire che richiedono uno script, e quindi nella vista layout che scrive il tag chiamo al mio metodo di supporto per emettere gli script richiesti

Ecco i metodi di supporto:

public static string RequireScript(this HtmlHelper html, string path, int priority = 1)
{
    var requiredScripts = HttpContext.Current.Items["RequiredScripts"] as List<ResourceInclude>;
    if (requiredScripts == null) HttpContext.Current.Items["RequiredScripts"] = requiredScripts = new List<ResourceInclude>();
    if (!requiredScripts.Any(i => i.Path == path)) requiredScripts.Add(new ResourceInclude() { Path = path, Priority = priority });
    return null;
}

public static HtmlString EmitRequiredScripts(this HtmlHelper html)
{
    var requiredScripts = HttpContext.Current.Items["RequiredScripts"] as List<ResourceInclude>;
    if (requiredScripts == null) return null;
    StringBuilder sb = new StringBuilder();
    foreach (var item in requiredScripts.OrderByDescending(i => i.Priority))
    {
        sb.AppendFormat("<script src=\"{0}\" type=\"text/javascript\"></script>\n", item.Path);
    }
    return new HtmlString(sb.ToString());
}
public class ResourceInclude
{
    public string Path { get; set; }
    public int Priority { get; set; }
}

Una volta impostato, la tua visualizzazione parziale deve solo chiamare @Html.RequireScript("/Path/To/Script").

E nella sezione principale della vista layout si chiama @Html.EmitRequiredScripts().

Un ulteriore vantaggio di questo è che ti consente di eliminare le richieste di script duplicate. Se hai più viste / viste parziali che richiedono un dato script, puoi tranquillamente presumere che lo visualizzerai solo una volta


Soluzione elegante e pulita. +1
bevacqua

Mi sono appena imbattuto in questa soluzione dopo aver tirato fuori la maggior parte dei miei capelli - soluzione eccellente ....
higgsy

Non riesco a far funzionare questa soluzione. Sembra che EmitRequiredScripts () venga chiamato prima che qualsiasi vista parziale possa chiamare RequireScript (). Sto facendo qualcosa di sbagliato?
Bryan Roth

Qualcosa non suona bene, Bryan. Ho utilizzato ampiamente questa soluzione nell'ultimo anno circa e ha funzionato bene. Magari posta una nuova domanda con i dettagli del tuo problema e collega l'URL qui
Mr Bell

1
Questo ha un supporto per il busting della cache durante la distribuzione di una nuova versione dell'app? Il metodo out-of-box @ scripts.Render () inserisce un parametro URL all'estremità che viene generato in fase di compilazione in modo che il browser sia costretto a recuperare l'ultima versione quando viene distribuita una nuova versione.
Simon Green

28

Le viste parziali non possono partecipare alle sezioni delle viste principali.


1
Questo è quello che sospettavo. Grazie.
Craig M

@JohnBubriski C'è in Razor 2. Non so di prev. versioni.
Shimmy Weitzhandler

@ SLaks, perché è di design? Nel mio scenario ho un parziale che è un rotatore di banner, voglio che i suoi script / stili vengano caricati solo quando è acceso, perché è sbagliato caricarlo in linea?
Shimmy Weitzhandler

2
@ Shimmy: dovresti usare un sistema di gestione delle risorse, come Cassette.
SLaks

Grazie. Lo esaminerò.
Shimmy Weitzhandler

13

Potresti avere un secondo parziale che si occupa solo di iniettare il javascript necessario. Metti diversi script lì intorno ai @ifblocchi, se vuoi:

@model string
@if(Model == "bla") {
    <script type="text/javascript">...</script>
}

@else if(Model == "bli") {
    <script type="text/javascript">...</script>
}

Questo potrebbe ovviamente essere ripulito un po ', ma poi, nella Scriptssezione della tua vista:

@section Scripts
{
    @Html.Partial("_Scripts", "ScriptName_For_Partial1")
}

Ancora una volta, potrebbe non vincere un premio di bellezza ma funzionerà.


1
Questo è abbastanza vicino a quello che ho finito per fare. Non è decisamente carino, ma funziona. L'unico aspetto negativo di questo è che non puoi ottenere il parziale tramite una chiamata ajax e avere il JS incluso. Penso che a lungo termine finirò per eseguire il refactoring utilizzando modelli jQuery e invierò semplicemente JSON dai miei controller invece di creare l'html sul lato server.
Craig M

@ CraigM è anche lì che sto andando. MVC è legittimo, ma ha molto più senso (per me) utilizzare modelli lato client (sto esaminando Backbone.js) e quindi eseguire il push / pull da un'API.
one.beat.consumer

@ one.beat.customer - Uso i modelli di sottolineatura da quando uso anche Backbone, ma sto pensando di passare alla libreria Hogan da Twitter o a Plates da Nodejitsu. Entrambi hanno caratteristiche piuttosto carine.
Craig M

10

Il modo più elegante per farlo è spostare gli script della vista parziale in un file separato e quindi renderlo nella sezione della vista Script:

<h2>This is the view</h2>

@Html.RenderPartial("_Partial")

@section Scripts
{
    @Html.RenderPartial("_PartialScripts")

    <script type="text/javascript">
        alert("I'm a view script.");
    </script>
}

La vista parziale _ Partial.cshtml :

<p>This is the partial.</p>

La vista parziale _ PartialScripts.cshtml solo con script:

<script type="text/javascript">
    alert("I'm a partial script!");
</script>

Non è così automatico come alcuni dei metodi di estensione o plug-in suggeriti in altre risposte, ma ha il vantaggio della semplicità e della chiarezza. Gli piace.
Mark Meuer

7

Installa il pacchetto nuget Forloop.HtmlHelpers: aggiunge alcuni helper per la gestione degli script nelle viste parziali e nei modelli di editor.

Da qualche parte nel tuo layout, devi chiamare

@Html.RenderScripts()

Questo sarà il punto in cui tutti i file di script e i blocchi di script verranno emessi nella pagina, quindi consiglierei di metterlo dopo gli script principali nel layout e dopo una sezione degli script (se ne hai uno).

Se stai usando The Web Optimization Framework con bundling, puoi usare overload

@Html.RenderScripts(Scripts.Render)

in modo che questo metodo venga utilizzato per scrivere file di script.

Ora, ogni volta che desideri aggiungere file di script o blocchi in una vista, una vista parziale o un modello, usa semplicemente

@using (Html.BeginScriptContext())
{
  Html.AddScriptFile("~/Scripts/jquery.validate.js");
  Html.AddScriptBlock(
    @<script type="text/javascript">
       $(function() { $('#someField').datepicker(); });
     </script>
  );
}

Gli helper assicurano che venga eseguito il rendering di un solo riferimento al file di script se aggiunto più volte e assicurano inoltre che i file di script siano visualizzati nell'ordine previsto, ad es.

  1. disposizione
  2. Parziali e modelli (nell'ordine in cui appaiono nella vista, dall'alto verso il basso)

5

[Versione aggiornata] Versione aggiornata dopo la domanda di @Necrocubus per includere gli script inline.

public static class ScriptsExtensions
{
    const string REQ_SCRIPT = "RequiredScript";
    const string REQ_INLINESCRIPT = "RequiredInlineScript";
    const string REQ_STYLE = "RequiredStyle";

    #region Scripts
    /// <summary>
    /// Adds a script 
    /// </summary>
    /// <param name="html"></param>
    /// <param name="path"></param>
    /// <param name="priority">Ordered by decreasing priority </param>
    /// <param name="bottom"></param>
    /// <param name="options"></param>
    /// <returns></returns>
    public static string RequireScript(this IHtmlHelper html, string path, int priority = 1, bool bottom=false, params string[] options)
    {
        var ctxt = html.ViewContext.HttpContext;

        var requiredScripts = ctxt.Items[REQ_SCRIPT] as List<ResourceToInclude>;
        if (requiredScripts == null) ctxt.Items[REQ_SCRIPT] = requiredScripts = new List<ResourceToInclude>();
        if (!requiredScripts.Any(i => i.Path == path)) requiredScripts.Add(new ResourceToInclude() { Path = path, Priority = priority, Options = options, Type=ResourceType.Script, Bottom=bottom});
        return null;
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="html"></param>
    /// <param name="script"></param>
    /// <param name="priority">Ordered by decreasing priority </param>
    /// <param name="bottom"></param>
    /// <returns></returns>
    public static string RequireInlineScript(this IHtmlHelper html, string script, int priority = 1, bool bottom = false)
    {
        var ctxt = html.ViewContext.HttpContext;

        var requiredScripts = ctxt.Items[REQ_INLINESCRIPT] as List<InlineResource>;
        if (requiredScripts == null) ctxt.Items[REQ_INLINESCRIPT] = requiredScripts = new List<InlineResource>();
        requiredScripts.Add(new InlineResource() { Content=script, Priority = priority, Bottom=bottom, Type=ResourceType.Script});
        return null;
    }

    /// <summary>
    /// Just call @Html.EmitRequiredScripts(false)
    /// at the end of your head tag and 
    /// @Html.EmitRequiredScripts(true) at the end of the body if some scripts are set to be at the bottom.
    /// </summary>
    public static HtmlString EmitRequiredScripts(this IHtmlHelper html, bool bottom)
    {
        var ctxt = html.ViewContext.HttpContext;

        var requiredScripts = ctxt.Items[REQ_SCRIPT] as List<ResourceToInclude>;
        var requiredInlineScripts = ctxt.Items[REQ_INLINESCRIPT] as List<InlineResource>;
        var scripts = new List<Resource>();
        scripts.AddRange(requiredScripts ?? new List<ResourceToInclude>());
        scripts.AddRange(requiredInlineScripts ?? new List<InlineResource>());
        if (scripts.Count==0) return null;
        StringBuilder sb = new StringBuilder();
        foreach (var item in scripts.Where(s=>s.Bottom==bottom).OrderByDescending(i => i.Priority))
        {
            sb.Append(item.ToString());
        }
        return new HtmlString(sb.ToString());
    }
    #endregion Scripts

    #region Styles
    /// <summary>
    /// 
    /// </summary>
    /// <param name="html"></param>
    /// <param name="path"></param>
    /// <param name="priority">Ordered by decreasing priority </param>
    /// <param name="options"></param>
    /// <returns></returns>
    public static string RequireStyle(this IHtmlHelper html, string path, int priority = 1, params string[] options)
    {
        var ctxt = html.ViewContext.HttpContext;

        var requiredScripts = ctxt.Items[REQ_STYLE] as List<ResourceToInclude>;
        if (requiredScripts == null) ctxt.Items[REQ_STYLE] = requiredScripts = new List<ResourceToInclude>();
        if (!requiredScripts.Any(i => i.Path == path)) requiredScripts.Add(new ResourceToInclude() { Path = path, Priority = priority, Options = options });
        return null;
    }

    /// <summary>
    /// Just call @Html.EmitRequiredStyles()
    /// at the end of your head tag
    /// </summary>
    public static HtmlString EmitRequiredStyles(this IHtmlHelper html)
    {
        var ctxt = html.ViewContext.HttpContext;

        var requiredScripts = ctxt.Items[REQ_STYLE] as List<ResourceToInclude>;
        if (requiredScripts == null) return null;
        StringBuilder sb = new StringBuilder();
        foreach (var item in requiredScripts.OrderByDescending(i => i.Priority))
        {
            sb.Append(item.ToString());
        }
        return new HtmlString(sb.ToString());
    }
    #endregion Styles

    #region Models
    public class InlineResource : Resource
    {
        public string Content { get; set; }
        public override string ToString()
        {
            return "<script>"+Content+"</script>";
        }
    }

    public class ResourceToInclude : Resource
    {
        public string Path { get; set; }
        public string[] Options { get; set; }
        public override string ToString()
        {
            switch(Type)
            {
                case ResourceType.CSS:
                    if (Options == null || Options.Length == 0)
                        return String.Format("<link rel=\"stylesheet\" href=\"{0}\" type=\"text/css\" />\n", Path);
                    else
                        return String.Format("<link rel=\"stylesheet\" href=\"{0}\" type=\"text/css\" {1} />\n", Path, String.Join(" ", Options));
                default:
                case ResourceType.Script:
                    if (Options == null || Options.Length == 0)
                        return String.Format("<script src=\"{0}\" type=\"text/javascript\"></script>\n", Path);
                    else
                        return String.Format("<script src=\"{0}\" type=\"text/javascript\" {1}></script>\n", Path, String.Join(" ", Options));
            }
        }
    }
    public class Resource
    {
        public ResourceType Type { get; set; }
        public int Priority { get; set; }
        public bool Bottom { get; set; }
    }
    public enum ResourceType
    {
        Script,
        CSS
    }
    #endregion Models
}

I miei 2 centesimi, è un vecchio post, ma ancora rilevante, quindi ecco un aggiornamento aggiornato della soluzione di Mr Bell che funziona con ASP.Net Core.

Consente di aggiungere script e stili al layout principale da viste parziali e sottoview importate e possibilità di aggiungere opzioni alle importazioni di script / stili (come il differimento asincrono, ecc.):

public static class ScriptsExtensions
{
    const string REQ_SCRIPT = "RequiredScript";
    const string REQ_STYLE = "RequiredStyle";

    public static string RequireScript(this IHtmlHelper html, string path, int priority = 1, params string[] options)
    {
        var ctxt = html.ViewContext.HttpContext;

        var requiredScripts = ctxt.Items[REQ_SCRIPT] as List<ResourceInclude>;
        if (requiredScripts == null) ctxt.Items[REQ_SCRIPT] = requiredScripts = new List<ResourceInclude>();
        if (!requiredScripts.Any(i => i.Path == path)) requiredScripts.Add(new ResourceInclude() { Path = path, Priority = priority, Options = options });
        return null;
    }


    public static HtmlString EmitRequiredScripts(this IHtmlHelper html)
    {
        var ctxt = html.ViewContext.HttpContext;

        var requiredScripts = ctxt.Items[REQ_SCRIPT] as List<ResourceInclude>;
        if (requiredScripts == null) return null;
        StringBuilder sb = new StringBuilder();
        foreach (var item in requiredScripts.OrderByDescending(i => i.Priority))
        {
            if (item.Options == null || item.Options.Length == 0)
                sb.AppendFormat("<script src=\"{0}\" type=\"text/javascript\"></script>\n", item.Path);
            else
                sb.AppendFormat("<script src=\"{0}\" type=\"text/javascript\" {1}></script>\n", item.Path, String.Join(" ", item.Options));

        }
        return new HtmlString(sb.ToString());
    }


    public static string RequireStyle(this IHtmlHelper html, string path, int priority = 1, params string[] options)
    {
        var ctxt = html.ViewContext.HttpContext;

        var requiredScripts = ctxt.Items[REQ_STYLE] as List<ResourceInclude>;
        if (requiredScripts == null) ctxt.Items[REQ_STYLE] = requiredScripts = new List<ResourceInclude>();
        if (!requiredScripts.Any(i => i.Path == path)) requiredScripts.Add(new ResourceInclude() { Path = path, Priority = priority, Options = options });
        return null;
    }


    public static HtmlString EmitRequiredStyles(this IHtmlHelper html)
    {
        var ctxt = html.ViewContext.HttpContext;

        var requiredScripts = ctxt.Items[REQ_STYLE] as List<ResourceInclude>;
        if (requiredScripts == null) return null;
        StringBuilder sb = new StringBuilder();
        foreach (var item in requiredScripts.OrderByDescending(i => i.Priority))
        {
            if (item.Options == null || item.Options.Length == 0)
                sb.AppendFormat("<link rel=\"stylesheet\" href=\"{0}\" type=\"text/css\" />\n", item.Path);
            else
                sb.AppendFormat("<link rel=\"stylesheet\" href=\"{0}\" type=\"text/css\" {1} />\n", item.Path, String.Join(" ", item.Options));
        }
        return new HtmlString(sb.ToString());
    }


    public class ResourceInclude
    {
        public string Path { get; set; }
        public int Priority { get; set; }
        public string[] Options { get; set; }
    }
}

Grazie amico! Questo dovrebbe essere votato di più perché è più pertinente della risposta che ha 6 anni.
Necroqubus

Inoltre, queste estensioni possono essere modificate per consentire a sezioni di script di essere input? @ <text> </text> o qualcosa di simile a sezioni? Altrimenti ho ancora bisogno di un piccolo script JS per inizializzare l'altro script con le variabili del modello lato server: /
Necroqubus

@Necroqubus puoi controllare la versione aggiornata, tuttavia non l'ho ancora testata :)
Jean

Va bene, proverò a testarlo per te. Spero che funzioni con ASP.NET Core 1.0 MVC. Per il contesto ho più livelli di parziali annidati e desidero che i loro script vengano visualizzati a piè di pagina.
Necroqubus

Non è necessario <text>, aggiungilo come stringa (puoi ancora aggiungere il prefisso @ "" per più righe se preferisci) e senza i <script>tag
Jean

1

È possibile creare una nuova Layoutpagina e avvolgere la PartialView all'interno di una visualizzazione completa che è responsabile del rendering dei contenuti e anche di eventuali sezioni della libreria.

Ad esempio, diciamo che ho il seguente codice:

HomeController.cs

[HttpGet]
public ActionResult About()
{
    var vm = new AboutViewModel();
    return View("About", vm);
}

Quando viene eseguito il rendering della vista Pagina intera, viene in genere visualizzato unendo due file:

About.cshtml

@model AboutViewModel

@{
    ViewBag.Title = "About CSHN";
}

<h3>@ViewBag.Title</h3>

@section Styles {
    <style> /* style info here */ </style>
}

@section Scripts {
    <script> /* script info here */ </script>
}

_Layout.cshtml (o qualsiasi cosa sia specificata in _ViewStart o sovrascritta nella pagina)

<!DOCTYPE html>

<html>
<head>
    @RenderSection("Styles", false)
    <title>@ViewBag.Title</title>
</head>
<body>
    @RenderBody()

    @RenderSection("scripts", false)
</body>
</html>

Supponiamo ora di voler eseguire il rendering About.cshtmlcome vista parziale , forse come finestra modale in risposta alla chiamata AJAX. L'obiettivo qui è di restituire solo il contenuto specificato nella pagina delle informazioni, script e tutto il resto, senza tutto il rigonfiamento incluso nel _Layout.cshtmllayout principale (come un <html>documento completo ).

Potresti provarlo in questo modo, ma non verrà fornito con nessuno dei blocchi di sezione:

return PartialView("About", vm);

Invece, aggiungi una pagina di layout più semplice come questa:

_PartialLayout.cshtml

<div>
    @RenderBody()
    @RenderSection("Styles", false)
    @RenderSection("scripts", false)
</div>

O per supportare una finestra modale come questa:

_ModalLayout.cshtml

<div class="modal modal-page fade" tabindex="-1" role="dialog" >
    <div class="modal-dialog modal-lg" role="document">
        <div class="modal-content">

            <div class="modal-header">
                <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
                <h4 class="modal-title">@ViewBag.Title</h4>
            </div>

            <div class="modal-body">

                @RenderBody()
                @RenderSection("Styles", false)
                @RenderSection("scripts", false)

            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-inverse" data-dismiss="modal">Dismiss</button>
            </div>
        </div>
    </div>
</div>

Quindi puoi specificare una vista principale personalizzata in questo controller o in qualsiasi altro gestore di cui desideri eseguire il rendering simultaneo dei contenuti e degli script di una vista

[HttpGet]
public ActionResult About()
{
    var vm = new AboutViewModel();
    return !Request.IsAjaxRequest()
              ? View("About", vm)
              : View("About", "~/Views/Shared/_ModalLayout.cshtml", vm);
}

1

Per chi cerca la versione aspnet core 2.0:

    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using Microsoft.AspNetCore.Html;
    using Microsoft.AspNetCore.Http;

    public static class HttpContextAccessorExtensions
    {
        public static string RequireScript(this IHttpContextAccessor htmlContextAccessor, string path, int priority = 1)
        {
            var requiredScripts = htmlContextAccessor.HttpContext.Items["RequiredScripts"] as List<ResourceInclude>;
            if (requiredScripts == null) htmlContextAccessor.HttpContext.Items["RequiredScripts"] = requiredScripts = new List<ResourceInclude>();
            if (requiredScripts.All(i => i.Path != path)) requiredScripts.Add(new ResourceInclude() { Path = path, Priority = priority });
            return null;
        }

        public static HtmlString EmitRequiredScripts(this IHttpContextAccessor htmlContextAccessor)
        {
            var requiredScripts = htmlContextAccessor.HttpContext.Items["RequiredScripts"] as List<ResourceInclude>;
            if (requiredScripts == null) return null;
            StringBuilder sb = new StringBuilder();
            foreach (var item in requiredScripts.OrderByDescending(i => i.Priority))
            {
                sb.AppendFormat("<script src=\"{0}\" type=\"text/javascript\"></script>\n", item.Path);
            }
            return new HtmlString(sb.ToString());
        }
        public class ResourceInclude
        {
            public string Path { get; set; }
            public int Priority { get; set; }
        }
    }

Aggiungi al tuo layout dopo la chiamata alla sezione di rendering degli script:

@HttpContextAccessor.EmitRequiredScripts()

E nella tua visione parziale:

@inject IHttpContextAccessor HttpContextAccessor

...

@HttpContextAccessor.RequireScript("/scripts/moment.min.js")

0

Basandomi sulla risposta di Mr Bell e Shimmy sopra, aggiungo una funzione extra per lo script Bundle.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Text;
using System.Web.Mvc;
namespace ABC.Utility
{
public static  class PartialViewHelper
{
    public static string RequireScript(this HtmlHelper html, string path, int priority = 1)
    {
        var requiredScripts = HttpContext.Current.Items["RequiredScripts"] as List<ResourceInclude>;
        if (requiredScripts == null) HttpContext.Current.Items["RequiredScripts"] = requiredScripts = new List<ResourceInclude>();
        if (!requiredScripts.Any(i => i.Path == path)) requiredScripts.Add(new ResourceInclude() { Path = path, Priority = priority });
        return null;
    }

    public static string RequireBundleStyles(this HtmlHelper html, string bundleName)
    {
        var a = System.Web.Optimization.Styles.Render(bundleName);
        var requiredStyles = HttpContext.Current.Items["RequiredStyles"] as IHtmlString;
        if (requiredStyles == null) HttpContext.Current.Items["RequiredStyles"] = requiredStyles = a;
        return null;
    }

    public static string RequireBundleScripts(this HtmlHelper html, string bundleName)
    {
        var a=System.Web.Optimization.Scripts.Render(bundleName);
        var requiredScripts = HttpContext.Current.Items["RequiredScripts"] as IHtmlString;
        if (requiredScripts == null) HttpContext.Current.Items["RequiredScripts"] = requiredScripts = a;
        return null;
    }

    public static HtmlString EmitRequiredBundleStyles(this HtmlHelper html)
    {
        var requiredStyles = HttpContext.Current.Items["RequiredStyles"] as IHtmlString;
        if (requiredStyles == null) return null;
        return MvcHtmlString.Create(requiredStyles.ToHtmlString()) ;
    }

    public static HtmlString EmitRequiredBundleScripts(this HtmlHelper html)
    {
        var requiredScripts = HttpContext.Current.Items["RequiredScripts"] as IHtmlString;
        if (requiredScripts == null) return null;
        return MvcHtmlString.Create(requiredScripts.ToHtmlString());
    }

    public static HtmlString EmitRequiredScripts(this HtmlHelper html)
    {
        var requiredScripts = HttpContext.Current.Items["RequiredScripts"] as List<ResourceInclude>;
        if (requiredScripts == null) return null;
        StringBuilder sb = new StringBuilder();
        foreach (var item in requiredScripts.OrderByDescending(i => i.Priority))
        {
            sb.AppendFormat("<script src=\"{0}\" type=\"text/javascript\"></script>\n", item.Path);
        }
        return new HtmlString(sb.ToString());
    }
    public class ResourceInclude
    {
        public string Path { get; set; }
        public int Priority { get; set; }
    }
}//end class
}// end namespace  

Esempio su PartialView: - @ Html.RequireBundleStyles ("~ / bundles / fileupload / bootstrap / BasicPlusUI / css"); @ Html.RequireBundleScripts ( "~ / fasci / fileupload / bootstrap / BasicPlusUI / js");

Esempio su MasterPage: - @ Html.EmitRequiredBundleStyles ()


0

Utilizza le @using(Html.Delayed()){ ...your content... }estensioni dalla risposta https://stackoverflow.com/a/18790222/1037948 per eseguire il rendering di qualsiasi contenuto (script o solo HTML) più avanti nella pagina. L'interno Queuedovrebbe garantire un ordine corretto.


0

Questa funzionalità è implementata anche in ClientDependency.Core.Mvc.dll. Fornisce gli helper html: @ Html.RequiresJs e @ Html.RenderJsHere (). Pacchetto Nuget: ClientDependency-Mvc


0

Ecco la mia soluzione alle domande frequenti "come inserire sezioni da viste parziali a viste principali o vista layout principale per asp.net mvc?". Se effettui una ricerca su stackoverflow con le parole chiave "sezione + parziale", otterrai un elenco piuttosto ampio di domande correlate e risposte date, ma nessuna di esse mi sembra elegante per mezzo della grammatica del motore rasoio. Quindi ho solo dato un'occhiata al motore Razor per vedere se potrebbe esserci una soluzione migliore a questa domanda.

Fortunatamente, ho trovato qualcosa che mi interessa di come il motore Razor esegue la compilazione per il file del modello di visualizzazione (* .cshtml, * .vbhtml). (Spiegherò più avanti), di seguito è riportato il mio codice della soluzione che penso sia abbastanza semplice ed elegante da usare.

namespace System.Web.Mvc.Html
{
    public static class HtmlHelperExtensions
    {
        /// <summary>
        /// 确保所有视图,包括分部视图(PartialView)中的节(Section)定义被按照先后顺序追加到最终文档输出流中
        /// </summary>
        public static MvcHtmlString EnsureSection(this HtmlHelper helper)
        {
            var wp = (WebViewPage)helper.ViewDataContainer;
            Dictionary<string, WebPages.SectionWriter> sw = (Dictionary<string, WebPages.SectionWriter>)typeof(WebPages.WebPageBase).GetProperty("SectionWriters", Reflection.BindingFlags.NonPublic | Reflection.BindingFlags.Instance).GetValue(wp);
            if (!helper.ViewContext.HttpContext.Items.Contains("SectionWriter"))
            {
                Dictionary<string, Stack<WebPages.SectionWriter>> qss = new Dictionary<string, Stack<WebPages.SectionWriter>>();
                helper.ViewContext.HttpContext.Items["SectionWriter"] = qss;
            }
            var eqs = (Dictionary<string, Stack<WebPages.SectionWriter>>)helper.ViewContext.HttpContext.Items["SectionWriter"];
            foreach (var kp in sw)
            {
                if (!eqs.ContainsKey(kp.Key)) eqs[kp.Key] = new Stack<WebPages.SectionWriter>();
                eqs[kp.Key].Push(kp.Value);
            }
            return MvcHtmlString.Create("");
        }

        /// <summary>
        /// 在文档流中渲染指定的节(Section)
        /// </summary>
        public static MvcHtmlString RenderSectionEx(this HtmlHelper helper, string section, bool required = false)
        {
            if (helper.ViewContext.HttpContext.Items.Contains("SectionWriter"))
            {
                Dictionary<string, Stack<WebPages.SectionWriter>> qss = (Dictionary<string, Stack<WebPages.SectionWriter>>)helper.ViewContext.HttpContext.Items["SectionWriter"];
                if (qss.ContainsKey(section))
                {
                    var wp = (WebViewPage)helper.ViewDataContainer;
                    var qs = qss[section];
                    while (qs.Count > 0)
                    {
                        var sw = qs.Pop();
                        var os = ((WebViewPage)sw.Target).OutputStack;
                        if (os.Count == 0) os.Push(wp.Output);
                        sw.Invoke();
                    }
                }
                else if (!qss.ContainsKey(section) && required)
                {
                    throw new Exception(string.Format("'{0}' section is not defined.", section));
                }
            }
            return MvcHtmlString.Create("");
        }
    }
}

utilizzo : Anche usare il codice è abbastanza semplice e sembra quasi lo stesso stile del solito. Supporta anche qualsiasi livello per le viste parziali nidificate. vale a dire. Ho una catena di modelli di visualizzazione: _ViewStart.cshtml-> layout.cshtml-> index.cshtml -> [head.cshtml, foot.cshtml] -> ad.cshtml.

In layout.cshtml, abbiamo:

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title>@ViewBag.Title - @ViewBag.WebSetting.Site.WebName</title>
    <base href="@ViewBag.Template/" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta http-equiv="Cache-Control" content="no-siteapp" />
    <meta name="viewport" content="width=device-width, initial-scale=1,maximum-scale=1.0, user-scalable=0,user-scalable=no">
    <meta name="format-detection" content="telephone=no">
    <meta name="renderer" content="webkit">
    <meta name="author" content="Taro Technology Co.,LTD" />
    <meta name="robots" content="index,follow" />
    <meta name="description" content="" />
    <meta name="keywords" content="" />
    <link rel="alternate icon" type="@ViewBag.WebSetting.Site.WebFavIcon" href="@ViewBag.WebSetting.Site.WebFavIcon">
    @Html.RenderSectionEx("Head")
</head>
<body>
    @RenderBody()
    @Html.RenderSectionEx("Foot")
</body>
</html>

E in index.cshtml, abbiamo:

@{
    ViewBag.Title = "首页";
}

@Html.Partial("head")
<div class="am-container-1">
    .......
</div>
@Html.Partial("foot")

E in head.cshtml, avremmo il codice:

@section Head{
    <link rel="stylesheet" href="assets/css/amazeui.css" />
    <link rel="stylesheet" href="assets/css/style.css" />
}

<header class="header">
   ......
</header>
@Html.EnsureSection()

è lo stesso in foot.cshtml o ad.cshtml, puoi ancora definire la sezione Head o Foot in essi, assicurati di chiamare @ Html.EnsureSection () una volta alla fine del file di visualizzazione parziale. Questo è tutto ciò che devi fare per sbarazzarti del problema sottoposto in asp mvc.

Condivido il mio frammento di codice in modo che altri possano utilizzarlo. Se ritieni che sia utile, non esitare a valutare il mio post. :)

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.