Створіть частину бритва з часткового


102

Моя основна мотивація для спроби зробити це - отримати Javascript, який потрібен лише частково внизу сторінки з рештою Javascript, а не в середині сторінки, де часткова надається.

Ось спрощений приклад того, що я намагаюся зробити:

Ось макет із розділом Сценарії прямо перед тілом.

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

Ось приклад подання з використанням цього макета.

<h2>This is the view</h2>

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

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

І ось часткова частина виводиться з виду.

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

У цьому прикладі розмітка, зазначена у представленні, розміщується у розділі, але розмітка від часткової - ні. Чи можливо заповнити розділ з часткового перегляду Razor? Якщо ні, то які ще існують способи отримання Javascript, які потрібні лише з частинами внизу сторінки, не включаючи їх у глобальному масштабі?


можливо, це проблема, тому що у вас є інший розділ сценарію в частковому .. IDK .. ваш код трохи заплутаний ..
gideon

Це не. Навіть якщо розділ не залишиться у вікні перегляду, код у частині не перетворить його на остаточну відображену сторінку. Я думаю, що SLaks є правильним, оскільки партії не можуть брати участь у розділах батьківського перегляду.
Крейг М

Відповіді:


78

Я розбирався з цим, щоб написати пару методів розширення до класу HtmlHelper. Це дозволяє частковому погляду сказати, що їм потрібен сценарій, а потім у вікні макета, що пише тег, я закликаю до свого помічника методу, щоб видавати необхідні сценарії

Ось допоміжні методи:

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

Після того, як у вас є місце, ваш частковий вигляд просто повинен зателефонувати @Html.RequireScript("/Path/To/Script").

І в головному розділі перегляду макета ви телефонуєте @Html.EmitRequiredScripts().

Додатковим бонусом цього є те, що воно дозволяє відмивати дублікати запитів скриптів. Якщо у вас є кілька переглядів / часткових представлень, які потребують певного сценарію, можете сміливо припускати, що ви виведете його лише один раз


Елегантний і чистий розчин. +1
bevacqua

Щойно натрапив на це рішення після того, як витягнув більшу частину моїх волосся - відмінне рішення ....
higgsy

Я не можу змусити це рішення працювати. Здається, що EmitRequiredScripts () викликається до того, як будь-які часткові перегляди отримають виклик RequireScript (). Я щось роблю не так?
Брайан Рот

Щось не здається правильно, Брайан. Я широко використовував це рішення протягом останнього року або близько того, і воно добре працює. Можливо, опублікуйте нове запитання із деталями вашої проблеми та зв’яжіть URL-адресу тут
Містер Белл

1
Чи є в цьому підтримка перешкод кешу під час розгортання нової версії програми? Метод out-of-box @ scriptpts.Render () приклеює параметр URL в кінці, який генерується під час збирання, щоб браузер змушений отримати останню версію при розгортанні нової версії.
Саймон Грін

28

Часткові представлення не можуть брати участь у розділах батьківських переглядів.


1
Це те, що я підозрював. Дякую.
Крейг М

@JohnBubriski Є в Razor 2. Не знаю про prev. версії.
Шиммі Вайцхандлер

@SLaks, чому це дизайн? У моєму сценарії у мене є частковий, який є ротатором банерів, я хочу, щоб його сценарії / стилі завантажувались лише тоді, коли він увімкнено, чому погано завантажувати його в рядок?
Шиммі Вайцхандлер

2
@Shimmy: Вам слід використовувати систему управління ресурсами, наприклад, касету.
СЛАКС

Дякую. Я розберуся в це.
Шиммі Вайцхандлер

14

Ви можете мати другу частку, яка відповідає лише за введення необхідного JavaScript. Розмістіть кілька скриптів там навколо @ifблоків, якщо ви хочете:

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

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

Це, очевидно, може бути трохи очищено, але потім, у Scriptsрозділі вашого перегляду:

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

Знову ж таки, це може не виграти приз краси, але це спрацює.


1
Це досить близько до того, що я закінчив. Це, безумовно, не дуже, але це працює. Єдиним недоліком цього є те, що ви не можете отримати часткове за допомогою виклику ajax і включити JS. Я думаю, що довгостроково я збираюся закінчити рефакторинг за допомогою шаблонів jQuery і просто надішлю JSON від моїх контролерів замість того, щоб будувати HTML на стороні сервера.
Крейг М

@CraigM саме там я і очолюю. MVC є законним, але це має набагато більше сенсу (для мене) використовувати шаблони на стороні клієнта (я шукаю Backbone.js), а потім натискати / витягувати з API.
one.beat.consumer

@ one.beat.customer - я використовую шаблони підкреслення, оскільки я також використовую Backbone, але я думаю про перехід до бібліотеки Хогана з Twitter або на Plates від Nodejitsu. Обидва мають досить приємні риси.
Крейг М

10

Більш елегантний спосіб зробити це - перемістити сценарії часткового перегляду в окремий файл, а потім вивести їх у розділ перегляду «Сценарії»:

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

Частковий вигляд _ Partial.cshtml :

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

Частковий вигляд _ PartialScripts.cshtml лише зі скриптами:

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

Це не настільки автоматично, як деякі методи розширення або плагіни, запропоновані в інших відповідях, але це має перевагу у простоті та чіткості. Це подобається.
Марк Меуер

7

Встановіть Forloop.HtmlHelpers NuGet пакета - це додає деякі помічників для управління скриптами часткового виду і шаблони редактора.

Десь у вашому макеті вам потрібно зателефонувати

@Html.RenderScripts()

Тут будуть виводитися будь-які файли сценаріїв та блоки сценаріїв на сторінку, тому я рекомендую розмістити її після основних сценаріїв у макеті та після розділу сценаріїв (якщо у вас є).

Якщо ви використовуєте Рамку веб-оптимізації для комплектування, ви можете використовувати перевантаження

@Html.RenderScripts(Scripts.Render)

так що цей метод використовується для запису файлів сценаріїв.

Тепер, будь-коли, коли ви хочете додати файли або блоки сценаріїв у подання, часткове подання або шаблон, просто використовуйте

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

Помічники гарантують, що лише одна посилання на файл скрипту надається, якщо їх додано кілька разів, а також гарантує, що файли скриптів будуть виведені в очікуваному порядку, тобто

  1. Макет
  2. Партії та шаблони (у порядку, в якому вони відображаються у вікні, зверху вниз)

5

[Оновлена ​​версія] Оновлена ​​версія після питання @Necrocubus до Включення вбудованих сценаріїв.

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
}

Мої 2 копійки, це стара публікація, але все ще актуальна, тому ось оновлене оновлення рішення містера Белла, яке працює з ASP.Net Core.

Це дозволяє додавати сценарії та стилі до основного макета із імпортованих часткових представлень та підпрезентацій, а також можливість додавати параметри до імпорту скриптів / стилів (наприклад, відкладення async тощо):

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

Спасибі, чоловіче! На це слід більше звертати увагу, оскільки це більш релевантно, ніж відповідь, якій 6 років.
Necroqubus

Також чи можна змінити ці розширення, щоб дозволити вводити розділи скриптів? @ <text> </text> чи щось подібне до розділів? Інакше мені ще потрібен невеликий скрипт JS, щоб ініціалізувати інший скрипт із змінними моделі на стороні сервера: /
Necroqubus

@Necroqubus ви можете перевірити оновлену версію, однак я її ще не перевіряв :)
Жан

Добре, я спробую перевірити це для вас. Я сподіваюся, що він працює з ASP.NET Core 1.0 MVC. Для контексту я маю кілька рівнів вкладених партій і хочу, щоб їх сценарії виводилися в нижній колонтитул.
Necroqubus

Вам не потрібен <text>, просто додайте його як рядок (ви можете все-таки префіксувати за допомогою "" "для багаторядкових, якщо хочете), і без <script>тегів
Жан

1

Ви можете створити нову Layoutсторінку і загортати PartialView всередині повного перегляду, який відповідає за візуалізацію вмісту, а також будь-яких розділів бібліотеки.

Наприклад, скажімо, у мене є такий код:

HomeController.cs

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

Коли відображається перегляд "Повна сторінка", зазвичай він відображається шляхом об'єднання двох файлів:

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 (або все, що вказано в _ViewStart або замінено на сторінці)

<!DOCTYPE html>

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

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

Тепер , припустимо, ви хотіли About.cshtmlвідобразити як частковий вигляд , можливо, як модальне вікно у відповідь на дзвінок AJAX. Ціль тут полягає в тому, щоб повернути лише вміст, вказаний на сторінці about, сценарії та все, без усієї роздуми, включеної в _Layout.cshtmlосновний макет (як повний<html> документ).

Ви можете спробувати так, але це не буде в одному з розділів блоків:

return PartialView("About", vm);

Замість цього додайте простішу сторінку макета на зразок цієї:

_PartialLayout.cshtml

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

Або підтримати модальне вікно на зразок цього:

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

Тоді ви можете вказати спеціальний головний перегляд у цьому контролері чи будь-якому іншому обробнику, який хочете одночасно передати вміст та сценарії подання

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

1

Для тих, хто шукає версію ядра aspnet 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; }
        }
    }

Додайте до свого макета після виклику розділу візуалізації сценаріїв:

@HttpContextAccessor.EmitRequiredScripts()

І на ваш частковий погляд:

@inject IHttpContextAccessor HttpContextAccessor

...

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

0

Спираючись на відповідь містера Белла та Шиммі вище, я додаю додаткову функцію для сценарію 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  

Зразок на PartialView: - @ Html.RequireBundleStyles ("~ / bundles / fileupload / bootstrap / BasicPlusUI / css"); @ Html.RequireBundleScripts ("~ / bundles / fileupload / bootstrap / BasicPlusUI / js");

Зразок на MasterPage: - @ Html.EmitRequiredBundleStyles ()


0

Використовуйте @using(Html.Delayed()){ ...your content... }розширення з відповіді https://stackoverflow.com/a/18790222/1037948, щоб відобразити будь-який вміст (сценарії чи просто HTML) пізніше на сторінці. Внутрішні Queueповинні забезпечити правильне впорядкування.


0

Ця функціональність також реалізована в ClientDependency.Core.Mvc.dll. Він надає помічників html: @ Html.RequiresJs і @ Html.RenderJsHere (). Nuget пакет: ClientDependency-Mvc


0

Тут викладено моє рішення часто заданих питань "як ввести секції від часткових представлень до головних представлень або головного перегляду макета для asp.net mvc?". Якщо ви зробите пошук по stackoverflow за ключовими словами "розділ + частковий", ви отримаєте досить великий список пов’язаних питань та відповіді, але жодне з них мені не здається елегантним за допомогою граматики двигуна бритви. Тож я просто зазирну до двигуна Razor, щоб побачити, чи може бути якесь краще рішення цього питання.

На щастя, я знайшов щось цікаве для мене, як двигун Razor робить компіляцію для файлу шаблону перегляду (* .cshtml, * .vbhtml). (Я поясню пізніше), нижче - мій код рішення, який, на мою думку, є досить простим і елегантним у використанні.

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

використання : Використовувати код також досить просто, і він виглядає майже в тому ж стилі, що і звичайний. Він також підтримує будь-які рівні вкладених часткових представлень. тобто. У мене є ланцюжок шаблонів подання: _ViewStart.cshtml-> layout.cshtml-> index.cshtml -> [head.cshtml, foot.cshtml] -> ad.cshtml.

У layout.cshtml ми маємо:

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

І в index.cshtml ми маємо:

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

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

І в head.cshtml у нас буде код:

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

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

це те саме в foot.cshtml або ad.cshtml, ви все одно можете визначити в них розділ Голова або Ніжка, переконайтесь, що дзвоніть @ Html.EnsureSection () один раз в кінці файлу часткового перегляду. Це все, що вам потрібно зробити, щоб позбутися від підданої проблеми в asp mvc.

Я просто ділюся фрагментом коду, щоб інші могли ним скористатися. Якщо ви вважаєте, що це корисно, будь ласка, не соромтеся оцінити мою публікацію. :)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.