How do I set dynamic readonly on MVC 5 razor view? - asp.net-mvc

I have a bool value IsAcre. If true, then textbox needs to be able to accept a value. If false, the textbox should be readonly. I am trying a couple different ways:
#Html.EditorFor(model => model.Activity.Acres, new { htmlAttributes = new { #class = "form-control", Model.Activity.IsAcres ? null : #readonly = "readonly" }} )
and
#Html.TextBoxFor(model => model.Activity.Volume, new { #class = "form-control", #readonly = "#Model.Activity.IsVolume" })
First one seems to be syntacticlly incorrect, the second one renders as:
readonly="False"
Even though it renders the false readonly, the textbox is still set to readonly, I assume because the bool value is passed as a string.
Is there a way to do this inline or will have to if/else on the entire textbox?

The readonly attribute is a boolean attribute which means that its presence represents the true value, so your second code snippet that renders readonly="False" makes the input readonly (although its invalid html). Note also from the specs
The values "true" and "false" are not allowed on boolean attributes. To represent a false value, the attribute has to be omitted altogether.
You need to use the code in your first code snippet to ensure the attribute is either added or omitted, although you could use alternatives such as
#{ var attributes = Model.Activity.IsAcres ? (object)new { #class = "form-control"} : (object)new { #class = "form-control", readonly = "readonly" }; }
#Html.TextBoxFor(model => model.Activity.Volume, attributes)
and if this is something you use regularly, then you could create a HtmlHelper extension method that conditionally adds the attribute based on the value of another property (say)
#Html.ReadOnlyTextBoxIf(m => m.Activity.Acres, Model.Activity.IsAcres, new { #class = "form-control" })

Related

Razor EditorFor with Onclick Event

I have nullable Boolean value that is being presented as a checkbox using the following code:
#Html.EditorFor(m => m.IsInitialStatus, new { htmlAttributes = new { #onclick = "InitialOrStarting()" } })
however the #onclick attribute is not being added to the HTML when the page is loaded. Am I missing something here? I had taken the example from an answer on this page.
I have also looked at changing this to a CheckBoxFor but keep getting an issue with the nullable Bool datatypes.
Any help on this would be appreciated! I just want a nullable bool checkbox with an onClick event firing to a Javascript function... I am not the most advanced user but this seems to be more difficult for me to do than maybe it should!?
EDIT
There appears to be an EditorTemplate for Boolean which contains:
#model bool?
#Html.CheckBox("", Model.GetValueOrDefault())
You are using the overload of EditorFor() where the 2nd parameter is additionalViewData. If you did not have a specific EditorTemplate for bool?, the method would generate the default template, which is a <select> with 3 values for null, true and false, and include the attributes.
But because you have an EditorTemplate, you need to add the attributes yourself by reading the value from the ViewDataDictionary (typically, an EditorTemplate includes multiple html elements, so the method cannot know which element you want to apply the attributes to).
Your template would need to be
#model bool?
#{ var attributes = ViewData["htmlAttributes"]; }
#Html.CheckBox("", Model.GetValueOrDefault(), attributes)
Having said that, your should not be doing this. A bool? has 3 states (null, true or false) but a checkbox has only 2 states - on or off (translates to true or false) so your EditorTemplate does not represent the possible values of your property.
If you only want to allow true or false, then your property should not be nullable. Alternatively, use the default template that does allow a null selection (or if you want an alternative UI, create a template that renders 3 radio buttons for example)
In addition, I recommend you stop polluting you markup with behavior and use Unobtrusive JavaScript - i.e. your script will be
$(yourCheckBox).click(function() {
... // do something
});
Onclick Event does not working for #HtmlEditorFor. But you can use class attribute.
<script>
$(".single-checkbox").on("change", function () {
if ($(".single-checkbox:checked").length > 2) {
this.checked = false;
alert ("Only 2 choice")
}
});
</script>
#Html.EditorFor(model => model.YourProperty, new { htmlAttributes = new { #class = "single-checkbox" } })
#Html.EditorFor(model => model.YourProperty, new { htmlAttributes = new { #class = "single-checkbox" } })
#Html.EditorFor(model => model.YourProperty, new { htmlAttributes = new { #class = "single-checkbox" } })
#Html.EditorFor(model => model.YourProperty, new { htmlAttributes = new { #class = "single-checkbox" } })

Custom EditorFor Template and htmlAttributes

I'm trying to use EditorFor custom templates.
I want to create a Int32 and decimal templates to render the inputs with some validations.
This is what I'm trying
#model int?
#Html.TextBoxFor(model => model, null, new { #type="text", #oninput = "this.value=this.value.replace(/[^0-9]/g,'')" } )
And I call it like
#Html.EditorFor(x => x.ExampleIntField)
It renders an <input type="text", oninput="this.value=this.value.replace(/[^0-9]/g,'')"
To here everything works, but when I try to pass extra htmlAttributes like readonly I don't understand how I must receive it in EditorFor template.
Example
#Html.EditorFor(x => x.ExampleIntField, new { htmlAttributes = new { #readonly = "readonly" } } )
I tried this I got the exact same <input type="text", oninput="this.value=this.value.replace(/[^0-9]/g,'')" rendered without readonly attribute
You are using the overload of EditorFor() that passes the object as additionalViewData. You can read that within the template from the ViewDataDictionary
#model int?
#{ var attributes = ViewData["htmlAttributes"]; } // returns { #readonly = "readonly" }
which you could then merge with your existing attributes and use in the TextBoxFor() method.
#{
var htmlAttributes = HtmlHelper.AnonymousObjectToHtmlAttributes(attributes);
htmlAttributes.Add("oninput", "this.value=this.value.replace(/[^0-9]/g,'')";
}
#Html.TextBoxFor(model => model, htmlAttributes)
Note that TextBoxFor() generates type="text" so there is no need to add it again. In addition, you do not need the leading # unless its a reserved keyword (for example #class = "...")

MVC 5 TextBoxFor not able to do conditional Bootstrap CSS?

I'm using a TextBoxFor and what I need to do is see if a property is NULL or Blank. If so then I need to let the end user type in their info. If not, meaning I can find their name, I need to disable the field so that they can not alter it.
I did some searching and try these two options but they are not working.
#Html.TextBoxFor(m => m.EmployeeName, string.IsNullOrWhiteSpace(Model.EmployeeName) ? new { Class = "form-control" } : new { Class = "form-control", disabled = "disabled" })
#Html.TextBoxFor(m => m.EmployeeName, new { Class = "form-control", string.IsNullOrWhiteSpace(Model.EmployeeName) ? disabled = "disabled" : null })
Any ideas on what I can try?
Your current code would be giving an error because there is no implicit conversion between the 2 anonymous types and you would need to cast them to object
var attributes = String.IsNullOrWhiteSpace(Model.EmployeeName) ?
(object)new { #class = "form-control" } :
(object)new { #class = "form-control", disabled = "disabled" })
#Html.TextBoxFor(m => m.EmployeeName, attributes)
Note however that disabling an control means its value will not be submitted, so if EmployeeName initially has a value, it will be null when submitted. You might want to consider using readonly = "readonly" instead.

ASP.NET MVC 4 - Set input value using #Html.EditorFor

Currently i have this field on a form:
#Html.EditorFor(model => model.AmountDecimal,
new { htmlAttributes = new { #class = "form-control" } })
But:
a) i want it to have the value "100" predefined
b) don't want it to be editable
I know how to do it in raw HTML but i need it to be in razor.
Thanks
It would make sense to set this value e.g. in the constructor of your model or in the controller before you call your view
public ActionResult MyAction()
{
var model = new MyViewModel
{
AmountDecimal= 100
};
return View(model);
}
But if you really like to do it in razor, you may use HiddenFor
#Html.HiddenFor(model => model.AmountDecimal, new { #Value = "100" });
<input type="text" name = "dummy" class="form-control" value="100" readonly/>
Keep in mind to never trust a user input ;)
I think your are loooking for something like this:
#Html.EditorFor(model => model.AmountDecimal,
new { htmlAttributes = new { #class = "form-control", #Value = "100", #readonly = "readonly"} })

Html.TextBox conditional attribute with ASP.NET MVC Preview 5

I have a strongly-typed MVC View Control which is responsible for the UI where users can create and edit Client items. I'd like them to be able to define the ClientId on creation, but not edit, and this to be reflected in the UI.
To this end, I have the following line:
<%= Html.TextBox("Client.ClientId", ViewData.Model.ClientId, new
{ #readonly =
(ViewData.Model.ClientId != null && ViewData.Model.ClientId.Length > 0
? "readonly" : "false")
} )
%>
It seems that no matter what value I give the readonly attribute (even "false" and ""), Firefox and IE7 make the input read-only, which is annoyingly counter-intuitive. Is there a nice, ternary-operator-based way to drop the attribute completely if it is not required?
Tough problem... However, if you want to define only the readonly attribute, you can do it like this:
<%= Html.TextBox("Client.ClientId", ViewData.Model.ClientId,
ViewData.Model.ClientId != null && ViewData.Model.ClientId.Length > 0
? new { #readonly = "readonly" }
: null)
%>
If you want to define more attributes then you must define two anonymous types and have multiple copies of the attributes. For example, something like this (which I don't like anyway):
ClientId.Length > 0
? (object)new { #readonly = "readonly", #class = "myCSS" }
: (object)new { #class = "myCSS" }
If you want to define several attributes, and conditional readonly without duplicate the other attributes,
you can use Dictionary instead of anonymous types for the attributes.
e.g.
Dictionary<string, object> htmlAttributes = new Dictionary<string, object>();
htmlAttributes.Add("class", "myCSS");
htmlAttributes.Add("data-attr1", "val1");
htmlAttributes.Add("data-attr2", "val2");
if (Model.LoggedInData.IsAdmin == false)
{
htmlAttributes.Add("readonly", "readonly");
}
#:User: #Html.TextBoxFor(
m => m.User,
htmlAttributes)
Tip: Its the mere presence of readonly/disabled attribute that makes the element readonly or disabled in the browser.
#Html.TextBoxFor(x => x.Name, isReadonly ?(object) new { #readonly = true } : new { /*Some other attributes*/ })
And alternative is just to emit it as plain old HTML. Yes, the editor will make you think you are wrong, but that seems to happen quite frequently with VS2008SP1. This example is specifically for checkboxes which seems to be completely wasted in CTP5, but it gives you an idea how to emit conditional attributes.
<input type="checkbox" name="roles" value='<%# Eval("Name") %>'
<%# ((bool) Eval("InRole")) ? "checked" : "" %>
<%# ViewData.Model.IsInRole("Admin") ? "" : "disabled" %> />
I think it should be
<%= ((bool) Eval("InRole")) ? "checked" : "" %>
instead of this in leppies answer.
<%# ((bool) Eval("InRole")) ? "checked" : "" %>
At least it did not work for me with # but it worked with =. Did i do anything wrong? Thanks for the tip anyway :)
i use this :
#Html.TextAreaFor(model => model.ComentarioGestor, comentarioGestor? new { #class = "form-control" } : new { #class = "form-control", #readonly = "readonly" } as object)
$(function() {
$("[readonly='false']").removeAttr("readonly");
});
I tried most of the suggestions above and now I have arrived at the simplest with a single line. Combine 2 anonymous html attributes object by declaring wither one of it as "object" type.
#Html.TextBoxFor(m => m.Email, !isEdit ? new { id = "email_box" } : new { id = "email_box", #readonly = isEdit ? "readonly" : "false" } as object)

Resources