Remplir une section de rasoir à partir d'un élément

102

Ma principale motivation pour essayer de faire cela est d'obtenir du Javascript qui n'est requis que par un partiel en bas de page avec le reste du Javascript et non au milieu de la page où le partiel est rendu.

Voici un exemple simplifié de ce que j'essaie de faire:

Voici la mise en page avec une section Scripts juste avant le corps.

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

Voici un exemple de vue utilisant cette disposition.

<h2>This is the view</h2>

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

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

Et voici le rendu partiel de la vue.

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

Dans cet exemple, le balisage spécifié dans la vue est placé dans la section, mais le balisage du partiel ne l'est pas. Est-il possible de remplir une section à partir d'une vue partielle avec Razor? Sinon, quelles sont les autres méthodes pour obtenir du Javascript qui ne sont nécessaires que pour les partiels au bas de la page sans l'inclure globalement?

Craig M
la source
c'est peut-être un problème parce que vous avez une autre section de script dans le partiel .. IDK .. votre code est un peu déroutant ..
gideon
Ce n'est pas. Même si la section est laissée en dehors de la vue, le code du partiel ne le fait pas dans la page rendue finale. Je pense que SLaks a raison en ce sens que les partiels ne peuvent pas participer aux sections de la vue parent.
Craig M

Réponses:

78

La façon dont j'ai géré cela est d'écrire quelques méthodes d'extension dans la classe HtmlHelper. Cela permet aux vues partielles de dire qu'elles nécessitent un script, puis dans la vue de mise en page qui écrit la balise que j'appelle à ma méthode d'assistance pour émettre les scripts requis

Voici les méthodes d'aide:

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

Une fois que vous l'avez en place, votre vue partielle n'a plus qu'à appeler @Html.RequireScript("/Path/To/Script").

Et dans la section principale de la vue de mise en page, vous appelez @Html.EmitRequiredScripts().

Un avantage supplémentaire de ceci est qu'il vous permet d'éliminer les demandes de script en double. Si vous avez plusieurs vues / vues partielles qui nécessitent un script donné, vous pouvez supposer en toute sécurité que vous ne le sortirez qu'une seule fois

Monsieur Bell
la source
Solution élégante et propre. +1
bevacqua
Je viens de
tomber
Je n'arrive pas à faire fonctionner cette solution. Il semble que EmitRequiredScripts () soit appelé avant qu'une vue partielle ne puisse appeler RequireScript (). Est-ce que je fais quelque chose de mal?
Bryan Roth
Quelque chose ne va pas, Bryan. J'ai largement utilisé cette solution au cours de la dernière année et cela fonctionne bien. Peut-être postez une nouvelle question avec les détails de votre problème et liez l'url ici
Mr Bell
1
Est-ce que cela prend en charge le contournement du cache lors du déploiement d'une nouvelle version de l'application? La méthode prête à l'emploi @ scripts.Render () colle à la fin un paramètre URL qui est généré au moment de la construction afin que le navigateur soit obligé de récupérer la dernière version lorsqu'une nouvelle version est déployée.
Simon Green
28

Les vues partielles ne peuvent pas participer aux sections de leurs vues parentes.

SLaks
la source
1
C'est ce que je soupçonnais. Merci.
Craig M
@JohnBubriski Il y en a dans Razor 2. Je ne sais pas pour prev. versions.
Shimmy Weitzhandler
@SLaks, pourquoi est-ce par conception? Dans mon scénario, j'ai un partiel qui est un rotateur de bannière, je veux que ses scripts / styles ne se chargent que lorsqu'il est activé, pourquoi est-il mauvais de le charger en ligne?
Shimmy Weitzhandler
2
@Shimmy: Vous devez utiliser un système de gestion des ressources, tel que Cassette.
SLaks
Je vous remercie. Je vais l'examiner.
Shimmy Weitzhandler
14

Vous pourriez avoir un deuxième partiel qui se charge uniquement d'injecter le javascript nécessaire. Placez-y plusieurs scripts autour des @ifblocs, si vous le souhaitez:

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

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

Cela pourrait évidemment être nettoyé un peu, mais ensuite, dans la Scriptssection de votre vue:

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

Encore une fois, cela ne gagnera peut-être pas de prix de beauté, mais cela fonctionnera.

Sergi Papaseit
la source
1
C'est assez proche de ce que j'ai fini par faire. Ce n'est certainement pas joli, mais ça marche. Le seul inconvénient est que vous ne pouvez pas obtenir le partiel via un appel ajax et avoir le JS inclus. Je pense qu'à long terme, je vais finir par refactoriser à l'aide de modèles jQuery et simplement envoyer JSON depuis mes contrôleurs au lieu de construire le html côté serveur.
Craig M
@CraigM c'est là que je vais aussi. MVC est légitime, mais il est tellement plus logique (pour moi) d'utiliser des modèles côté client (je regarde dans Backbone.js), puis de pousser / tirer à partir d'une API.
one.beat.consumer
@ one.beat.customer - J'utilise les modèles de soulignement depuis que j'utilise également Backbone, mais je pense passer soit à la bibliothèque Hogan de Twitter, soit aux plaques de Nodejitsu. Les deux ont de jolies fonctionnalités.
Craig M
10

La manière la plus élégante de le faire est de déplacer les scripts de vue partielle dans un fichier séparé, puis de les rendre dans la section Scripts de la vue:

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

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

La vue partielle _ PartialScripts.cshtml avec des scripts uniquement:

<script type="text/javascript">
    alert("I'm a partial script!");
</script>
Vlad Rudenko
la source
Ce n'est pas aussi automatique que certaines des méthodes d'extension ou des plug-ins suggérés dans d'autres réponses, mais cela présente l'avantage de la simplicité et de la clarté. Il aime ça.
Mark Meuer
7

Installez le package nuget Forloop.HtmlHelpers - il ajoute des aides pour la gestion des scripts dans les vues partielles et les modèles d'éditeur.

Quelque part dans votre mise en page, vous devez appeler

@Html.RenderScripts()

Ce sera là où tous les fichiers de script et blocs de script seront affichés dans la page, je vous recommande donc de le placer après vos principaux scripts dans la mise en page et après une section de scripts (si vous en avez une).

Si vous utilisez The Web Optimization Framework avec bundling, vous pouvez utiliser la surcharge

@Html.RenderScripts(Scripts.Render)

afin que cette méthode soit utilisée pour écrire des fichiers de script.

Désormais, à chaque fois que vous souhaitez ajouter des fichiers de script ou des blocs dans une vue, une vue partielle ou un modèle, utilisez simplement

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

Les assistants veillent à ce qu'une seule référence de fichier de script soit rendue si elle est ajoutée plusieurs fois et garantit également que les fichiers de script sont rendus dans un ordre attendu, c'est-à-dire

  1. Disposition
  2. Partiels et modèles (dans l'ordre dans lequel ils apparaissent dans la vue, de haut en bas)
Russ Cam
la source
5

[Version mise à jour] Version mise à jour suite à la question @Necrocubus pour inclure les scripts en ligne.

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
}

Mes 2 cents, c'est un ancien post, mais toujours d'actualité, voici donc une mise à jour mise à jour de la solution de M. Bell qui fonctionne avec ASP.Net Core.

Il permet d'ajouter des scripts et des styles à la mise en page principale à partir de vues partielles et de sous-vues importées, et la possibilité d'ajouter des options aux importations de script / style (comme async defer, 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; }
    }
}
Jean
la source
Merci mec! Cela devrait être davantage voté car il est plus pertinent qu'une réponse datant de 6 ans.
Necroqubus
En outre, ces extensions peuvent-elles être modifiées pour permettre aux sections de scripts d'être des entrées? @ <text> </text> ou quelque chose comme des sections? Sinon, j'ai encore besoin d'un petit script JS pour initialiser l'autre script avec des variables de modèle côté serveur: /
Necroqubus
@Necroqubus vous pouvez vérifier la version mise à jour, mais je ne l'ai pas encore testée :)
Jean
Très bien, je vais essayer de le tester pour vous. J'espère que cela fonctionne avec ASP.NET Core 1.0 MVC. Pour le contexte, j'ai plusieurs niveaux de partiels imbriqués et je souhaite que leurs scripts soient rendus au pied de page.
Necroqubus
Vous n'avez pas besoin du <text>, ajoutez-le simplement sous forme de chaîne (vous pouvez toujours préfixer @ "" pour plusieurs lignes si vous préférez), et sans les <script>balises
Jean
1

Vous pouvez créer une nouvelle Layoutpage et envelopper le PartialView dans une vue complète qui est responsable du rendu du contenu et de toutes les sections de la bibliothèque.

Par exemple, disons que j'ai le code suivant:

HomeController.cs

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

Lorsque la vue Pleine page est rendue, elle est généralement rendue en fusionnant deux fichiers:

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 (ou tout ce qui est spécifié dans _ViewStart ou remplacé dans la page)

<!DOCTYPE html>

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

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

Maintenant , supposons que vous vouliez effectuer le rendu en About.cshtmltant que vue partielle , peut-être en tant que fenêtre modale en réponse à un appel AJAX. Le but ici étant de ne renvoyer que le contenu spécifié dans la page à propos, les scripts et tout, sans tout le ballonnement inclus dans la _Layout.cshtmlmise en page principale (comme un full<html> document ).

Vous pouvez l'essayer comme ceci, mais il ne viendra avec aucun des blocs de section:

return PartialView("About", vm);

À la place, ajoutez une page de mise en page plus simple comme celle-ci:

_PartialLayout.cshtml

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

Ou pour prendre en charge une fenêtre modale comme celle-ci:

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

Ensuite, vous pouvez spécifier une vue principale personnalisée dans ce contrôleur ou tout autre gestionnaire que vous souhaitez rendre le contenu et les scripts d'une vue simultanément

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

Pour ceux qui recherchent la version 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; }
        }
    }

Ajoutez à votre mise en page après l'appel de la section de rendu des scripts:

@HttpContextAccessor.EmitRequiredScripts()

Et dans votre vue partielle:

@inject IHttpContextAccessor HttpContextAccessor

...

@HttpContextAccessor.RequireScript("/scripts/moment.min.js")
Sébastien
la source
0

Sur la base de la réponse de M. Bell et Shimmy ci-dessus, j'ajoute une fonction supplémentaire pour le 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  

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

Exemple sur MasterPage: - @ Html.EmitRequiredBundleStyles ()

Harris Yer
la source
0

Utilisez les @using(Html.Delayed()){ ...your content... }extensions de la réponse https://stackoverflow.com/a/18790222/1037948 pour afficher n'importe quel contenu (scripts ou simplement HTML) plus tard dans la page. L'interne Queuedoit garantir une commande correcte.

drzaus
la source
0

Cette fonctionnalité est également implémentée dans ClientDependency.Core.Mvc.dll. Il fournit les helpers html: @ Html.RequiresJs et @ Html.RenderJsHere (). Package Nuget: ClientDependency-Mvc

Evert
la source
0

Voici ma solution aux questions fréquemment posées "comment injecter des sections de vues partielles aux vues principales ou à la vue de mise en page principale pour asp.net mvc?". Si vous effectuez une recherche sur stackoverflow par mots-clés "section + partial", vous obtiendrez une assez grande liste de questions connexes, et des réponses données, mais aucune d'entre elles ne me semble élégante au moyen de la grammaire du moteur de rasoir. Je regarde donc le moteur Razor pour voir s'il pourrait y avoir une meilleure solution à cette question.

Heureusement, j'ai trouvé quelque chose qui m'intéresse sur la façon dont le moteur Razor effectue la compilation du fichier de modèle de vue (* .cshtml, * .vbhtml). (J'expliquerai plus tard), ci-dessous est mon code de la solution qui je pense est assez simple et assez élégante dans l'utilisation.

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

usage : Utiliser le code est également assez simple, et il a presque le même style que d'habitude. Il prend également en charge tous les niveaux des vues partielles imbriquées. c'est à dire. J'ai une chaîne de modèles de vue: _ViewStart.cshtml-> layout.cshtml-> index.cshtml -> [head.cshtml, foot.cshtml] -> ad.cshtml.

Dans layout.cshtml, nous avons:

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

Et dans index.cshtml, nous avons:

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

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

Et dans head.cshtml, nous aurions le code:

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

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

c'est la même chose dans foot.cshtml ou ad.cshtml, vous pouvez toujours définir la section Head ou Foot en eux, assurez-vous d'appeler @ Html.EnsureSection () une fois à la fin du fichier de vue partielle. C'est tout ce que vous devez faire pour vous débarrasser du problème soumis dans asp mvc.

Je partage simplement mon extrait de code pour que d'autres puissent en profiter. Si vous pensez que c'est utile, n'hésitez pas à augmenter mon avis. :)

Jonathan
la source