Display EditorFor() value as Read-Only on MVC View?

Analytic Lunatic picture Analytic Lunatic · Feb 18, 2015 · Viewed 37.2k times · Source

I have several fields I display on most of my views in my MVC5 Code-First app: [created_date], [created_by], [modified_date], and [modified_by]. For the user, I would like to include these also on my Edit() view, but unlike my other fields I do not wish to have them editable (commonly created/by and modified/by data should NOT be editable).

This is how I have the fields declared on my Edit() view currently:

    <div class="form-group">
        @*@Html.LabelFor(model => model.created_date, htmlAttributes: new { @class = "control-label col-md-2" })*@
        <span class="control-label col-md-2">Created Date:</span>
        <div class="col-md-10">
            @Html.DisplayFor(model => model.created_date, new { htmlAttributes = new { @class = "form-control" } })
            @Html.ValidationMessageFor(model => model.created_date, "", new { @class = "text-danger" })
        </div>
    </div>

    <div class="form-group">
        @*@Html.LabelFor(model => model.created_by, htmlAttributes: new { @class = "control-label col-md-2" })*@
        <span class="control-label col-md-2">By:</span>
        <div class="col-md-10">
            @Html.DisplayFor(model => model.created_by, new { htmlAttributes = new { @class = "form-control" } })
            @Html.ValidationMessageFor(model => model.created_by, "", new { @class = "text-danger" })
        </div>
    </div>

    <div class="form-group">
        @*@Html.LabelFor(model => model.modified_date, htmlAttributes: new { @class = "control-label col-md-2" })*@
        <span class="control-label col-md-2">Modified Date:</span>
        <div class="col-md-10">
            @Html.DisplayFor(model => model.modified_date, new { htmlAttributes = new { @class = "form-control" } })
            @Html.ValidationMessageFor(model => model.modified_date, "", new { @class = "text-danger" })
        </div>
    </div>

    <div class="form-group">
        @*@Html.LabelFor(model => model.modified_by, htmlAttributes: new { @class = "control-label col-md-2" })*@
        <span class="control-label col-md-2">By:</span>
        <div class="col-md-10">
            @Html.DisplayFor(model => model.modified_by, new { htmlAttributes = new { @class = "form-control" } })
            @Html.ValidationMessageFor(model => model.modified_by, "", new { @class = "text-danger" })
        </div>
    </div>

Can anyone offer a way to make these fields read-only?

I tried DisplayFor() like on my Details View, but when I go to save the record, the value in my EditorFor() for [created_date] changes from "2015-02-18" to "0001-01-01" and created_by changes from my system username as previously set to null with the validation message "The created_by field is required." I believe this has something to do with my model annotations for the fields, but I don't see why the DisplayFor() is not passing the value through my controller like how the EditorFor() does...?

Model Annotations:

    [Required]
    [DataType(DataType.Date)]
    [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
    public DateTime created_date { get; set; }

    [Required]
    public string created_by { get; set; }

    [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}")]
    public DateTime? modified_date { get; set; }

    public string modified_by { get; set; }

Answer

user3559349 picture user3559349 · Feb 18, 2015

DisplayFor() does not generate a control that posts back. You need to include a hidden input in addition to the display text, for example

<div class="form-group">
    <span class="control-label col-md-2">@Html.DisplayNameFor(m => created_by)</span>
    <div class="col-md-10">
        @Html.DisplayFor(model => model.created_by
        @Html.HiddenFor(m => created_by)
    </div>
</div>

Another option is to make the textbox readonly

<div class="form-group">
    @Html.LabelFor(model => model.created_by, new { @class = "control-label col-md-2" })
    <div class="col-md-10">
        @Html.TextBoxFor(model => model.created_by, new { @class = "form-control", @readonly = "readonly" })
    </div>
</div>

Note also there is no point adding @Html.ValidationMessageFor()

Side note: There is nothing to stop a user changing the values of a hidden input (using FireBug for example), so as always, using a view model is the best approach (the view model would not include validation attributes on the 'readonly' properties) and then when posting, get the original data model and map the relevant view model properties to the data model before saving.