How can I set attributes when my model property is rendered with any Html helper?
Example:
I have my custom data annotation:
[MyCustomAttribute(AnyHtmlProperty = "100")]
public string Name{get;set;}
So, when this property is rendered, I want something like this:
<input type="text" anyHtmlProperty="100" />
You could write an HtmlHelper extension method which accepts an expression. Here's a non-functioning example of what the razor syntax would look like.
#Html.CustomInputFor(x => x.Name)
Related
When using any of the Input Extension Helper Methods, like #Html.TextboxFor, any Validation Attributes from your model are automatically generated by the Razor engine (via ClientValidationEnabled/UnobtrusiveJavaScriptEnabled).
For example, take the following case which works fine
Model:
[Required]
public string QuestionOne { get; set; }
View:
#Html.TextBoxFor(model => model.QuestionOne)
#Html.ValidationMessageFor(model => model.QuestionOne)
Generated Markup:
<input type="text" id="QuestionOne" name="QuestionOne" value=""
data-val="true" data-val-required="The QuestionOne field is required." >
<span class="field-validation-valid" data-valmsg-for="QuestionOne" data-valmsg-replace="true"></span>
In this case the attributes data-val="true" & data-val-required="The QuestionOne field is required." are picked up by Unobtrusive validation and the form element is successfully validated.
However, for extensibility reasons, I want to be able to generate the <input> element myself instead of using TextBoxFor. So my view would now look like this:
<input type="textbox"
id="#Html.IdFor(m => m.QuestionTwo)"
name="#Html.NameFor(m => m.QuestionTwo)"
value="#Model.QuestionTwo"
data-val="true" data-val-required="Selection is Required" />
#Html.ValidationMessageFor(model => model.QuestionTwo)
In this case, I'm faking the validation attribute output by just re-writing data-val="true" (etc) by hand, but this would have to be expanded to cover every single case.
Here's a running Demo in .NET Fiddle
Q: Can I build /return a list of data-val-* attributes for a given element?
You can use the GetUnobtrusiveValidationAttributes() method of HtmlHelper to get the validation attributes associated with a specific property.
For example in the view
#{ var attributes = Html.GetUnobtrusiveValidationAttributes("QuestionTwo"); }
<input
type="textbox"
#foreach(var attr in attributes)
{
#:#attr.Key="#attr.Value"
}
id="#Html.IdFor(m => m.QuestionTwo)"
....
/>
Note the #:#attr.Key="#attr.Value" line will give a warning (Missing attribute name) but will run correctly
Alternatively, you could use javaScript/jQuery to add the attributes
<script type="text/javascript">
var attributes = #Html.Raw(Json.Encode(attributes));
var input = $('#QuestionTwo');
for(var i in attributes) {
input.attr(i, attributes[i]);
}
</script>
I have forked the DotNetFiddle here to show the working code for both options.
While the above code shows how it can be done, you should not be doing that. The HtmlHelper methods execute a lot of code your ignoring to ensure correct 2-way model binding, for example, the value attribute is determined by first checking for a value in ModelState, then in the ViewDataDictionary, and only if the previous values do not exist, does it use the value of the property (the second part of TextBoxFor displaying initial value, not the value updated from code explains the behavior).
Except for the incorrect value attribute, the code you have shown for the <input> is the same as will be generated by simply using #Html.TextBoxFor(m => m.Question2). I assume your real case is different, but if you cannot make use of TextBoxFor() and using an overload that accepts htmlAttributes to generate the html you need, then the correct approach is to create your own HtmlHelper method (and making use of existing methods in the HtmlHelper class and System.Web.Mvc.Html namespace)
I want to extend the helper to make it like this:
#html.TextBoxFor(x=>x.CustomerId).ReadOnly()
and output the input element without the name attribute, so that it will not be posted to the server.
This should do the trick:
public static class MyInputExtensions
{
public static MvcHtmlString NameLessTextBoxFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression)
{
var textBox = htmlHelper.TextBoxFor(expression);
string pattern = #"name=""([^""]*)""";
string fixedHtml = Regex.Replace(textBox.ToHtmlString(), pattern, "");
return new MvcHtmlString(fixedHtml);
}
}
Usage:
#Html.NameLessTextBoxFor(x=> x.CustomerId)
You can't do it.
(at least without some ugly workarounds with processing string value returned from helper)
Html helpers were written to help you generate form fields for your model with intention that they will sent data to server. For strongly-typed helper (like Html.TextBoxFor(x => x.CustomerId)) the name is taken from passed expression and for not strongly-typed helpers (like Html.TextBoxFor("CustomerId", Model.CustomerId)) there is a check that throws exception when name is null or empty.
If you want to generate input without "name" attribute then simply do not use html helper methods.
For example, if you want to change you html helper usage to generate same output but without "name" attribute then:
instead of this:
#Html.TextBoxFor(x => x.BetAmount)
write this:
<input type="text" value="#Model.BetAmount" />
instead of this:
#Html.TextBoxFor(x => x.BetAmount, new { #class = "red", placeholder = "Type Stuff", data_maximum_value = Model.MaximumBetAmount })
write this:
<input type="text" value="#Model.BetAmount" class="red" placeholder="Type Stuff" data_maximum_value="#Model.MaximumBetAmount" />
instead of this (you use overload with "format" argument):
#Html.TextBoxFor(x => x.BetAmount, "{0:0}", new { #class = "blue" })
write this:
<input type="text" value="#Html.FormatValue(Model.BetAmount,"{0:0}")" class="red" />
because Html.TextBoxFor uses Html.FormatValue when you pass "format" argument.
This is not exactly the same what html helper do because html helpers first tries to get data from ModelState for validation purpose (it's a common gotcha). But for 99% of times this is probably good enough
I recommend checking actual source code of ASP.NET MVC if you want to know what helper methods are actually doing. It's not black magic.
I have the following class:
public class City {
public string Name { get; set; }
public bool CityValid { get; set; }
}
I know how to set up the name but how can I set up the CityValid field so it acts like a checkbox. I'd like to do this without using HTML helpers.
If you really don't want to use helpers, you would use a normal HTML input tag:
<input type="checkbox" id="CityValid" name="CityValid" value="#Model.CityValid" />
<input type="hidden" id="CityValue_Hidden" name="CityValid" value="false" />
The name attribute has to match your property name so that the model binder will pick it up correctly when you post back to the server.
When you use the helpers, something similar to the above markup will be generated. The hidden field is there so that a value is always sent with the form post data, regardless of whether you check the box or not (if you leave the box unchecked, no value gets sent by default, not even a 'false').
However, unless you're doing something really weird, I'd recommend you stick to using the helpers. Either:
#Html.CheckboxFor(m => m.CityValid)
or
#Html.EditorFor(m => m.CityValid)
In your view you could use the EditorFor helper:
#model City
...
#Html.EditorFor(x => x.CityValid)
The default editor template for a boolean field generates a checkbox.
You could first create it with a HTML-helper, look at the markup that gets created when running the page, and then recreate that markup...
ASP.Net Html.TextBoxFor (and the other XxxxxFor editor helper methods) default to rendering a field prefix. How do I disable field prefixes so it simply renders the property name as the Name/ID?
Here's an example:
<%= Html.EditorFor(m => chart.Title) %>
is rendered as:
<input id="Chart_Title" name="Chart.Title" type="text" value="">
I would prefer it to be rendered as:
<input id="Title" name="Title" type="text" value="">
There's a parameter in an overload of EditorFor called htmlFieldName. Specify your name here.
The helpers take whatever you give it. So you need to pass in just the "Title" portion of the variable; maybe try assigning title to a variable, and use Html.EditorFor for that Title variable, and that might give you a different response.
The reason it does that is the helpers are setup to reflect from the Model context, so typically you may see Model.Chart.Title, and as such, the helpers create this path in the name so it can post the entire model back to the action method, if it needed to.
HTH.
I have a complex object that if I render in this way:
<%=Html.EditorFor(x => x.ContactRoleDto.Competencies[0].Name) %>
The above generates the following element which has the name and id tags that I want:
<input Size="40" id="ContactRoleDto_Name" maxlength="100" name="ContactRoleDto.Name" type="text" value="" />
I would like to render a tag with the correct id and name attributes that are in the same form as above, i.e. ContactRoleDto.Competencies[0].Name".
How is the best way to achieve this?
All I really want is the ability to pull out the correct id and name fields that will help me model bind a table that is dynamically generated or rendered.
Is ModelMetaData the best way to go, I do not want to go the UIHint route.
You could use the ExpressionHelper.GetExpressionText method. For example:
Expression<Func<string, YourModel>> expression = x => x.ContactRoleDto.Competencies[0].Name;
string id = ExpressionHelper.GetExpressionText(expression);