Füllen Sie einen Rasiermesserabschnitt aus einem Teil

102

Meine Hauptmotivation für diesen Versuch besteht darin, Javascript zu erhalten, das nur von einem Teil am Ende der Seite mit dem Rest des Javascript und nicht in der Mitte der Seite, auf der der Teil gerendert wird, benötigt wird.

Hier ist ein vereinfachtes Beispiel dafür, was ich versuche:

Hier ist das Layout mit einem Skriptabschnitt direkt vor dem Text.

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

Hier ist eine Beispielansicht mit diesem Layout.

<h2>This is the view</h2>

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

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

Und hier ist der Teil, der aus der Ansicht gerendert wird.

<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 diesem Beispiel wird das in der Ansicht angegebene Markup in den Abschnitt eingefügt, das Markup aus dem Teil jedoch nicht. Ist es möglich, einen Abschnitt aus einer Teilansicht mit Razor zu füllen? Wenn nicht, welche anderen Methoden gibt es, um Javascript zu erhalten, das nur von Partials am Ende der Seite benötigt wird, ohne es global einzuschließen?

Craig M.
quelle
Vielleicht ist es ein Problem, weil Sie einen anderen
Skriptabschnitt
Es ist nicht. Selbst wenn der Abschnitt nicht in der Ansicht angezeigt wird, schafft es der Code im Teil nicht in die endgültig gerenderte Seite. Ich denke, SLaks ist insofern richtig, als Partials nicht an den Abschnitten der übergeordneten Ansicht teilnehmen können.
Craig M

Antworten:

78

Ich habe damit umgegangen, indem ich ein paar Erweiterungsmethoden in die HtmlHelper-Klasse geschrieben habe. Auf diese Weise können Teilansichten sagen, dass sie ein Skript benötigen, und dann in der Layoutansicht, die das von mir aufgerufene Tag in meine Hilfsmethode schreibt, um die erforderlichen Skripte auszugeben

Hier sind die Hilfsmethoden:

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

Sobald Sie dies eingerichtet haben, muss Ihre Teilansicht nur noch aufgerufen werden @Html.RequireScript("/Path/To/Script").

Und im Kopfbereich der Layoutansicht rufen Sie an @Html.EmitRequiredScripts().

Ein zusätzlicher Bonus ist, dass Sie damit doppelte Skriptanforderungen aussortieren können. Wenn Sie mehrere Ansichten / Teilansichten haben, für die ein bestimmtes Skript erforderlich ist, können Sie davon ausgehen, dass Sie es nur einmal ausgeben

Herr Bell
quelle
Elegante und saubere Lösung. +1
Bevacqua
Ich bin gerade auf diese Lösung
gestoßen,
Ich kann diese Lösung nicht zum Laufen bringen. Es scheint, dass EmitRequiredScripts () aufgerufen wird, bevor Teilansichten RequireScript () aufrufen. Mache ich etwas falsch?
Bryan Roth
Etwas klingt nicht richtig, Bryan. Ich habe diese Lösung im letzten Jahr ausgiebig genutzt und sie hat gut funktioniert. Vielleicht posten Sie eine neue Frage mit den Details Ihres Problems und verlinken Sie die URL hier
Mr Bell
1
Gibt es Unterstützung für Cache-Busting beim Bereitstellen einer neuen Version der App? Die Out-of-Box-Methode @ scripts.Render () fügt am Ende einen URL-Parameter hinzu, der beim Erstellen generiert wird, sodass der Browser gezwungen ist, die neueste Version abzurufen, wenn eine neue Version bereitgestellt wird.
Simon Green
28

Teilansichten können nicht an den Abschnitten ihrer übergeordneten Ansichten teilnehmen.

SLaks
quelle
1
Das habe ich vermutet. Vielen Dank.
Craig M
@ JohnBubriski Es gibt in Razor 2. Weiß nicht über prev. Versionen.
Shimmy Weitzhandler
@SLaks, warum ist das so beabsichtigt? In meinem Szenario habe ich einen Teil, der ein Banner-Rotator ist. Ich möchte, dass seine Skripte / Stile nur geladen werden, wenn er aktiviert ist. Warum ist es schlecht, ihn inline zu laden?
Shimmy Weitzhandler
2
@Shimmy: Sie sollten ein Ressourcenverwaltungssystem wie Kassette verwenden.
SLaks
Danke dir. Ich werde es untersuchen.
Shimmy Weitzhandler
13

Sie könnten einen zweiten Teil haben, der nur für das Injizieren des erforderlichen Javascript zuständig ist. Platzieren Sie dort mehrere Skripte um @ifBlöcke, wenn Sie möchten:

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

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

Dies könnte natürlich ein bisschen aufgeräumt werden, aber dann im ScriptsAbschnitt Ihrer Ansicht:

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

Auch hier wird es vielleicht keinen Schönheitspreis gewinnen, aber es wird funktionieren.

Sergi Papaseit
quelle
1
Das ist ziemlich nah an dem, was ich letztendlich gemacht habe. Es ist definitiv nicht schön, aber es funktioniert. Der einzige Nachteil dabei ist, dass Sie den Teil nicht über einen Ajax-Aufruf erhalten können und das JS enthalten haben. Ich denke, langfristig werde ich das Refactoring mit jQuery-Vorlagen beenden und einfach JSON von meinen Controllern senden, anstatt das HTML auf der Serverseite zu erstellen.
Craig M
@CraigM das ist, wohin ich auch gehe. MVC ist legitim, aber es ist (für mich) viel sinnvoller, Vorlagen clientseitig zu verwenden (ich schaue in Backbone.js) und dann Push / Pull von einer API.
one.beat.consumer
@ one.beat.customer - Ich verwende die Vorlagen des Unterstrichs, seit ich auch Backbone verwende, aber ich denke darüber nach, entweder von Twitter zur Hogan-Bibliothek oder von Nodejitsu zu Platten zu wechseln. Beide haben ziemlich schöne Eigenschaften.
Craig M
10

Die elegantere Möglichkeit, dies zu tun, besteht darin, Teilansichts-Skripte in eine separate Datei zu verschieben und sie dann im Ansichtsabschnitt "Skripte" zu rendern:

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

Die Teilansicht _ Partial.cshtml :

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

Die Teilansicht _ PartialScripts.cshtml nur mit Skripten:

<script type="text/javascript">
    alert("I'm a partial script!");
</script>
Vlad Rudenko
quelle
Dies ist nicht so automatisch wie einige der in anderen Antworten vorgeschlagenen Erweiterungsmethoden oder Plug-Ins, bietet jedoch den Vorteil der Einfachheit und Klarheit. Es gefällt mir.
Mark Meuer
7

Installieren Sie das Nuget- Paket Forloop.HtmlHelpers. Es enthält einige Hilfsprogramme zum Verwalten von Skripten in Teilansichten und Editorvorlagen.

Irgendwo in Ihrem Layout müssen Sie anrufen

@Html.RenderScripts()

Hier werden alle Skriptdateien und Skriptblöcke auf der Seite ausgegeben. Ich würde daher empfehlen, sie nach Ihren Hauptskripten im Layout und nach einem Skriptabschnitt (falls vorhanden) einzufügen.

Wenn Sie das Web Optimization Framework mit Bündelung verwenden, können Sie die Überladung verwenden

@Html.RenderScripts(Scripts.Render)

Damit wird diese Methode zum Schreiben von Skriptdateien verwendet.

Wenn Sie jetzt Skriptdateien oder -blöcke zu einer Ansicht, Teilansicht oder Vorlage hinzufügen möchten, verwenden Sie diese einfach

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

Die Helfer stellen sicher, dass nur eine Skriptdateireferenz gerendert wird, wenn sie mehrmals hinzugefügt wird, und sie stellen auch sicher, dass Skriptdateien in der erwarteten Reihenfolge gerendert werden, d. H.

  1. Layout
  2. Partials und Vorlagen (in der Reihenfolge, in der sie in der Ansicht von oben nach unten angezeigt werden)
Russ Cam
quelle
5

[Aktualisierte Version] Aktualisierte Version nach der @ Necrocubus-Frage, um Inline-Skripte einzuschließen.

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
}

Meine 2 Cent, es ist ein alter Beitrag, aber immer noch relevant. Hier ist ein aktualisiertes Update der Lösung von Herrn Bell, die mit ASP.Net Core funktioniert.

Es ermöglicht das Hinzufügen von Skripten und Stilen zum Hauptlayout aus importierten Teilansichten und Unteransichten sowie das Hinzufügen von Optionen zu Skript- / Stilimporten (wie z. B. asynchrones Verschieben usw.):

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
quelle
Danke mann! Dies sollte mehr positiv bewertet werden, da es relevanter ist als eine Antwort, die 6 Jahre alt ist.
Necroqubus
Können diese Erweiterungen auch so geändert werden, dass Abschnitte von Skripten eingegeben werden können? @ <text> </ text> oder so etwas wie Abschnitte? Ansonsten brauche ich noch ein kleines JS-Skript, um das andere Skript mit
serverseitigen
@ Necroqubus Sie können die aktualisierte Version überprüfen, aber ich habe es noch nicht getestet :)
Jean
Okay, ich werde versuchen, es für dich zu testen. Ich hoffe, es funktioniert mit ASP.NET Core 1.0 MVC. Für den Kontext habe ich mehrere Ebenen verschachtelter Partials und möchte, dass ihre Skripte in der Fußzeile gerendert werden.
Necroqubus
Benötigen Sie das nicht <text>, fügen Sie es einfach als Zeichenfolge hinzu (Sie können immer noch @ "" für mehrere Zeilen voranstellen, wenn Sie dies bevorzugen) und ohne die <script>Tags
Jean
1

Sie können eine neue LayoutSeite erstellen und die Teilansicht in eine Vollansicht einbinden, die für das Rendern des Inhalts und aller Bibliotheksabschnitte verantwortlich ist.

Angenommen, ich habe den folgenden Code:

HomeController.cs

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

Wenn die Vollseitenansicht gerendert wird, wird sie normalerweise durch Zusammenführen von zwei Dateien gerendert:

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 (oder was auch immer in _ViewStart angegeben oder auf der Seite überschrieben ist)

<!DOCTYPE html>

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

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

Nun sei angenommen , Sie Render wollte About.cshtmlals Teilansicht , vielleicht als modale Fenster als Reaktion auf AJAX - Aufruf. Das Ziel hierbei ist, nur den Inhalt zurückzugeben, der auf der About-Seite, den Skripten und allem angegeben ist, ohne dass das _Layout.cshtmlMaster-Layout das gesamte Aufblähen enthält (wie ein vollständiges <html>Dokument).

Sie könnten es so versuchen, aber es wird mit keinem der Abschnittsblöcke geliefert:

return PartialView("About", vm);

Fügen Sie stattdessen eine einfachere Layoutseite wie die folgende hinzu:

_PartialLayout.cshtml

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

Oder um ein modales Fenster wie dieses zu unterstützen:

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

Anschließend können Sie in diesem Controller oder einem anderen Handler eine benutzerdefinierte Hauptansicht angeben , mit der Sie den Inhalt und die Skripts einer Ansicht gleichzeitig rendern möchten

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

Für diejenigen, die nach der Aspnet Core 2.0-Version suchen:

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

Fügen Sie Ihrem Layout hinzu, nachdem der Skript-Rendering-Abschnitt aufgerufen wurde:

@HttpContextAccessor.EmitRequiredScripts()

Und in Ihrer Teilansicht:

@inject IHttpContextAccessor HttpContextAccessor

...

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

Basierend auf der Antwort von Herrn Bell und Shimmy oben füge ich eine zusätzliche Funktion für das Bundle-Skript hinzu.

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  

Beispiel für PartialView: - @ Html.RequireBundleStyles ("~ / bundles / fileupload / bootstrap / BasicPlusUI / css"); @ Html.RequireBundleScripts ("~ / bundles / fileupload / bootstrap / BasicPlusUI / js");

Beispiel auf MasterPage: - @ Html.EmitRequiredBundleStyles ()

Harris Yer
quelle
0

Verwenden Sie @using(Html.Delayed()){ ...your content... }Erweiterungen aus der Antwort https://stackoverflow.com/a/18790222/1037948 , um Inhalte (Skripte oder nur HTML) später auf der Seite zu rendern. Intern Queuesollte die korrekte Bestellung sicherstellen.

drzaus
quelle
0

Diese Funktionalität ist auch in ClientDependency.Core.Mvc.dll implementiert. Es bietet die HTML-Helfer: @ Html.RequiresJs und @ Html.RenderJsHere (). Nuget-Paket: ClientDependency-Mvc

Evert
quelle
0

Hier ist meine Lösung für die häufig gestellten Fragen "Wie werden Abschnitte aus Teilansichten in Hauptansichten oder Hauptlayoutansichten für asp.net mvc eingefügt?". Wenn Sie den Stapelüberlauf nach den Schlüsselwörtern "Abschnitt + Teil" durchsuchen, erhalten Sie eine ziemlich große Liste verwandter Fragen und Antworten, aber keine davon scheint mir mithilfe der Grammatik der Rasiermaschine elegant zu sein. Ich schaue mir nur die Razor-Engine an, um zu sehen, ob es eine bessere Lösung für diese Frage gibt.

Glücklicherweise fand ich etwas Interessantes für mich, wie die Razor-Engine die Kompilierung für die Ansichtsvorlagendatei (* .cshtml, * .vbhtml) durchführt. (Ich werde es später erklären), unten ist mein Code der Lösung, der meiner Meinung nach recht einfach und elegant genug ist.

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

Verwendung : Die Verwendung des Codes ist ebenfalls recht einfach und sieht fast genauso aus wie üblich. Es werden auch beliebige Ebenen für die verschachtelten Teilansichten unterstützt. dh. Ich habe eine Ansichtsvorlagenkette: _ViewStart.cshtml-> layout.cshtml-> index.cshtml -> [head.cshtml, foot.cshtml] -> ad.cshtml.

In layout.cshtml haben wir:

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

Und in index.cshtml haben wir:

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

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

Und in head.cshtml hätten wir den 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()

Es ist dasselbe in foot.cshtml oder ad.cshtml. Sie können weiterhin den Kopf- oder Fußabschnitt darin definieren. Rufen Sie @ Html.EnsureSection () einmal am Ende der Teilansichtsdatei auf. Das ist alles, was Sie tun müssen, um das Problem in asp mvc zu beseitigen.

Ich teile nur mein Code-Snippet, damit andere es nutzen können. Wenn Sie der Meinung sind, dass dies nützlich ist, zögern Sie bitte nicht, meinen Beitrag zu bewerten. :) :)

Jonathan
quelle