Set disable attribute based on a condition for Html.TextBoxFor
Asked Answered
C

13

85

I want to set disable attribute based on a condition for Html.TextBoxFor in asp.net MVC like below

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

This helper has two output disabled="disabled " or disabled="". both of theme make the textbox disable.

I want to disable the textbox if Model.ExpireDate == null else I want to enable it

Circuitous answered 12/7, 2011 at 6:8 Comment(1)
Have a look at my answer here: https://mcmap.net/q/212987/-mvc3-conditionally-disable-html-textboxforSherbrooke
P
91

The valid way is:

disabled="disabled"

Browsers also might accept disabled="" but I would recommend you the first approach.

Now this being said I would recommend you writing a custom HTML helper in order to encapsulate this disabling functionality into a reusable piece of code:

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

which you could use like this:

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

and you could bring even more intelligence into this helper:

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

so that now you no longer need to specify the disabled condition:

@Html.MyTextBoxFor(
    model => model.ExpireDate, 
    new { 
        style = "width: 70px;", 
        maxlength = "10", 
        id = "expire-date" 
    }
)
Paddlefish answered 12/7, 2011 at 6:12 Comment(3)
I want to disable the textbox if Model.ExpireDate == null else I want to enable itCircuitous
This solution is great - as far as it goes... but it would be nice to find a clean solution that doesn't require putting a wrapper around every HtmlHelper we use that might have a disabled attribute (TextBoxFor, TextAreaFor, CheckBoxFor, etc.) Ideally something that works inline with the existing ones. I've built a solution that basically just wraps the anonymous object and returns a RouteValueDictionary - but it doesn't feel especially clean.Gloss
"disabled", "disabled=''" and "disabled='disabled'" are all equally valid in html, and it is misleading (and false) to say that the shorter ones might only be accepted by the different browsers. Cf. dev.w3.org/html5/markup/syntax.html#syntax-attr-emptyAutism
S
58

Actually, the internal behavior is translating the anonymous object to a dictionary. So what I do in these scenarios is go for a dictionary:

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

Or, as Stephen commented here:

@Html.EditorFor(m => m.Description,
    Model.IsDisabled ? (object)new { disabled = "disabled" } : (object)new { })
Stockman answered 25/3, 2015 at 5:18 Comment(1)
@Html.EditorFor(m => m.Description, Model.IsDisabled ? (object)new { disabled = "disabled" } : (object)new { }) => this seems the best way. ThanksCriticize
R
23

I like Darin method. But quick way to solve this,

Html.TextBox("Expiry", null, new { style = "width: 70px;", maxlength = "10", id = "expire-date", disabled = "disabled" }).ToString().Replace("disabled=\"disabled\"", (1 == 2 ? "" : "disabled=\"disabled\""))
Request answered 12/7, 2011 at 10:15 Comment(1)
I think you should surround this with @Html.Raw()Loisloise
T
18

One simple approach I have used is conditional rendering:

@(Model.ExpireDate == null ? 
  @Html.TextBoxFor(m => m.ExpireDate, new { @disabled = "disabled" }) : 
  @Html.TextBoxFor(m => m.ExpireDate)
)
Taggart answered 10/12, 2014 at 19:14 Comment(0)
K
16

If you don't use html helpers you may use simple ternary expression like this:

<input name="Field"
       value="@Model.Field" tabindex="0"
       @(Model.IsDisabledField ? "disabled=\"disabled\"" : "")>
Kelleher answered 24/10, 2016 at 8:43 Comment(0)
O
13

I achieved it using some extension methods

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

and then....

@Html.TextBoxFor(model => model.Name, new { @class= "someclass"}).IsDisabled(Model.ExpireDate == null)
Oviform answered 27/4, 2015 at 23:17 Comment(2)
Works if you change rawstring.Length - 2 for 7 and add " " after last ".Barcarole
Doesn't work with TextAreaFor, need a solution for all kind of input typesYate
M
11

Is solved this using RouteValueDictionary (works fine as htmlAttributes as it's based on IDictionary) and an extension method:

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

Usage:

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

Credit goes to https://mcmap.net/q/213229/-in-c-convert-anonymous-type-into-key-value-array

Misinform answered 29/10, 2015 at 9:34 Comment(1)
IMHO, this is the best answerOmophagia
P
10

This is late, but may be helpful to some people.

I have extended @DarinDimitrov's answer to allow for passing a second object that takes any number of boolean html attributes like disabled="disabled" checked="checked", selected="selected" etc.

It will render the attribute only if the property value is true, anything else and the attribute will not be rendered at all.

The custom reuseble HtmlHelper:

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

}

Which you can use like so:

@Html.MyTextBoxFor(m => Model.Employee.Name
                   , new { @class = "x-large" , placeholder = "Type something…" }
                   , new { disabled = true})
Precious answered 28/2, 2013 at 11:26 Comment(0)
S
7

if you dont want to use Html Helpers take look it my solution

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

that it

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

and I think the better way to do it is to do that check in the controller and save it within a variable that is accessible inside the view(Razor engine) in order to make the view free from business logic

Swaddle answered 29/8, 2016 at 13:34 Comment(3)
If you use the disabled attribute in a control, the control will be disabled no matter what value the attribute has. Even the presence of the attribute without a value will disable the control.Houstonhoustonia
This solution works really well, and I suspect downvoters may have overlooked that the expression is boolean. When the expression is boolean, the disabled attribute will render as disabled="disabled" if the expression is true, or be completely omitted if false. Which is exactly what you want.Threlkeld
This will either render disabled="false" or disabled="true", no?Nimesh
F
4

Yet another solution would be to create a Dictionary<string, object> before calling TextBoxFor and pass that dictionary. In the dictionary, add "disabled" key only if the textbox is to be diabled. Not the neatest solution but simple and straightforward.

Forestforestage answered 8/8, 2013 at 12:3 Comment(0)
P
2

Another approach is to disable the text box on the client side.

In your case you have only one textbox that you need to disable but consider the case where you have multiple input, select, and textarea fields that yout need to disable.

It is much easier to do it via jquery + (since we can not rely on data coming from the client) add some logic to the controller to prevent these fields from being saved.

Here is an example:

<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);
  }
}
Pennell answered 9/9, 2012 at 11:55 Comment(0)
J
1

I like the extension method approach so you don't have to pass through all possible parameters.
However using Regular expressions can be quite tricky (and somewhat slower) so I used XDocument instead:

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

Use the extension method like this:
@Html.EditorFor(m => m.MyProperty).SetDisabled(Model.ExpireDate == null)

Jerrine answered 5/6, 2020 at 13:22 Comment(0)
G
0

The extension below is very simple and straight forward. It gets the resulting HTML string returned by the helper and adds the disabled attribute if true.

using System.Web.Mvc.Html;
using System.Web.Mvc;   

namespace MyApplication.Extensions
{
    public static class MyExtensions
    {
        public static MvcHtmlString Locked(this MvcHtmlString htmlString, bool lockThis)
        {
            string html = htmlString.ToString();
            return new MvcHtmlString(lockThis ? html.Replace("id=", " disabled id=") : html );
        }
    }
}

This approach will work for all Html editing helpers and not just the HtmlTextBoxFor. Simply add .Locked(bool) to your fields and set the boolean condition at the top of your view as shown below.

Example:

@using MyApplication.Extensions
@model MyModel

@{
    // Set condition to true if not an Admin or Manager
    bool lockCondition = (!User.IsInRole("Admin") && !User.IsInRole("Manager"));
}
.
.
<div class="col-md-6">
   @Html.TextBoxFor(m => m.CommisionAmt).Locked(lockCondition)
</div>
Gerhan answered 1/11, 2023 at 21:37 Comment(1)
Thank you for your interest in contributing to the Stack Overflow community. This question already has quite a few answers—including one that has been extensively validated by the community. Are you certain your approach hasn’t been given previously? If so, it would be useful to explain how your approach is different, under what circumstances your approach might be preferred, and/or why you think the previous answers aren’t sufficient. Can you kindly edit your answer to offer an explanation?Heshum

© 2022 - 2024 — McMap. All rights reserved.