Ustaw atrybut disable na podstawie warunku dla Html.TextBoxFor

81

Chcę ustawić wyłączenie atrybutu na podstawie warunku dla Html.TextBoxFor w asp.net MVC, jak poniżej

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

Ten pomocnik ma dwa wyjścia wyłączone = „wyłączone” lub wyłączone = „”. oba motywy powodują wyłączenie pola tekstowego.

Chcę wyłączyć pole tekstowe, jeśli Model.ExpireDate == null w przeciwnym razie chcę je włączyć

Ghooti Farangi
źródło
Spójrz na moją odpowiedź tutaj: stackoverflow.com/a/43131930/6680521
Extragorey

Odpowiedzi:

85

Prawidłowy sposób to:

disabled="disabled"

Przeglądarki również mogą to zaakceptować, disabled="" ale polecam pierwsze podejście.

Mając to na uwadze, zalecałbym napisanie niestandardowego pomocnika HTML w celu hermetyzacji tej funkcji wyłączającej w fragment kodu wielokrotnego użytku:

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

którego możesz użyć w ten sposób:

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

i możesz wnieść jeszcze więcej inteligencji do tego pomocnika:

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

dzięki czemu nie musisz już określać warunku wyłączenia:

@Html.MyTextBoxFor(
    model => model.ExpireDate, 
    new { 
        style = "width: 70px;", 
        maxlength = "10", 
        id = "expire-date" 
    }
)
Darin Dimitrov
źródło
Chcę wyłączyć pole tekstowe, jeśli Model.ExpireDate == null jeszcze chcę to włączyć
Ghooti Farangi
4
To rozwiązanie jest świetne - o ile to możliwe ... ale byłoby miło znaleźć czyste rozwiązanie, które nie wymaga umieszczania otoki wokół każdego używanego przez nas HtmlHelpera, który może mieć wyłączony atrybut (TextBoxFor, TextAreaFor, CheckBoxFor itp. .) Idealnie coś, co działa w zgodzie z istniejącymi. Zbudowałem rozwiązanie, które po prostu otacza anonimowy obiekt i zwraca RouteValueDictionary - ale nie wydaje się być szczególnie czyste.
Mir
3
„disabled”, „disabled = ''” i „disabled = 'disabled'” są jednakowo poprawne w html i mylące (i fałszywe) jest twierdzenie, że te krótsze mogą być akceptowane tylko przez różne przeglądarki. Por. dev.w3.org/html5/markup/syntax.html#syntax-attr-empty
Shautieh
53

W rzeczywistości wewnętrzne zachowanie polega na tłumaczeniu anonimowego obiektu na słownik. Więc to, co robię w tych scenariuszach, to szukanie słownika:

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

Albo, jak Stephen skomentował tutaj :

@Html.EditorFor(m => m.Description,
    Model.IsDisabled ? (object)new { disabled = "disabled" } : (object)new { })
Shimmy Weitzhandler
źródło
@ Html.EditorFor (m => m.Description, Model.IsDisabled? (Object) new {disabled = "disabled"}: (object) new {}) => to wydaje się najlepszy sposób. Dzięki
Carmine Checker,
23

Podoba mi się metoda Darina. Ale szybki sposób rozwiązania tego problemu,

Html.TextBox("Expiry", null, new { style = "width: 70px;", maxlength = "10", id = "expire-date", disabled = "disabled" }).ToString().Replace("disabled=\"disabled\"", (1 == 2 ? "" : "disabled=\"disabled\""))
user571646
źródło
1
Myślę, że powinieneś otoczyć to @ Html.Raw ()
Shadi Namrouti
14

Jednym prostym podejściem, które zastosowałem, jest renderowanie warunkowe:

@(Model.ExpireDate == null ? 
  @Html.TextBoxFor(m => m.ExpireDate, new { @disabled = "disabled" }) : 
  @Html.TextBoxFor(m => m.ExpireDate)
)
mfsumption
źródło
13

Jeśli nie używasz pomocników html, możesz użyć prostego wyrażenia trójskładnikowego w następujący sposób:

<input name="Field"
       value="@Model.Field" tabindex="0"
       @(Model.IsDisabledField ? "disabled=\"disabled\"" : "")>
Andrei Tserakhau
źródło
13

Osiągnąłem to za pomocą kilku metod rozszerzających

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

i wtedy....

@Html.TextBoxFor(model => model.Name, new { @class= "someclass"}).IsDisabled(Model.ExpireDate == null)
MurtuzaB
źródło
Działa, jeśli zmienisz nieprzetworzony ciąg.Długość - 2 na 7 i dodasz „po ostatniej”.
Jozef Krchňavý
Nie działa z TextAreaFor, potrzebuję rozwiązania dla wszelkiego rodzaju typów danych wejściowych
erhan355
10

To późno, ale niektórym osobom może się przydać.

Rozszerzyłem odpowiedź @ DarinDimitrov, aby umożliwić przekazanie drugiego obiektu, który przyjmuje dowolną liczbę logicznych atrybutów html, takich jak disabled="disabled" checked="checked", selected="selected" itp.

Wyrenderuje atrybut tylko wtedy, gdy wartość właściwości jest prawdziwa, cokolwiek innego i atrybut nie zostanie w ogóle renderowany.

Niestandardowy HtmlHelper do ponownego wykorzystania:

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

}

Którego możesz użyć w ten sposób:

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

Jest rozwiązany za pomocą RouteValueDictionary (działa dobrze jako htmlAttributes, ponieważ jest oparty na IDictionary) i metody rozszerzenia:

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

Stosowanie:

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

Kredyt trafia do https://stackoverflow.com/a/3481969/40939

Niels Bosma
źródło
IMHO, to najlepsza odpowiedź
JenonD
6

jeśli nie chcesz używać pomocników HTML, spójrz na moje rozwiązanie

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

że to

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

i myślę, że lepszym sposobem na zrobienie tego jest sprawdzenie kontrolera i zapisanie go w zmiennej dostępnej w widoku (silnik Razor), aby the view free from business logic

Basheer AL-MOMANI
źródło
7
Jeśli użyjesz atrybutu disabled w kontrolce, formant zostanie wyłączony bez względu na wartość atrybutu. Nawet obecność atrybutu bez wartości spowoduje wyłączenie kontroli.
Geeky Guy
2
To rozwiązanie działa naprawdę dobrze i podejrzewam, że osoby negatywnie nastawione mogły przeoczyć, że wyrażenie jest logiczne. Gdy wyrażenie jest logiczne, wyłączony atrybut będzie renderowany jako disabled = "disabled", jeśli wyrażenie ma wartość true, lub zostanie całkowicie pominięty, jeśli false. To jest dokładnie to, czego chcesz.
Carsten
Spowoduje to renderowanie disabled = "false" lub disabled = "true", nie?
Andez
4

Jeszcze innym rozwiązaniem byłoby utworzenie Dictionary<string, object>przed wywołaniem TextBoxFori przekazanie tego słownika. W słowniku dodaj "disabled"klucz tylko wtedy, gdy pole tekstowe ma być wyłączone. Nie jest to najładniejsze rozwiązanie, ale proste i nieskomplikowane.

Kjell Rilbe
źródło
2

Innym podejściem jest wyłączenie pola tekstowego po stronie klienta.

W twoim przypadku masz tylko jedno pole tekstowe, które musisz wyłączyć, ale rozważ przypadek, w którym masz wiele pól wejściowych, zaznaczania i pól tekstowych, których nie musisz wyłączać.

Dużo łatwiej jest to zrobić za pomocą jquery + (ponieważ nie możemy polegać na danych pochodzących od klienta) dodaj trochę logiki do kontrolera, aby zapobiec zapisywaniu tych pól.

Oto przykład:

<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
źródło
0

Podoba mi się podejście oparte na metodzie rozszerzenia, więc nie musisz przechodzić przez wszystkie możliwe parametry.
Jednak używanie wyrażeń regularnych może być dość trudne (i nieco wolniejsze), więc XDocumentzamiast tego użyłem :

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

Użyj następującej metody rozszerzenia:
@Html.EditorFor(m => m.MyProperty).SetDisabled(Model.ExpireDate == null)

Kapé
źródło