ASP.NET MVC brzytwa: atrybut warunkowy w HTML


84

Poniższy kod nie wydaje się czysty. Jakieś sugestie dotyczące ulepszenia kodu?

<li @if(ViewData["pagename"].ToString()=="Business details"){ <text>class="active" </text> } >
        <a  @if(ViewData["pagename"].ToString()=="Business details"){ <text>style="color: white; background-color: #08C; border: 1px solid #08C;" </text> }
            href="@Url.Action("BusinessDetails", "Business")">Business Details</a>
    </li> 
    <li @if (ViewData["pagename"].ToString() == "Booking policies"){ <text>class="active"</text> }> 
        <a  @if (ViewData["pagename"].ToString() == "Booking policies")
               { <text>style="color: white; background-color: #08C; border: 1px solid #08C;" </text> }
            href="@Url.Action("BookingPolicies", "Business")">Booking policies</a> 
    </li> 

Może stworzenie niestandardowego pomocnika HTML, który wyrenderowałby LI z podrzędnymi elementami odsyłaczy?
— Nick Bork

Odpowiedzi:


146

MVC ma wbudowane atrybuty warunkowe ...

<div @{if (myClass != null) { <text>class="@myClass"</text> } }>Content</div>
<div class="@myClass">Content</div>

Jeśli @myClass ma wartość null, po prostu w ogóle nie użyje atrybutu ...

Wiem, że może to nie rozwiązać Twojego obecnego problemu, ale jest godny uwagi!

http://weblogs.asp.net/jgalloway/archive/2012/02/16/asp-net-4-beta-released.aspx


Uważaj, aby nazwa Twojej klasy nie była „aktywna”, w przeciwnym razie będziesz mieć atrybut klasy wejściowej. Nie mam pojęcia dlaczego.
— ScottE

3
Czy istnieje sposób na osiągnięcie tego samego zachowania z poszanowaniem wartości null podczas wywoływania pomocników html przekazujących anonimowy htmlPropertiesobiekt? Np. Chcę warunkowo przekazać atrybut disabled, jak @Html.TextBoxFor(lambda, new { disabled = condition ? true : null }), ale to nadal renderuje disabled=""kiedy disabledbyło null, co jest tym samym, co renderowanie , disabled="anything"ponieważ disabledjest aktywne, gdy atrybut jest obecny, niezależnie od wartości. Znalazłem stackoverflow.com/q/7978137/11683 na ten temat, ale zastanawiam się, czy są obecnie lepsze sposoby?
— GSerg

1
Uwaga dodatkowa: atrybuty „data -...” nie mogą być warunkowe i renderować pustą wartość nawet dla null ( stackoverflow.com/questions/13267619/… )
— Alexei Levenkov

76
<li class="@(ViewBag.pagename == "Business details" ? "active" : null)">  

Powinieneś zamienić inline style="..."na oddzielną nazwę klasy i użyć tam tej samej składni.

Byłoby jednak bardziej przejrzyste utworzenie oddzielnej metody rozszerzenia pomocnika HTML, która pobiera nazwę strony i akcji oraz generuje kod HTML w sposób ogólny.


1
Ładny, znacznie czystszy niż niektóre inne opcje (poza opcją Razor 2.0).
— ctrlplusb

21

Używam małej metody pomocniczej, która warunkowo doda atrybut, jeśli wartość nie jest pusta i, jeśli jest zdefiniowana, gdy wartość wyrażenia funkcji logicznej wyniesie true:

public static MvcHtmlString Attr(this HtmlHelper helper, string name, string value, Func<bool> condition = null)
{
    if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(value))
    {
        return MvcHtmlString.Empty;
    }

    var render = condition != null ? condition() : true;

    return render ? 
        new MvcHtmlString(string.Format("{0}=\"{1}\"", name, HttpUtility.HtmlAttributeEncode(value))) : 
        MvcHtmlString.Empty;
}

Po zdefiniowaniu mogę użyć tej metody w moich widokach Razor:

<li @(Html.Attr("class", "new", () => example.isNew))>
...
</li>

Powyższy kod wyrenderuje się, <li class="new">...</li>jeśli example.isNew == true, jeśli nie, pominie cały classatrybut.


1
Bardzo elegancki sposób na zrobienie tego. Ale zamiast Func<bool>lambdy wolę prosty bool?parametr, bo jest prostszy:<li @Html.Attr("class", "new", example.isNew)>
— T-moty

Podoba mi się to podejście, ponieważ wiele niestandardowych skryptów JavaScript w mojej aplikacji będzie nadal działać, gdy nazwa atrybutu będzie nadal dostępna. A przynajmniej nie powoduje to powtarzania tego samego div z powodu różnic atrybutów. Dzięki!
— Ben Sewards,


0

Podejście z metodą rozszerzenia TagWrap. Kod Twojego pytania wyglądałby tak:

@using (Html.TagWrap("li", condition ? new { @class = "active" } : null))
{
    var anchorAttrs = new Dictionary<string, object> { { "href", Url.Action("BusinessDetails", "Business") } };
    if(condition)
    {
        anchorAttrs["style"] = "color: white; background-color: #08C; border: 1px solid #08C;";
    }
    using (Html.TagWrap("a", anchorAttrs))
    {
        <text>Business Details</text>
    }
}

Metody rozszerzenia TagWrap

using Microsoft.AspNetCore.Mvc.ViewFeatures;

public static IDisposable TagWrap(this IHtmlHelper htmlHelper, string tagName, object data)
{
    return htmlHelper.TagWrap(tagName, HtmlHelper.AnonymousObjectToHtmlAttributes(data));
}

public static IDisposable TagWrap(this IHtmlHelper htmlHelper, string tagName, IDictionary<string, object> data)
{
    var tag = new TagBuilder(tagName);
    tag.MergeAttributes(data);

    htmlHelper.ViewContext.Writer.Write(tag.RenderStartTag());

    return new DisposableAction(() =>
        htmlHelper.ViewContext.Writer.Write(tag.RenderEndTag()));
}

Klasa pomocnicza używana do renderowania tagu zamykającego w Dispose

public class DisposableAction : IDisposable
{
    private readonly Action DisposeAction;

    public DisposableAction(Action action)
    {
        DisposeAction = action;
    }

    public void Dispose()
    {
        DisposeAction();
    }
}

0

Na podstawie rozmrażania odpowiedz tutaj na adaptację, przyjmując objectzamiast a string:

    public static MvcHtmlString ConditionalAttr(this HtmlHelper helper, string attributeName, object value, Func<bool> condition)
    {
        if (string.IsNullOrEmpty(attributeName) || value == null)
        {
            return MvcHtmlString.Empty;
        }

        var render = condition != null ? condition() : true;

        return render ? 
            new MvcHtmlString($"{attributeName}=\"{HttpUtility.HtmlAttributeEncode(value.ToString())}\"") : 
            MvcHtmlString.Empty;
    }

W ten sposób nie musisz zamieniać innych typów danych w łańcuchy przed ich przekazaniem, oszczędzając fiew .ToString(). Jest jednak różnica : przekazanie pustego ciągu będzie nadal renderowane. Jako przykład:

@Html.ConditionalAttr("data-foo", "", () => Model.IsFooNeeded)

// Ouput:
data-foo=""

0
@{ var classAttr= needClass ? "class=\"class-name\"" : "" }

a następnie w kodzie HTML

<li @Html.Raw(classAttr) >  
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.