Html.TextBox conditional attribute with ASP.NET MVC Preview 5 - asp.net-mvc

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)

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

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.

How do I set dynamic readonly on MVC 5 razor view?

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

Can I change the html name generated by Html.DropDownListFor?

IDI am using the following code to create a drop down list:
#for (var index = 0; index < Model.AdminSummaries.Count(); index++)
{
<div class="rep_tr0">
<div class="rep_td0">
#Html.DropDownListFor(x => Model.AdminSummaries[index].Status, AdminStatusReference.GetAdminStatusOptions())
</div>
The code creates the following:
<select id="AdminSummaries_2__Status" name="AdminSummaries[2].Status">
<option value="1">Released</option>
<option value="2">Review</option>
<option value="3">New</option>
</select>
<select id="AdminSummaries_3__Status" name="AdminSummaries[3].Status">
<option value="1">Released</option>
<option value="2">Review</option>
<option value="3">New</option>
</select>
Is there any way that I could change it so that it creates a ID of "Status_2", "Status_3" etc.
As #Anar said in the comments;
Actually, you can change the name attribute the same way as id, but instead of "name", use "Name". Surprisingly it works.
#Html.DropDownListFor(x => Model.AdminSummaries[index].Status,
AdminStatusReference.GetAdminStatusOptions(),
new { id = string.Format("Status_{0}",index ), Name = "GiveName" });
Sometimes the HTML helpers don't HELP. DropDownListFor coding can get complicated real fast and in the end it's just rendering HTML so sometimes it's better to go old school
<select name="myname" class="dropdownlist" id="myid">
#foreach (SelectListItem item in Model.SomeSelectList) {
if (item.Value == Model.theValue) {
<option value="#(item.Value)" selected="selected">#item.Text</option>
} else {
<option value="#(item.Value)">#item.Text</option>
}
}
</select>
For those wondering why this doesn't work, the whole thing is case sensitive and need an # in front of it...
so, this works:
new { #id = "myID", #Name = "myName", #Value = "myValue", #class = "form-control", #onchange = "javascript:DoSomething(this.value);" }
And this doesn't (mind the lowercase 'n' in #name)
new { #id = "myID", #name = "myName", #Value = "myValue", #class = "form-control", #onchange = "javascript:DoSomething(this.value);" }
You can set/change the ID just like any HTML attribute (but not name I've found).
#Html.DropDownListFor(x => Model.AdminSummaries[index].Status, AdminStatusReference.GetAdminStatusOptions(), new { id = string.Format("Status_{0}",index ) });
Just use "Name" instead of "name" and it works.
#Html.DropDownList("ClassID", null, "Select Class", htmlAttributes: new { id = _id, Name =_id, #class = "form-control" })
You need to change From DropDownListFor To DropDownList. Than you can change name easily.
#Html.DropDownList("YourName", SelectList, new { #id = "YourId", #class = "YourClass" })
Hope It will work.
ID can be changed as described by #Arbiter. However, if you want to change the name, then I think you need to use #Html.DropDownList rather than #Html.DropDownListFor. This loses you any benefits of having the strong typing for the property element, but as just changing IDs means you can't access the values in a meaningful way if looking at the Request.Form response, this may be an acceptable workaround for you.
Thanks .. it work...adding .Name change the name of the html password attribute
#Html.PasswordFor(Function(model) model.password, New With {.Autocomplete = "off", .Name = "oldpassword", .id = "oldpassword", .value = Model.password, .readonly = True})
My solution to this issue is to replace the id and name after DropDownListFor() with a html helper extension method.
public static MvcHtmlString ReplaceIdAndName(this MvcHtmlString htmlSource, string name)
{
MvcHtmlString result;
if (name == null)
result = htmlSource;
else
{
string id = name.Replace("[", "_").Replace("]", "_").Replace(".", "_"); // HTML ids cannot have []. so MVC convention replaces with _
XDocument html = XDocument.Parse(htmlSource.ToHtmlString());
html.Elements().Select(e => e.Attribute("id")).FirstOrDefault().SetValue(id);
html.Elements().Select(e => e.Attribute("name")).FirstOrDefault().SetValue(name);
result = MvcHtmlString.Create(html.ToString());
}
return result;
}
DropDownListFor(...).ReplaceIdAndName("myclass[1].myprop");

Enabling & disabling a textbox in razor view (ASP.Net MVC 3)

I want to Enable or Disable a textbox based on the value (Model.CompanyNameEnabled).
The below code is not working. Please rectify.
#{
string displayMode = (Model.CompanyNameEnabled) ? "" : "disabled = disabled";
#Html.TextBox("CompanyName", "", new { displayMode })
}
#{
object displayMode = (Model.CompanyNameEnabled) ? null : new {disabled = "disabled" };
#Html.TextBox("CompanyName", "", displayMode)
}
You should pass htmlAttribute as anonymous object, with property names = html attribute names, property values = attribute values. Your mistake was that you were passing string instead of name=value pair
<input id="textbox1" type="text" #{#((Model.CompanyNameEnabled) ? null : new { disabled = "disabled" })}; />
Haven't tested it, but should work
A simple approach:
#Html.TextBoxFor(x => x.Phone, new { disabled = "disabled", #class = "form-control" })
As is already mentioned in this thread the suggested answer doesn't work in MVC5 anymore. There's actually an easy two step solution to that problem.
Assign a class to the HTML inputs you want to be disabled / enabled (id will do for a single item just as fine of course). In the example below I assigned a class 'switch-disabled' to the input.
#Html.TextBox("CompanyName", "", new { htmlAttributes = new { #class = "form-control switch-disable" } })
Use javascript(jquery) to enable / disable the disabled parameter in HTML. In my example below I do this at the page load.
<script>
$(document).ready(() => {
if(#Model.CompanyNameEnabled)
{
$('.switch-disable').attr("disabled", false);
}else{
$('.switch-disable').attr("disabled", true);
}
});
</script>

Resources