Search code examples
asp.net-mvcrazorhtml.dropdownlistforhtml.listboxfor

Disabled property not working for ListBoxFor and DropDownListFor


I have a web page that dynamically creates components by rendering them through a RenderPartial Html Helper like this

Html.RenderPartial("_UploadStaticField", config);

Within the config object is a field called 'isUnderReview'. When this is set to true the components should be disabled by using the code below

//Single selection
<div class="editor-section">
    <div class="label">
        @Html.DisplayEditLabel(Model.Label, Model.Required.Value)
    </div>
    <div class="field large-text-field">
        @Html.DropDownListFor(m => m.SelectedListOptionID, new SelectList(Model.ListOptions, "ID", "Name", Model.SelectedListOptionID).OrderBy(l => l.Value), new Dictionary<string, object>{
                    {"id", "staticField_" + Model.ID}})
    </div>
</div>

<script>
    $(document).ready(function () {
        if ("@Model.IsUnderReview" == "True") {
        document.getElementById("staticField_" + "@Model.ID").disabled = true;
        }
    });
</script>

and..

//Multiple selection
<div class="editor-section">
    <div class="label">
        @Html.DisplayEditLabel(Model.Label, Model.Required.Value)
    </div>
    <div class="field large-text-field">
        @Html.ListBoxFor(x => x.SelectedRoles, filetypes, new { @class = "multiselectFileTypes" , id = "staticFieldM_" + Model.ID})
    </div>
</div>

@Scripts.Render(BundleConfig.Scripts_MultiSelect)
<script>
    $(document).ready(function () {
        if ("@Model.IsUnderReview" == "True") {
            document.getElementById("staticFieldM_" + "@Model.ID").disabled = true;
        }
    });
</script>

The code works to the point that the methods run but the components are still able to be used. Is there a way of cancelling any users selections which will serve as disabling as the values wont change?


Solution

  • Scripts should never be in partials (you potentially generating multiple inline scripts that may cause other problems, especially with the bundle). However a script is not even necessary for this, you can use a simple if statement or conditional attributes to generate what you want.

    When you say "but the components are still able to be used", I'm guessing that your using a plugin to generate controls as suggested by Scripts.Render(BundleConfig.Scripts_MultiSelect) which will be hiding the original <select> element and generating its own html which is why it still interactive.

    But the next problem is that disabled controls do not post back a value, so the vales of SelectedListOptionID and SelectedRoles will be their default, possibly resulting in your app failing depending on the code in your POST method.

    Move the @Scripts.Render() into you view or layout, delete the scripts to disable the element and then change the partial to

    @if(Model.IsUnderReview)
    {
        @Html.HiddenFor(m => m.SelectedListOptionID) // if you want the value to be posted
        // add an element to display the Name associated with the SelectedListOptionID
        // if necessary, for example your view model might include a property
        // string SelectedListOptionName
    }
    else
    {
        @Html.DropDownListFor(m => m.SelectedListOptionID, new SelectList(Model.ListOptions, "ID", "Name").OrderBy(l => l.Value))
    }
    

    Side notes:

    1. There is no reason to add you own id attribute (the DropDownListFor() method generates <select id="SelectedListOptionID" ... >
    2. Remove the last parameter of the SelectList constructor (Model.SelectedListOptionID) - its ignored by the DropDownListFor() method. I also recommend your model contains an IEnumerable<SelectListItem> OptionsList property and you populate that in the controller so that it can simply be @Html.DropDownListFor(m => m.SelectedListOptionID, Model.OptionsList)