Maquinilla de afeitar ASP.NET MVC: atributo condicional en HTML

83

El siguiente código no parece limpio. ¿Alguna sugerencia para mejorar el código?

<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> 
Mahesh
fuente
¿Quizás crear un ayudante HTML personalizado que represente el LI con elementos de enlace secundario?
Nick Bork

Respuestas:

142

MVC tiene atributos condicionales integrados ...

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

Si @myClass es nulo, simplemente no usará el atributo en absoluto ...

Sé que puede que no resuelva del todo su problema actual, ¡pero es digno de mención!

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

jcreamer898
fuente
Tenga cuidado de que el nombre de su clase no esté "activo" o tendrá un atributo de clase de entrada. No tengo idea de por qué.
ScottE
3
¿Hay alguna manera de lograr el mismo comportamiento de respeto de nulos al llamar a los ayudantes html que pasan el htmlPropertiesobjeto anónimo ? Por ejemplo, quiero pasar un atributo condicionalmente disabled, como @Html.TextBoxFor(lambda, new { disabled = condition ? true : null }), pero que aún se muestra disabled=""cuando disabledestaba null, que es lo mismo que reproducir disabled="anything"porque disabledestá activo cuando el atributo está presente, independientemente del valor. Encontré stackoverflow.com/q/7978137/11683 sobre el tema, pero me pregunto si hay mejores formas hoy en día.
GSerg
1
Nota al margen: los atributos "data -..." no pueden ser condicionales y representar un valor vacío incluso para nulos ( stackoverflow.com/questions/13267619/… )
Alexei Levenkov
75
<li class="@(ViewBag.pagename == "Business details" ? "active" : null)">  

Debe reemplazar el en línea style="..."con un nombre de clase separado y usar la misma sintaxis allí.

Sin embargo, sería más limpio crear un método de extensión auxiliar HTML separado que tome un nombre de página y acción y genere el HTML de forma genérica.

SLaks
fuente
1
Bonito, mucho más limpio que algunas de las otras opciones (además de la opción Razor 2.0).
ctrlplusb
21

Utilizo un pequeño método auxiliar que agregará condicionalmente un atributo si el valor no está vacío y, si está definido, cuando una expresión de función booleana se evalúa como 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;
}

Una vez definido, puedo usar este método en mis vistas de Razor:

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

El código anterior se mostrará <li class="new">...</li>si example.isNew == true, de lo contrario, se omitirá todo el classatributo.

descongelar
fuente
1
Una forma muy elegante de hacer eso. Pero en lugar de Func<bool>lambda, preferiré un bool?parámetro simple , porque es más simple:<li @Html.Attr("class", "new", example.isNew)>
T-moty
Me gusta este enfoque porque gran parte del JavaScript personalizado en mi aplicación aún se ejecutará cuando el nombre del atributo todavía esté allí. Y al menos esto no te hace repetir el mismo div debido a diferencias de atributos. ¡Gracias!
Ben Sewards
0

Aproximación con el método de extensión TagWrap. El código para su pregunta se vería así:

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

Métodos de extensión TagWrap

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

Clase auxiliar utilizada para representar la etiqueta de cierre en Dispose

public class DisposableAction : IDisposable
{
    private readonly Action DisposeAction;

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

    public void Dispose()
    {
        DisposeAction();
    }
}
Pavlo Neiman
fuente
0

Basado en desescarches, responda aquí una adaptación, tomando un en objectlugar de un 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;
    }

De esta manera, no tiene que convertir sus otros tipos de datos en cadenas antes de pasarlos, guardando una vista .ToString(). Sin embargo, hay una diferencia : pasar una cadena vacía aún se procesará. Como ejemplo:

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

// Ouput:
data-foo=""
spaark
fuente
0
@{ var classAttr= needClass ? "class=\"class-name\"" : "" }

y luego en el HTML

<li @Html.Raw(classAttr) >  
user4649102
fuente