เติมส่วนมีดโกนจากบางส่วน


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 ถูกต้องในบางส่วนที่ไม่สามารถเข้าร่วมในส่วนของมุมมองหลักได้
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

เพิ่งเจอวิธีนี้หลังจากดึงผมส่วนใหญ่ออก - วิธีแก้ที่ยอดเยี่ยม ....
สูง

ฉันไม่สามารถใช้วิธีแก้ปัญหานี้ได้ ดูเหมือนว่า EmitRequiredScripts () จะถูกเรียกก่อนมุมมองบางส่วนจะเรียก RequireScript () ฉันทำอะไรผิดหรือเปล่า?
Bryan Roth

มีบางอย่างไม่ถูกต้องไบรอัน ฉันใช้วิธีนี้อย่างแพร่หลายในช่วงหนึ่งปีที่ผ่านมาและใช้งานได้ดี อาจจะโพสต์คำถามใหม่พร้อมรายละเอียดปัญหาของคุณและลิงค์ url ที่นี่
Mr Bell

1
สิ่งนี้มีการรองรับการป้องกันแคชเมื่อปรับใช้แอปเวอร์ชันใหม่หรือไม่ เมธอด out-of-box @ scripts.Render () จะยึดพารามิเตอร์ URL ไว้ที่ส่วนท้ายซึ่งสร้างขึ้นในเวลาสร้างเพื่อให้เบราว์เซอร์ถูกบังคับให้ดึงข้อมูลเวอร์ชันล่าสุดเมื่อมีการปรับใช้เวอร์ชันใหม่
Simon Green

28

การดูบางส่วนไม่สามารถเข้าร่วมในส่วนของมุมมองระดับบนสุดได้


1
นี่คือสิ่งที่ฉันสงสัย ขอบคุณ.
Craig M

@JohnBubriski มีใน Razor 2 ไม่รู้เรื่อง prev เวอร์ชัน
Shimmy Weitzhandler

@SLaks ทำไมถึงเป็นแบบนี้? ในสถานการณ์ของฉันฉันมีบางส่วนที่เป็นตัวหมุนแบนเนอร์ฉันต้องการให้สคริปต์ / สไตล์ของมันโหลดเฉพาะเมื่อเปิดอยู่ทำไมการโหลดแบบอินไลน์จึงไม่ดี
Shimmy Weitzhandler

2
@Shimmy: คุณควรใช้ระบบการจัดการทรัพยากรเช่นเทปคาสเซ็ต
SLaks

ขอบคุณ. ฉันจะดูมัน
Shimmy Weitzhandler

14

คุณสามารถมีบางส่วนที่สองซึ่งรับผิดชอบในการฉีดจาวาสคริปต์ที่จำเป็นเท่านั้น วางสคริปต์หลาย ๆ ตัวไว้รอบ ๆ@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

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

สิ่งนี้ไม่ได้เป็นไปโดยอัตโนมัติเหมือนกับวิธีการขยายหรือปลั๊กอินที่แนะนำในคำตอบอื่น ๆ แต่มีข้อดีคือความเรียบง่ายและชัดเจน มันชอบ
Mark Meuer

7

ติดตั้งForloop.HtmlHelpersแพคเกจ nuget - จะเพิ่มผู้ช่วยบางส่วนสำหรับการจัดการสคริปต์ในมุมมองบางส่วนและแม่บรรณาธิการ

คุณต้องโทรหาที่ไหนสักแห่งในเค้าโครงของคุณ

@Html.RenderScripts()

นี่คือที่ที่ไฟล์สคริปต์และบล็อกสคริปต์จะถูกส่งออกในหน้าดังนั้นฉันขอแนะนำให้วางไว้หลังสคริปต์หลักของคุณในเค้าโครงและหลังส่วนสคริปต์ (ถ้าคุณมี)

หากคุณกำลังใช้ 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 เซ็นต์ของฉันมันเป็นโพสต์เก่า แต่ยังคงมีความเกี่ยวข้องดังนั้นนี่คือการอัปเดตที่อัปเกรดของโซลูชันของ Mr Bell ซึ่งทำงานร่วมกับ 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 คุณสามารถตรวจสอบเวอร์ชันที่อัปเดตได้ แต่ฉันยังไม่ได้ทดสอบ :)
Jean

ได้เลยจะลองทดสอบให้ดู ฉันหวังว่ามันจะใช้ได้กับ 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>

ตอนนี้สมมติว่าคุณต้องการ Render About.cshtmlเป็นPartial Viewซึ่งอาจเป็นหน้าต่างโมดอลเพื่อตอบสนองต่อการโทร 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>

จากนั้นคุณสามารถระบุ Master View ที่กำหนดเองในคอนโทรลเลอร์นี้หรือตัวจัดการอื่น ๆ ที่คุณต้องการแสดงเนื้อหาและสคริปต์ของมุมมองพร้อมกัน

[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

จากคำตอบของ Mr Bell And Shimmy ข้างต้นฉันได้เพิ่มฟังก์ชันพิเศษสำหรับสคริปต์ 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 ("~ / บันเดิล / 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 engine ทำการคอมไพล์สำหรับไฟล์เทมเพลตมุมมอง (* .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

ฉันแค่แชร์ข้อมูลโค้ดของฉันเพื่อให้คนอื่นใช้งานได้ หากคุณรู้สึกว่ามีประโยชน์โปรดอย่าลังเลที่จะเพิ่มคะแนนโพสต์ของฉัน :)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.