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.