Заполнить секцию бритвы из частичного


103

Моя основная мотивация для попытки сделать это - получить 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 ... ваш код немного сбивает с толку ...
гидеон

Это не. Даже если раздел не отображается в представлении, код в партиале не попадает в финальную отрисованную страницу. Я думаю, что SLaks прав в том, что частичные элементы не могут участвовать в разделах родительского представления.
Craig M

Ответы:


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 (). Я делаю что-то неправильно?
Брайан Рот

Что-то не так, Брайан. Я активно использовал это решение в течение последнего года или около того, и оно хорошо работает. Может быть, опубликуйте новый вопрос с подробностями вашей проблемы и укажите ссылку здесь
Мистер Белл,

1
Есть ли здесь поддержка очистки кеша при развертывании новой версии приложения? Стандартный метод @ scripts.Render () вставляет в конце параметр URL, который генерируется во время сборки, чтобы браузер был вынужден получить последнюю версию при развертывании новой версии.
Саймон Грин,

28

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


1
Это то, о чем я подозревал. Спасибо.
Craig M

@JohnBubriski Есть в Razor 2. Не знаю о пред. версии.
Shimmy Weitzhandler

@SLaks, почему так задумано? В моем сценарии у меня есть часть, которая является ротатором баннеров, я хочу, чтобы его скрипты / стили загружались только тогда, когда он включен, почему плохо загружать его встроенным?
Shimmy Weitzhandler

2
@Shimmy: Вам следует использовать систему управления ресурсами, такую ​​как Cassette.
SLaks

Спасибо. Я займусь этим.
Shimmy Weitzhandler

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 на стороне сервера.
Craig M

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

@ one.beat.customer - Я использую шаблоны подчеркивания, так как я также использую Backbone, но я думаю о переключении либо на библиотеку Hogan из Twitter, либо на Plates из Nodejitsu. Оба имеют довольно приятные функции.
Craig M

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

Установите пакет Nuget Forloop.HtmlHelpers - он добавляет несколько помощников для управления скриптами в частичных представлениях и шаблонах редактора.

Где-то в вашем макете нужно позвонить

@Html.RenderScripts()

Здесь будут выводиться любые файлы сценариев и блоки сценариев на странице, поэтому я бы рекомендовал поместить их после основных сценариев в макете и после раздела сценариев (если он у вас есть).

Если вы используете The Web Optimization Framework с объединением, вы можете использовать перегрузку

@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 defer и т. Д.):

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. Цель здесь - вернуть только содержимое, указанное на странице «О нас», сценариях и всем остальном, без излишков, включенных в _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 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; }
        }
    }

Добавьте в свой макет после вызова раздела рендеринга скриптов:

@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 по ключевым словам «section + partial», вы получите довольно большой список связанных вопросов и ответов, но ни один из них не кажется мне элегантным с помощью грамматики бритвенного движка. Поэтому я просто посмотрю на движок 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, вы все равно можете определить в них раздел Head или Foot, обязательно вызовите @ Html.EnsureSection () один раз в конце файла частичного представления. Это все, что вам нужно сделать, чтобы избавиться от данной проблемы в asp mvc.

Я просто делюсь своим фрагментом кода, чтобы другие могли его использовать. Если вы считаете, что это полезно, пожалуйста, не стесняйтесь оценивать мой пост. :)

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.