เป็นไปได้ไหมที่จะสร้างเมธอด @helper ทั่วไปด้วย Razor?


90

ฉันพยายามเขียนตัวช่วยใน Razor ที่มีลักษณะดังต่อไปนี้:

@helper DoSomething<T, U>(Expression<Func<T, U>> expr) where T : class

น่าเสียดายที่โปรแกรมแยกวิเคราะห์คิดว่านั่น<Tเป็นจุดเริ่มต้นขององค์ประกอบ HTML และฉันพบข้อผิดพลาดทางไวยากรณ์ เป็นไปได้ไหมที่จะสร้างตัวช่วยด้วย Razor ซึ่งเป็นวิธีการทั่วไป? ถ้าเป็นเช่นนั้นไวยากรณ์คืออะไร?


ยังคงไม่ได้รับการแก้ไขใน MVC 4 รุ่นปัจจุบัน :(
Alex Dresko

1
ยังคงไม่ได้รับการแก้ไขใน VS2012 อย่างไร
Alex Dresko

7
ความดีฉันรอไม่ไหวแล้วที่จะเพิ่มสิ่งนี้ ฉันหวังว่านี่จะเป็นที่ ๆ " ใช้งานเมื่อวาน " ในรายการลำดับความสำคัญ นอกหัวข้อบางส่วน แต่นอกเหนือจากนี้ฉันต้องการเห็นว่าคลาสที่สร้างขึ้นนั้นstaticเว้นแต่รายละเอียดการใช้งานจะห้ามไว้ เหตุผลก็คือเราสามารถใช้ตัวช่วยเสริมทั่วไป :@helper Foo<T>(this T o) where T : IBar { }
Dan Lugg

คำตอบ:


52

ไม่ตอนนี้ยังไม่สามารถทำได้ คุณสามารถเขียนตัวช่วย HTML ปกติแทนได้

public static MvcHtmlString DoSomething<T, U>(
    this HtmlHelper htmlHelper, 
    Expression<Func<T, U>> expr
) where T : class
{
    ...
}

แล้ว:

@(Html.DoSomething<SomeModel, string>(x => x.SomeProperty))

หรือหากคุณกำหนดเป้าหมายโมเดลเป็นอาร์กิวเมนต์ทั่วไปอันดับแรก:

public static MvcHtmlString DoSomething<TModel, TProperty>(
    this HtmlHelper<TModel> htmlHelper, 
    Expression<Func<TModel, TProperty>> expr
) where TModel : class
{
    ...
}

ซึ่งจะช่วยให้คุณสามารถเรียกสิ่งนี้ได้ (สมมติว่ามุมมองของคุณถูกพิมพ์อย่างรุนแรง แต่นั่นเป็นข้อสันนิษฐานที่ปลอดภัยเนื่องจากมุมมองทั้งหมดควรพิมพ์อย่างรุนแรง :-)):

@Html.DoSomething(x => x.SomeProperty)

10
หวังว่านี่จะเป็นสิ่งที่พวกเขาเพิ่มเข้าไปใน Razor helpers รุ่นต่อ ๆ ไป ความสามารถในการอ่านของตัวช่วยแบบดั้งเดิมนั้นต่ำกว่าไวยากรณ์ของ @helper มาก
mkedobbs

2
ใช่ตกลง การเปลี่ยนกลับไปใช้วิธีการที่เก่ากว่าไม่เพียง แต่ดูด แต่ยังแยกผู้ช่วยของคุณโดยพลการ
George R

127

สิ่งนี้เป็นไปได้ที่จะบรรลุภายในไฟล์ตัวช่วยที่มี@functionsไวยากรณ์ แต่ถ้าคุณต้องการความสามารถในการอ่านแบบมีดโกนที่คุณอ้างถึงคุณจะต้องโทรหาผู้ช่วยทั่วไปเพื่อทำ HTML ให้พอดีและเสร็จสิ้น

โปรดทราบว่าฟังก์ชันในไฟล์ Helper เป็นแบบคงที่ดังนั้นคุณยังคงต้องส่งผ่านอินสแตนซ์ HtmlHelper จากหน้านี้ในกรณีที่คุณตั้งใจจะใช้วิธีการดังกล่าว

เช่น Views \ MyView.cshtml:

@MyHelper.DoSomething(Html, m=>m.Property1)
@MyHelper.DoSomething(Html, m=>m.Property2)
@MyHelper.DoSomething(Html, m=>m.Property3)

App_Code \ MyHelper.cshtml:

@using System.Web.Mvc;
@using System.Web.Mvc.Html;
@using System.Linq.Expressions;
@functions
{
    public static HelperResult DoSomething<TModel, TItem>(HtmlHelper<TModel> html, Expression<Func<TModel, TItem>> expr)
    {
        return TheThingToDo(html.LabelFor(expr), html.EditorFor(expr), html.ValidationMessageFor(expr));
    }
}
@helper TheThingToDo(MvcHtmlString label, MvcHtmlString textbox, MvcHtmlString validationMessage)
{
    <p>
        @label
        <br />
        @textbox
        @validationMessage
    </p>
}
...

คุณไม่จำเป็นต้องทำให้วิธีการคงที่ดังนั้นคุณไม่จำเป็นต้องส่ง Html / Url / Model etc ของคุณ
Sheepy

อืมทำไมมันไม่เหมาะกับฉันล่ะ ฉันได้รับ "ไม่สามารถเข้าถึงวิธีการที่ไม่คงที่ 'TheThingToDo' ในบริบทคงที่" ..
TweeZz

12
@ Sheepy นั่นเป็นความจริงเพียงครึ่งเดียว คุณถูกต้องคุณสามารถทำให้พวกเขาไม่คงที่ แต่คุณจะได้รับมากกว่าSystem.Web.WebPages.Html.HtmlHelper System.Web.Mvc.HtmlHelperมีโอกาสที่ดีว่าเป็นรุ่นจะไม่เหมาะสำหรับคุณเนื่องจากส่วนใหญ่วิธีการขยายจะมีการเขียนผิดWebPages System.Web.Mvc.HtmlHelperนอกจากนี้ไม่มีUrlคุณสมบัติและUrlHelperต้องใช้RequestContextซึ่งไม่มีในWebPagesเวอร์ชันนี้ สรุปแล้วคุณอาจจะต้องผ่านในไฟล์Mvc HtmlHelper.
Kirk Woll

1
ผู้ช่วยต้องเป็นส่วนหนึ่งของโฟลเดอร์ App_Code ??
Vishal Sharma

1
ใช่ไฟล์นี้จะต้องอยู่ใน{MyMvcProject}\App_Code`. It doesn't work as advertised when you place it elsewhere. The error *Cannot access non-static method 'TheThingToDo' in static context* disappears when you move MyHelper.cshtml` App_Codeเข้า DoSomethingควรเป็นแบบคงที่เพื่อให้คุณสามารถเรียก@MyHelper.DoSomething(..)ดูได้ หากคุณทำให้มันไม่คงที่คุณจะต้องสร้างอินสแตนซ์MyHelperก่อน
Grilse

3

ในทุกกรณีTModelจะเหมือนกัน (แบบจำลองที่ประกาศสำหรับมุมมอง) และในกรณีของฉันสิ่งTValueนี้จะเหมือนกันดังนั้นฉันจึงสามารถประกาศประเภทอาร์กิวเมนต์ Expression:

@helper FormRow(Expression<Func<MyViewModel, MyClass>> expression) {
  <div class="form-group">
    @(Html.LabelFor(expression, new { @class = "control-label col-sm-6 text-right" }))
    <div class="col-sm-6">
      @Html.EnumDropDownListFor(expression, new { @class = "form-control" })
    </div>
    @Html.ValidationMessageFor(expression)
  </div>
}

หากฟิลด์โมเดลของคุณมีทั้งหมดstringคุณสามารถแทนที่MyClassด้วยstring.

การกำหนดตัวช่วยสองหรือสามตัวด้วยตัวช่วยที่TValueกำหนดไว้อาจจะไม่เลวแต่ถ้าคุณมีอะไรมากกว่านั้นที่จะสร้างโค้ดที่น่าเกลียดฉันก็ไม่พบวิธีแก้ปัญหาที่ดี ฉันพยายามห่อ@helperจากฟังก์ชันที่ฉันใส่ไว้ใน@functions {}บล็อก แต่ฉันไม่เคยทำได้ตามเส้นทางนั้น


เป็นสิ่งที่ชัดเจนเมื่อคุณคิดเกี่ยวกับเรื่องนี้ - ถ้าTModelคุณอาจจะรู้ล่วงหน้า
ta.speot.is

0

หากปัญหาหลักของคุณคือการได้รับค่าแอตทริบิวต์ชื่อสำหรับการผูกโดยใช้นิพจน์แลมบ์ดาดูเหมือนว่า@Html.TextBoxFor(x => x.MyPoperty)และหากส่วนประกอบของคุณมีแท็ก html ที่ซับซ้อนมากและควรนำไปใช้กับผู้ช่วยมีดโกนทำไมไม่สร้างวิธีการขยายHtmlHelper<TModel>เพื่อแก้ไข ชื่อผูกพัน:

namespace System.Web.Mvc
{
    public static class MyHelpers
    {
        public static string GetNameForBinding<TModel, TProperty>
           (this HtmlHelper<TModel> model, 
            Expression<Func<TModel, TProperty>> property)
        {
            return ExpressionHelper.GetExpressionText(property);
        }
    }
}

ผู้ช่วยมีดโกนของคุณควรเป็นเหมือนปกติ:

@helper MyComponent(string name)
{
    <input name="@name" type="text"/>
}

จากนั้นคุณสามารถใช้งานได้ที่นี่

@TheHelper.MyComponent(Html.GetNameForBinding(x => x.MyProperty))

นี่ไม่ใช่สิ่งที่ @ Html.IdFor (... ) มีไว้เพื่อ?
Alex Dresko

ใช่คุณสามารถทำได้@Htm.IdForแต่ต้องมีกระบวนการเพิ่มเติมในการแปลงเป็นสตริง ( .ToHtmlString()) ซึ่งผู้ช่วยเหลือต้องการสตริง
ktutnik
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.