Rellenar una sección de Razor desde un parcial

102

Mi principal motivación para intentar hacer esto es obtener Javascript que solo es requerido por un parcial en la parte inferior de la página con el resto del Javascript y no en el medio de la página donde se representa el parcial.

Aquí hay un ejemplo simplificado de lo que estoy tratando de hacer:

Aquí está el diseño con una sección de Scripts justo antes del cuerpo.

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

Aquí hay una vista de ejemplo con este diseño.

<h2>This is the view</h2>

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

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

Y aquí está el renderizado parcial de la 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>
}

En este ejemplo, el marcado especificado en la vista se coloca en la sección, pero el marcado del parcial no. ¿Es posible rellenar una sección desde una vista parcial con Razor? Si no es así, ¿cuáles son algunos otros métodos para obtener JavaScript que solo necesitan los parciales en la parte inferior de la página sin incluirlo globalmente?

Craig M
fuente
tal vez sea un problema porque tiene otra sección de script en el parcial ... IDK ... su código es un poco confuso ...
gideon
No es. Incluso si la sección se deja fuera de la vista, el código del parcial no se convierte en la página renderizada final. Creo que SLaks tiene razón en que los parciales no pueden participar en las secciones de la vista principal.
Craig M

Respuestas:

78

La forma en que manejé esto es escribir un par de métodos de extensión para la clase HtmlHelper. Eso permite que las vistas parciales digan que requieren un script, y luego en la vista de diseño que escribe la etiqueta, llamo a mi método auxiliar para emitir los scripts requeridos.

Estos son los métodos de ayuda:

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 vez que tenga eso en su lugar, su vista parcial solo necesita llamar @Html.RequireScript("/Path/To/Script").

Y en la sección de cabecera de la vista de diseño se llama @Html.EmitRequiredScripts().

Una ventaja adicional de esto es que le permite eliminar las solicitudes de script duplicadas. Si tiene múltiples vistas / vistas parciales que necesitan un script determinado, puede asumir con seguridad que solo lo generará una vez

Sr. Bell
fuente
Solución elegante y limpia. +1
bevacqua
Acabo de encontrar esta solución después de tirarme de la mayor parte del cabello - excelente solución ....
higgsy
No puedo hacer que esta solución funcione. Parece que se llama a EmitRequiredScripts () antes de que cualquier vista parcial llegue a llamar a RequireScript (). ¿Estoy haciendo algo mal?
Bryan Roth
Algo no suena bien, Bryan. He utilizado esta solución ampliamente durante el último año y ha funcionado bien. Tal vez publique una nueva pregunta con los detalles de su problema y vincule la URL aquí
Mr Bell
1
¿Tiene esto algún soporte para la prevención de caché al implementar una nueva versión de la aplicación? El método @ scripts.Render () listo para usar agrega un parámetro de URL al final que se genera en el momento de la compilación para que el navegador se vea obligado a buscar la última versión cuando se implementa una nueva versión.
Simon Green
28

Las vistas parciales no pueden participar en las secciones de sus vistas principales.

SLaks
fuente
1
Esto es lo que sospechaba. Gracias.
Craig M
@JohnBubriski Hay en Razor 2. No sé sobre anterior. Versiones
Shimmy Weitzhandler
@SLaks, ¿por qué es esto por diseño? En mi escenario, tengo un parcial que es un rotador de banner, quiero que sus scripts / estilos se carguen solo cuando está encendido, ¿por qué es malo cargarlo en línea?
Shimmy Weitzhandler
2
@Shimmy: debe utilizar un sistema de gestión de recursos, como Cassette.
SLaks
Gracias. Lo miraré.
Shimmy Weitzhandler
13

Podrías tener un segundo parcial que solo se encargue de inyectar el javascript necesario. Coloque varios guiones alrededor de los @ifbloques, si lo desea:

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

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

Obviamente, esto podría limpiarse un poco, pero luego, en la Scriptssección de su vista:

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

Una vez más, puede que no gane un premio de belleza, pero funcionará.

Sergi Papaseit
fuente
1
Esto se acerca bastante a lo que terminé haciendo. Definitivamente no es bonito, pero funciona. El único inconveniente de esto es que no puede obtener el parcial a través de una llamada ajax y tener el JS incluido. Creo que a largo plazo, terminaré refactorizando usando plantillas jQuery y simplemente enviaré JSON desde mis controladores en lugar de construir el html en el lado del servidor.
Craig M
@CraigM hacia ahí también me dirijo. MVC es legítimo, pero tiene mucho más sentido (para mí) usar plantillas del lado del cliente (estoy buscando en Backbone.js) y luego presionar / extraer desde una API.
one.beat.consumer
@ one.beat.customer: he estado usando plantillas de subrayado desde que también uso Backbone, pero estoy pensando en cambiarme a la biblioteca de Hogan desde Twitter o Plates desde Nodejitsu. Ambos tienen características bastante interesantes.
Craig M
10

La forma más elegante de hacer esto es mover scripts de vista parcial a un archivo separado y luego renderizarlo en la sección de vista Scripts:

<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 parcial _ Partial.cshtml :

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

La vista parcial _ PartialScripts.cshtml solo con scripts:

<script type="text/javascript">
    alert("I'm a partial script!");
</script>
Vlad Rudenko
fuente
Esto no es tan automático como algunos de los métodos de extensión o complementos sugeridos en otras respuestas, pero tiene la ventaja de la simplicidad y la claridad. Le gusta.
Mark Meuer
7

Instale el paquete nuget Forloop.HtmlHelpers: agrega algunos ayudantes para administrar scripts en vistas parciales y plantillas de editor.

En algún lugar de su diseño, debe llamar

@Html.RenderScripts()

Aquí será donde se generarán los archivos de secuencia de comandos y los bloques de secuencia de comandos en la página, por lo que recomendaría colocarlo después de sus secuencias de comandos principales en el diseño y después de una sección de secuencias de comandos (si tiene una).

Si está utilizando el marco de optimización web con agrupación, puede utilizar la sobrecarga

@Html.RenderScripts(Scripts.Render)

de modo que este método se utiliza para escribir archivos de script.

Ahora, en cualquier momento que desee agregar archivos de secuencia de comandos o bloques en una vista, vista parcial o plantilla, simplemente use

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

Los ayudantes aseguran que solo se procese una referencia de archivo de script si se agrega varias veces y también asegura que los archivos de script se procesen en el orden esperado

  1. Diseño
  2. Parciales y plantillas (en el orden en que aparecen en la vista, de arriba a abajo)
Russ Cam
fuente
5

[Versión actualizada] Versión actualizada siguiendo la pregunta de @Necrocubus para incluir scripts en línea.

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
}

Mis 2 centavos, es una publicación antigua, pero aún relevante, así que aquí hay una actualización mejorada de la solución del Sr. Bell que funciona con ASP.Net Core.

Permite agregar scripts y estilos al diseño principal desde vistas parciales y subvistas importadas, y la posibilidad de agregar opciones a las importaciones de scripts / estilos (como async diferir, etc.):

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; }
    }
}
Vaquero
fuente
¡Gracias hombre! Esto debería recibir más votos porque es más relevante que la respuesta que tiene 6 años.
Necroqubus
Además, ¿se pueden modificar estas extensiones para permitir que secciones de scripts sean entradas? @ <text> </text> o algo así como secciones? De lo contrario, todavía necesito un pequeño script JS para inicializar el otro script con variables del modelo del lado del servidor: /
Necroqubus
@Necroqubus puedes comprobar la versión actualizada, sin embargo, todavía no la he probado :)
Jean
Muy bien, intentaré probarlo por ti. Espero que funcione con ASP.NET Core 1.0 MVC. Para el contexto, tengo varios niveles de parciales anidados y quiero que sus scripts se representen al pie de página.
Necroqubus
No es necesario <text>, simplemente agréguelo como una cadena (aún puede prefijar con @ "" para múltiples líneas si lo prefiere), y sin las <script>etiquetas
Jean
1

Puede crear una nueva Layoutpágina y envolver el PartialView dentro de una Vista completa que es responsable de representar el contenido y también cualquier sección de la biblioteca.

Por ejemplo, digamos que tengo el siguiente código:

HomeController.cs

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

Cuando se renderiza la vista de página completa, normalmente se renderiza fusionando dos archivos:

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 lo que se especifique en _ViewStart o anulado en la página)

<!DOCTYPE html>

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

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

Ahora , suponga que desea renderizar About.cshtmlcomo una vista parcial , tal vez como una ventana modal en respuesta a la llamada AJAX. El objetivo aquí es devolver solo el contenido especificado en la página Acerca de, scripts y todo, sin toda la hinchazón incluida en el _Layout.cshtmldiseño maestro (como un<html> documento ).

Puede intentarlo así, pero no vendrá con ninguno de los bloques de sección:

return PartialView("About", vm);

En su lugar, agregue una página de diseño más simple como esta:

_PartialLayout.cshtml

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

O para admitir una ventana modal como esta:

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

Luego, puede especificar una vista maestra personalizada en este controlador o cualquier otro controlador que desee para representar el contenido y los scripts de una vista simultáneamente

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

Para aquellos que buscan la versión 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; }
        }
    }

Agregue a su diseño después de la llamada a la sección de procesamiento de scripts:

@HttpContextAccessor.EmitRequiredScripts()

Y en tu vista parcial:

@inject IHttpContextAccessor HttpContextAccessor

...

@HttpContextAccessor.RequireScript("/scripts/moment.min.js")
Sebastián
fuente
0

Basado en la respuesta de Mr Bell y Shimmy anterior, agrego una función adicional para el 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  

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

Muestra en MasterPage: - @ Html.EmitRequiredBundleStyles ()

Harris Yer
fuente
0

Utilice las @using(Html.Delayed()){ ...your content... }extensiones de la respuesta https://stackoverflow.com/a/18790222/1037948 para representar cualquier contenido (scripts o solo HTML) más adelante en la página. El interno Queuedebe garantizar un pedido correcto.

drzaus
fuente
0

Esta funcionalidad también se implementa en ClientDependency.Core.Mvc.dll. Proporciona los ayudantes html: @ Html.RequiresJs y @ Html.RenderJsHere (). Paquete Nuget: ClientDependency-Mvc

Evert
fuente
0

Aquí viene mi solución a las preguntas más frecuentes "¿cómo inyectar secciones de vistas parciales a vistas principales o vista de diseño principal para asp.net mvc?". Si haces una búsqueda en stackoverflow por palabras clave "sección + parcial", obtendrás una lista bastante grande de preguntas relacionadas y respuestas dadas, pero ninguna de ellas me parece elegante por medio de la gramática del motor razor. Así que solo eché un vistazo al motor Razor para ver si podría haber una solución mejor a esta pregunta.

Afortunadamente, encontré algo que me resulta interesante de cómo el motor Razor hace la compilación del archivo de plantilla de vista (* .cshtml, * .vbhtml). (Lo explicaré más adelante), a continuación se muestra mi código de la solución, que creo que es bastante simple y lo suficientemente elegante en su uso.

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

uso : Usar el código también es bastante simple, y tiene casi el mismo estilo que el habitual. También admite cualquier nivel de las vistas parciales anidadas. es decir. Tengo una cadena de plantillas de vista: _ViewStart.cshtml-> layout.cshtml-> index.cshtml -> [head.cshtml, foot.cshtml] -> ad.cshtml.

En layout.cshtml, tenemos:

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

Y en index.cshtml, tenemos:

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

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

Y en head.cshtml, tendríamos el código:

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

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

es lo mismo en foot.cshtml o ad.cshtml, aún puede definir la sección Head o Foot en ellos, asegúrese de llamar a @ Html.EnsureSection () una vez al final del archivo de vista parcial. Eso es todo lo que necesita hacer para deshacerse del problema sujeto en asp mvc.

Solo comparto mi fragmento de código para que otros puedan usarlo. Si cree que es útil, no dude en calificar mi publicación. :)

Jonathan
fuente