Isi Bagian Razor Dari Sebagian

102

Motivasi utama saya untuk mencoba melakukan ini adalah untuk mendapatkan Javascript yang hanya diperlukan oleh sebagian di bagian bawah halaman dengan Javascript lainnya dan bukan di tengah halaman tempat sebagian dirender.

Berikut adalah contoh sederhana dari apa yang saya coba lakukan:

Berikut adalah tata letak dengan bagian Skrip tepat di depan badan.

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

Berikut adalah contoh tampilan yang menggunakan tata letak ini.

<h2>This is the view</h2>

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

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

Dan inilah sebagian yang dirender dari tampilan.

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

Dalam contoh ini, markup yang ditentukan dalam tampilan ditempatkan ke dalam bagian, tetapi markup dari parsial tidak. Apakah mungkin untuk mengisi bagian dari pandangan parsial dengan Razor? Jika tidak, apa sajakah metode lain untuk mendapatkan Javascript yang hanya dibutuhkan sebagian di bagian bawah halaman tanpa menyertakannya secara global?

Craig M
sumber
mungkin ini menjadi masalah karena Anda memiliki bagian skrip lain di sebagian .. IDK .. kode Anda agak membingungkan ..
gideon
Ini bukan. Meskipun bagian dibiarkan keluar dari tampilan, kode di parsial tidak membuatnya menjadi halaman terakhir yang dirender. Saya pikir SLaks benar karena parsial tidak dapat berpartisipasi dalam bagian tampilan induk.
Craig M

Jawaban:

78

Cara saya menangani ini adalah dengan menulis beberapa metode ekstensi ke kelas HtmlHelper. Itu memungkinkan tampilan parsial untuk mengatakan bahwa mereka memerlukan skrip, lalu dalam tampilan tata letak yang menulis tag yang saya panggil ke metode pembantu saya untuk mengeluarkan skrip yang diperlukan

Berikut adalah metode helper:

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

Setelah Anda menerapkannya, tampilan parsial Anda hanya perlu dipanggil @Html.RequireScript("/Path/To/Script").

Dan di bagian kepala tampilan tata letak yang Anda panggil @Html.EmitRequiredScripts().

Bonus tambahan dari ini adalah memungkinkan Anda untuk menyingkirkan permintaan skrip duplikat. Jika Anda memiliki beberapa tampilan / sebagian tampilan yang membutuhkan skrip tertentu, Anda dapat dengan aman berasumsi bahwa Anda hanya akan mengeluarkannya sekali

Tuan Bell
sumber
Solusi elegan dan bersih. +1
bevacqua
Baru saja menemukan solusi ini setelah mencabut sebagian besar rambut saya - solusi yang sangat baik ....
higgsy
Saya tidak bisa mendapatkan solusi ini untuk bekerja. Tampaknya EmitRequiredScripts () dipanggil sebelum tampilan parsial apa pun dapat memanggil RequireScript (). Apakah saya melakukan sesuatu yang salah?
Bryan Roth
Ada yang tidak beres, Bryan. Saya telah menggunakan solusi ini secara ekstensif selama setahun terakhir ini dan telah bekerja dengan baik. Mungkin kirimkan pertanyaan baru dengan detail masalah Anda dan tautkan url di sini
Tuan Bell
1
Apakah ini memiliki dukungan perusak cache saat menerapkan versi baru aplikasi? Metode out-of-box @ scripts.Render () menempelkan parameter URL di bagian akhir yang dibuat pada waktu pembuatan sehingga browser dipaksa untuk mengambil versi terbaru ketika versi baru di-deploy.
Simon Green
28

Tampilan parsial tidak dapat berpartisipasi di bagian tampilan induknya.

SLaks
sumber
1
Ini yang saya duga. Terima kasih.
Craig M
@JohnBubriski Ada di Razor 2. Tidak tahu tentang sebelumnya. versi.
Shimmy Weitzhandler
@SLaks, mengapa ini disengaja? Dalam skenario saya, saya memiliki sebagian yang merupakan rotator spanduk, saya ingin skrip / gayanya dimuat hanya saat aktif, mengapa buruk untuk memuatnya sebaris?
Shimmy Weitzhandler
2
@Shimmy: Anda harus menggunakan sistem manajemen sumber daya, seperti Cassette.
SLaks
Terima kasih. Saya akan memeriksanya.
Shimmy Weitzhandler
13

Anda bisa memiliki bagian kedua yang hanya bertugas menyuntikkan javascript yang diperlukan. Tempatkan beberapa skrip di sana di sekitar @ifblok, jika Anda mau:

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

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

Ini jelas bisa dibersihkan sedikit, tetapi kemudian, di Scriptsbagian tampilan Anda:

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

Sekali lagi, itu mungkin tidak memenangkan hadiah kecantikan tetapi itu akan berhasil.

Sergi Papaseit
sumber
1
Ini cukup dekat dengan apa yang akhirnya saya lakukan. Jelas tidak cantik, tapi berhasil. Satu-satunya downside dari ini adalah Anda tidak bisa mendapatkan parsial melalui panggilan ajax dan memiliki JS yang disertakan. Saya pikir jangka panjang, saya akan melakukan refactoring menggunakan template jQuery dan hanya mengirim JSON dari pengontrol saya alih-alih membangun html di sisi server.
Craig M
@CraigM ke sanalah aku menuju juga. MVC itu sah, tetapi jauh lebih masuk akal (bagi saya) untuk menggunakan templat di sisi klien (saya mencari Backbone.js) dan kemudian mendorong / menarik dari API.
one.beat.consumer
@ one.beat.customer - Saya telah menggunakan template garis bawah karena saya juga menggunakan Backbone, tetapi saya berpikir untuk beralih ke pustaka Hogan dari Twitter atau Plates dari Nodejitsu. Keduanya memiliki fitur yang cukup bagus.
Craig M
10

Cara yang lebih elegan untuk melakukannya adalah dengan memindahkan skrip tampilan parsial ke dalam file terpisah dan kemudian merendernya di bagian tampilan Skrip:

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

Tampilan parsial _ Partial.cshtml :

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

Tampilan parsial _ PartialScripts.cshtml hanya dengan skrip:

<script type="text/javascript">
    alert("I'm a partial script!");
</script>
Vlad Rudenko
sumber
Ini tidak seotomatis beberapa metode ekstensi atau plugin yang disarankan dalam jawaban lain, tetapi memiliki keunggulan dalam kesederhanaan dan kejelasan. Itu menyukainya.
Mark Meuer
7

Instal paket nuget Forloop.HtmlHelpers - ini menambahkan beberapa pembantu untuk mengelola skrip dalam tampilan parsial dan template editor.

Di suatu tempat di tata letak Anda, Anda perlu menelepon

@Html.RenderScripts()

Ini akan menjadi tempat file skrip dan blok skrip akan dikeluarkan di halaman jadi saya akan merekomendasikan meletakkannya setelah skrip utama Anda dalam tata letak dan setelah bagian skrip (jika Anda memilikinya).

Jika Anda menggunakan The Web Optimization Framework dengan bundling, Anda dapat menggunakan kelebihan beban

@Html.RenderScripts(Scripts.Render)

Sehingga metode ini digunakan untuk menulis file script.

Sekarang, kapan pun Anda ingin menambahkan file atau blok skrip dalam tampilan, tampilan parsial, atau templat, cukup gunakan

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

Para pembantu memastikan bahwa hanya satu referensi file skrip yang dirender jika ditambahkan beberapa kali dan itu juga memastikan bahwa file skrip dirender dalam urutan yang diharapkan, yaitu

  1. Tata Letak
  2. Partial dan Template (dalam urutan kemunculannya dalam tampilan, dari atas ke bawah)
Russ Cam
sumber
5

[Versi terbaru] Versi yang diperbarui mengikuti pertanyaan @Necrocubus untuk menyertakan skrip sebaris.

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 sen saya, ini adalah posting lama, tetapi masih relevan, jadi berikut adalah pembaruan yang ditingkatkan dari solusi Tuan Bell yang bekerja dengan ASP.Net Core.

Ini memungkinkan menambahkan skrip dan gaya ke tata letak utama dari tampilan parsial dan subview yang diimpor, dan kemungkinan untuk menambahkan opsi ke impor skrip / gaya (seperti async defer dll):

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
sumber
Terima kasih bung! Ini harus lebih banyak disukai karena lebih relevan daripada jawaban yang berusia 6 tahun.
Necroqubus
Juga, dapatkah ekstensi ini dimodifikasi untuk memungkinkan bagian skrip menjadi input? @ <text> </text> atau sesuatu seperti bagian? Jika tidak, saya masih memerlukan skrip JS kecil untuk menginisialisasi skrip lain dengan variabel model sisi Server: /
Necroqubus
@Necroqubus Anda dapat memeriksa versi yang diperbarui, namun saya belum mengujinya :)
Jean
Baiklah, saya akan mencoba dan mengujinya untuk Anda. Saya harap ini berfungsi dengan ASP.NET Core 1.0 MVC. Untuk konteks, saya memiliki beberapa level parsial bersarang dan ingin skripnya dirender di footer.
Necroqubus
Tidak perlu <text>, cukup tambahkan sebagai string (Anda masih bisa mengawali dengan @ "" untuk multi baris jika Anda mau), dan tanpa <script>tag
Jean
1

Anda dapat membuat Layouthalaman baru dan membungkus PartialView di dalam Tampilan Penuh yang bertanggung jawab untuk merender konten dan juga bagian pustaka apa pun.

Misalnya, saya memiliki kode berikut:

HomeController.cs

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

Saat tampilan Halaman Penuh dirender, biasanya ditampilkan dengan menggabungkan dua file:

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 (atau apa pun yang ditentukan di _ViewStart atau diganti di halaman)

<!DOCTYPE html>

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

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

Sekarang , misalkan Anda ingin Render About.cshtmlas a Partial View , mungkin sebagai jendela modal dalam menanggapi panggilan AJAX. Tujuannya di sini adalah untuk mengembalikan hanya konten yang ditentukan di halaman tentang, skrip, dan semua, tanpa semua penggembungan yang disertakan dalam _Layout.cshtmltata letak master (seperti<html> dokumen ).

Anda dapat mencobanya seperti ini, tetapi itu tidak akan datang dengan salah satu blok bagian:

return PartialView("About", vm);

Sebagai gantinya, tambahkan halaman tata letak yang lebih sederhana seperti ini:

_PartialLayout.cshtml

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

Atau untuk mendukung jendela modal seperti ini:

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

Kemudian Anda dapat menentukan Tampilan Master kustom di pengontrol ini atau penangan lain yang Anda inginkan untuk merender konten dan skrip tampilan secara bersamaan

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

Bagi mereka yang mencari versi 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; }
        }
    }

Tambahkan ke tata letak Anda setelah skrip membuat panggilan bagian:

@HttpContextAccessor.EmitRequiredScripts()

Dan dalam pandangan parsial Anda:

@inject IHttpContextAccessor HttpContextAccessor

...

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

Berdasarkan jawaban dari Mr Bell And Shimmy di atas, saya menambahkan fungsi ekstra untuk skrip 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  

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

Contoh di MasterPage: - @ Html.EmitRequiredBundleStyles ()

Harris Yer
sumber
0

Gunakan @using(Html.Delayed()){ ...your content... }ekstensi dari answer https://stackoverflow.com/a/18790222/1037948 untuk merender konten apa pun (skrip atau hanya HTML) nanti di laman. Internal Queueharus memastikan pemesanan yang benar.

drzaus
sumber
0

Fungsionalitas ini juga diimplementasikan di ClientDependency.Core.Mvc.dll. Ini menyediakan pembantu html: @ Html.RequiresJs dan @ Html.RenderJsHere (). Paket Nuget: ClientDependency-Mvc

Evert
sumber
0

Berikut solusi saya untuk pertanyaan yang sering diajukan "bagaimana menyuntikkan bagian dari tampilan parsial ke tampilan utama atau tampilan tata letak utama untuk asp.net MVC?". Jika Anda melakukan pencarian di stackoverflow dengan kata kunci "bagian + parsial", Anda akan mendapatkan daftar pertanyaan terkait yang cukup besar, dan jawaban yang diberikan, tetapi tidak ada satupun yang tampak elegan bagi saya melalui tata bahasa mesin silet. Jadi saya hanya melihat ke mesin Razor untuk melihat apakah mungkin ada solusi yang lebih baik untuk pertanyaan ini.

Untungnya, saya menemukan sesuatu yang menarik bagi saya tentang bagaimana mesin Razor melakukan kompilasi untuk file template tampilan (* .cshtml, * .vbhtml). (Saya akan jelaskan nanti), di bawah ini adalah kode solusi saya yang menurut saya cukup sederhana dan cukup elegan dalam penggunaan.

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

pemakaian : Untuk menggunakan kodenya juga cukup sederhana, dan tampilannya hampir sama gaya biasanya. Ini juga mendukung setiap level ke tampilan parsial bersarang. yaitu. Saya memiliki rantai template tampilan: _ViewStart.cshtml-> layout.cshtml-> index.cshtml -> [head.cshtml, foot.cshtml] -> ad.cshtml.

Di layout.cshtml, kami memiliki:

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

Dan di index.cshtml, kami memiliki:

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

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

Dan di head.cshtml, kita akan memiliki kode:

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

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

itu sama di foot.cshtml atau ad.cshtml, Anda masih dapat menentukan bagian Head atau Foot di dalamnya, pastikan untuk memanggil @ Html.EnsureSection () sekali di akhir file tampilan parsial. Hanya itu yang perlu Anda lakukan untuk menyingkirkan masalah yang dibahas di asp mvc.

Saya hanya membagikan cuplikan kode saya sehingga orang lain dapat menggunakannya. Jika Anda merasa itu bermanfaat, jangan ragu untuk meningkatkan peringkat posting saya. :)

Jonathan
sumber