Establezca el atributo de desactivación según una condición para Html.TextBoxFor

81

Quiero establecer el atributo de desactivación según una condición para Html.TextBoxFor en asp.net MVC como se muestra a continuación

@Html.TextBoxFor(model => model.ExpireDate, new { style = "width: 70px;", maxlength = "10", id = "expire-date" disabled = (Model.ExpireDate == null ? "disable" : "") })

Este ayudante tiene dos salidas disabled = "disabled" o disabled = "". ambos temas desactivan el cuadro de texto.

Quiero deshabilitar el cuadro de texto si Model.ExpireDate == null de lo contrario, quiero habilitarlo

Ghooti Farangi
fuente
Eche un vistazo a mi respuesta aquí: stackoverflow.com/a/43131930/6680521
Extragorey

Respuestas:

85

La forma válida es:

disabled="disabled"

Los navegadores también pueden aceptar, disabled="" pero le recomendaría el primer enfoque.

Ahora bien, dicho esto, le recomendaría escribir un ayudante HTML personalizado para encapsular esta funcionalidad de desactivación en un fragmento de código reutilizable:

using System;
using System.Linq.Expressions;
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Html;
using System.Web.Routing;

public static class HtmlExtensions
{
    public static IHtmlString MyTextBoxFor<TModel, TProperty>(
        this HtmlHelper<TModel> htmlHelper, 
        Expression<Func<TModel, TProperty>> expression, 
        object htmlAttributes, 
        bool disabled
    )
    {
        var attributes = new RouteValueDictionary(htmlAttributes);
        if (disabled)
        {
            attributes["disabled"] = "disabled";
        }
        return htmlHelper.TextBoxFor(expression, attributes);
    }
}

que podrías usar así:

@Html.MyTextBoxFor(
    model => model.ExpireDate, 
    new { 
        style = "width: 70px;", 
        maxlength = "10", 
        id = "expire-date" 
    }, 
    Model.ExpireDate == null
)

y podrías aportar aún más inteligencia a este ayudante:

public static class HtmlExtensions
{
    public static IHtmlString MyTextBoxFor<TModel, TProperty>(
        this HtmlHelper<TModel> htmlHelper,
        Expression<Func<TModel, TProperty>> expression,
        object htmlAttributes
    )
    {
        var attributes = new RouteValueDictionary(htmlAttributes);
        var metaData = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
        if (metaData.Model == null)
        {
            attributes["disabled"] = "disabled";
        }
        return htmlHelper.TextBoxFor(expression, attributes);
    }
}

de modo que ahora ya no necesita especificar la condición de deshabilitado:

@Html.MyTextBoxFor(
    model => model.ExpireDate, 
    new { 
        style = "width: 70px;", 
        maxlength = "10", 
        id = "expire-date" 
    }
)
Darin Dimitrov
fuente
Quiero deshabilitar el cuadro de texto si Model.ExpireDate == null, de lo contrario, quiero habilitarlo
Ghooti Farangi
4
Esta solución es excelente, en la medida de lo posible ... pero sería bueno encontrar una solución limpia que no requiera poner un contenedor alrededor de cada HtmlHelper que usamos que pueda tener un atributo deshabilitado (TextBoxFor, TextAreaFor, CheckBoxFor, etc. .) Idealmente algo que funcione en línea con los existentes. He creado una solución que básicamente envuelve el objeto anónimo y devuelve un RouteValueDictionary, pero no se siente especialmente limpio.
Mir
3
"disabled", "disabled = ''" y "disabled = 'disabled'" son todos igualmente válidos en html, y es engañoso (y falso) decir que los más cortos solo pueden ser aceptados por diferentes navegadores. Cf. dev.w3.org/html5/markup/syntax.html#syntax-attr-empty
Shautieh
53

En realidad, el comportamiento interno es traducir el objeto anónimo a un diccionario. Entonces, lo que hago en estos escenarios es buscar un diccionario:

@{
  var htmlAttributes = new Dictionary<string, object>
  {
    { "class" , "form-control"},
    { "placeholder", "Why?" }        
  };
  if (Model.IsDisabled)
  {
    htmlAttributes.Add("disabled", "disabled");
  }
}
@Html.EditorFor(m => m.Description, new { htmlAttributes = htmlAttributes })

O, como Stephen comentó aquí :

@Html.EditorFor(m => m.Description,
    Model.IsDisabled ? (object)new { disabled = "disabled" } : (object)new { })
Shimmy Weitzhandler
fuente
@ Html.EditorFor (m => m.Description, Model.IsDisabled? (Object) new {disabled = "disabled"}: (object) new {}) => esta parece la mejor manera. Gracias
Carmine Checker
23

Me gusta el método Darin. Pero una forma rápida de resolver esto,

Html.TextBox("Expiry", null, new { style = "width: 70px;", maxlength = "10", id = "expire-date", disabled = "disabled" }).ToString().Replace("disabled=\"disabled\"", (1 == 2 ? "" : "disabled=\"disabled\""))
usuario571646
fuente
1
Creo que deberías rodear esto con @ Html.Raw ()
Shadi Namrouti
14

Un enfoque simple que he utilizado es la representación condicional:

@(Model.ExpireDate == null ? 
  @Html.TextBoxFor(m => m.ExpireDate, new { @disabled = "disabled" }) : 
  @Html.TextBoxFor(m => m.ExpireDate)
)
mfsumo
fuente
13

Si no usa ayudantes html, puede usar una expresión ternaria simple como esta:

<input name="Field"
       value="@Model.Field" tabindex="0"
       @(Model.IsDisabledField ? "disabled=\"disabled\"" : "")>
Andrei Tserakhau
fuente
13

Lo logré usando algunos métodos de extensión.

private const string endFieldPattern = "^(.*?)>";

    public static MvcHtmlString IsDisabled(this MvcHtmlString htmlString, bool disabled)
    {
        string rawString = htmlString.ToString();
        if (disabled)
        {
            rawString = Regex.Replace(rawString, endFieldPattern, "$1 disabled=\"disabled\">");
        }

        return new MvcHtmlString(rawString);
    }

    public static MvcHtmlString IsReadonly(this MvcHtmlString htmlString, bool @readonly)
    {
        string rawString = htmlString.ToString();
        if (@readonly)
        {
            rawString = Regex.Replace(rawString, endFieldPattern, "$1 readonly=\"readonly\">");
        }

        return new MvcHtmlString(rawString);
    }

y entonces....

@Html.TextBoxFor(model => model.Name, new { @class= "someclass"}).IsDisabled(Model.ExpireDate == null)
MurtuzaB
fuente
Funciona si cambia rawstring.Length - 2 por 7 y agrega "" después del último ".
Jozef Krchňavý
No funciona con TextAreaFor, necesita una solución para todo tipo de tipos de entrada
erhan355
10

Esto es tarde, pero puede ser útil para algunas personas.

He extendido la respuesta de @ DarinDimitrov para permitir pasar un segundo objeto que toma cualquier número de atributos html booleanos como disabled="disabled" checked="checked", selected="selected" etc.

Representará el atributo solo si el valor de la propiedad es verdadero, cualquier otra cosa y el atributo no se representará en absoluto.

El HtmlHelper reutilizable personalizado:

public static class HtmlExtensions
{
    public static IHtmlString MyTextBoxFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper,
                                                                Expression<Func<TModel, TProperty>> expression,
                                                                object htmlAttributes,
                                                                object booleanHtmlAttributes)
    {
        var attributes = new RouteValueDictionary(htmlAttributes);

        //Reflect over the properties of the newly added booleanHtmlAttributes object
        foreach (var prop in booleanHtmlAttributes.GetType().GetProperties())
        {
            //Find only the properties that are true and inject into the main attributes.
            //and discard the rest.
            if (ValueIsTrue(prop.GetValue(booleanHtmlAttributes, null)))
            {
                attributes[prop.Name] = prop.Name;
            }                
        }                                

        return htmlHelper.TextBoxFor(expression, attributes);
    }

    private static bool ValueIsTrue(object obj)
    {
        bool res = false;
        try
        {
            res = Convert.ToBoolean(obj);
        }
        catch (FormatException)
        {
            res = false;
        }
        catch(InvalidCastException)
        {
            res = false;
        }
        return res;
    }

}

Que puedes usar así:

@Html.MyTextBoxFor(m => Model.Employee.Name
                   , new { @class = "x-large" , placeholder = "Type something…" }
                   , new { disabled = true})
mrkosko
fuente
10

Se resuelve esto usando RouteValueDictionary (funciona bien como htmlAttributes ya que se basa en IDictionary) y un método de extensión:

public static RouteValueDictionary AddIf(this RouteValueDictionary dict, bool condition, string name, object value)
{
    if (condition) dict.Add(name, value);
    return dict;
}

Uso:

@Html.TextBoxFor(m => m.GovId, new RouteValueDictionary(new { @class = "form-control" })
.AddIf(Model.IsEntityFieldsLocked, "disabled", "disabled"))

El crédito va a https://stackoverflow.com/a/3481969/40939

Niels Bosma
fuente
En mi humilde opinión, esta es la mejor respuesta
JenonD
6

Si no quieres usar Html Helpers, mira mi solución.

disabled="@(your Expression that returns true or false")"

eso es

@{
    bool isManager = (Session["User"] as User).IsManager;
}
<textarea rows="4" name="LetterManagerNotes" disabled="@(!isManager)"></textarea>

y creo que la mejor manera de hacerlo es verificar el controlador y guardarlo dentro de una variable que sea accesible dentro de la vista (motor Razor) para hacer the view free from business logic

Basheer AL-MOMANI
fuente
7
Si usa el atributo deshabilitado en un control, el control se deshabilitará sin importar el valor que tenga el atributo. Incluso la presencia del atributo sin un valor deshabilitará el control.
Geeky Guy
2
Esta solución funciona muy bien, y sospecho que los votantes negativos pueden haber pasado por alto que la expresión es booleana. Cuando la expresión es booleana, el atributo disabled se mostrará como disabled = "disabled" si la expresión es verdadera, o se omitirá por completo si es falsa. Que es exactamente lo que quieres.
Carsten
Esto representará disabled = "false" o disabled = "true", ¿no?
Andez
4

Otra solución más sería crear un Dictionary<string, object>antes de llamar TextBoxFory pasar ese diccionario. En el diccionario, agregue la "disabled"clave solo si el cuadro de texto se va a deshabilitar. No es la solución más ordenada, pero sí simple y directa.

Kjell Rilbe
fuente
2

Otro enfoque es deshabilitar el cuadro de texto en el lado del cliente.

En su caso, solo tiene un cuadro de texto que necesita deshabilitar, pero considere el caso en el que tiene varios campos de entrada, selección y área de texto que necesita deshabilitar.

Es mucho más fácil hacerlo a través de jquery + (ya que no podemos confiar en los datos que vienen del cliente) agregar algo de lógica al controlador para evitar que estos campos se guarden.

Aquí hay un ejemplo:

<input id="document_Status" name="document.Status" type="hidden" value="2" />

$(document).ready(function () {

    disableAll();
}

function disableAll() {
  var status = $('#document_Status').val();

  if (status != 0) {
      $("input").attr('disabled', true);
      $("textarea").attr('disabled', true);
      $("select").attr('disabled', true);
  }
}
Dror
fuente
0

Me gusta el enfoque del método de extensión para que no tenga que pasar por todos los parámetros posibles.
Sin embargo, usar expresiones regulares puede ser bastante complicado (y algo más lento), así que usé en su XDocumentlugar:

public static MvcHtmlString SetDisabled(this MvcHtmlString html, bool isDisabled)
{
    var xDocument = XDocument.Parse(html.ToHtmlString());
    if (!(xDocument.FirstNode is XElement element))
    {
        return html;
    }

    element.SetAttributeValue("disabled", isDisabled ? "disabled" : null);
    return MvcHtmlString.Create(element.ToString());
}

Utilice el método de extensión como este:
@Html.EditorFor(m => m.MyProperty).SetDisabled(Model.ExpireDate == null)

Kapé
fuente