mvc3, editor template, css clas, maxlength and size - asp.net-mvc-3

I have an editor template as following but class, maxlength and size attributes are not getting to the source.
#using System.Globalization
#model DateTime?
#Html.TextBox("", (Model != null && Model.HasValue && !Model.Value.ToString(CultureInfo.InvariantCulture).Contains("1900") && !Model.Value.ToString(CultureInfo.InvariantCulture).Contains("0001") ? Model.Value.ToString("MM/dd/yyyy") : string.Empty), new { #class = "datePicker", maxlength = "12", size = "12" })
I have changed it to following and it is still the same
#Html.EditorFor(x => x.Criteria.FromDate, new { #class = "datePicker", maxlength = "12", size = "12" })
Source
<input class="text-box single-line" id="Criteria_FromDate" name="Criteria.FromDate" type="text" value="" />
How can i fix this?

Make sure your Editor template is named DateTime - placed in folder Views/Shared/EditorTemplates, and your model (Criteria.FormDate) is same type as EditorTemplate model (DateTime?).
If all DateTime fields will have same maxlength and size you can keep them hardcoded in your EditorTemplate.
Example for your html:
#EditorFor(x => x.Criteria.FormDate) //no need to pass html attributes object if they are not used in the editor template
-- its worth trying #EditorFor(model, "EditorTemplateName") to explicitly say you want that TemplateEditor for passed model. This is the case when you have multiple editors for same model type, so you call them explicitly(works like calling partial view and passing model to it).
EDIT:
After looking at your template, it seems to me that your Criteria.FormDate is non nullable. You should look at improving/refactoring your code in template.

Related

Custom Editor template based on ViewModel Dataannotation attribute MVC4

What I want to do is automatically add an image span after my input textboxes if the [Required] attribute decorates my ViewModel property be it an integer, double, string, date etc
For example, my ViewModel might look like
public class MyViewModel
{
[Required]
public string Name { get; set; }
}
and my View would look like
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
and the output would be something like
<input id="Name" class="text-box single-line" type="text" value="" name="Name" data-val-required="The Name field is required." data-val-length-max="20" data-val-length="The field Name must be a string with a maximum length of 20." data-val="true">
<span class="field-validation-valid" data-valmsg-replace="true" data-valmsg-for="Name"></span>
-- Note the automatically added span
<span class="indicator required" style="width: 11px;"></span>
I was intending to have some css that would show the image i.e.
span.required {
background-image: url("required.png");
}
Is this possible to do or do I need to create my own Helper method to implement this type of functionality?
Yes, it's possible, but in general I wouldn't recommend it, because templates are really there to customize type rendering, and you should be able to create templates without worrying if it overrides another template.
I would instead create a custom LabelFor helper, such as the one described here:
http://weblogs.asp.net/imranbaloch/archive/2010/07/03/asp-net-mvc-labelfor-helper-with-htmlattributes.aspx
or here:
http://weblogs.asp.net/raduenuca/archive/2011/02/17/asp-net-mvc-display-visual-hints-for-the-required-fields-in-your-model.aspx
A third option is to not do anything in MVC, but rather add some javascript that will add the indicator based on the standard MVC validation data attributes (if you're using unobtrusive validation). See the answer here:
https://stackoverflow.com/a/8524547/61164
What I did was to modify the jquery.validate.unobtrusive JS file to add a second container, specifically for your images, if there is a validation error.
var container2 = $(this).find("[data-valimg-for='" + escapeAttributeValue(inputElement[0].name) + "']"),
replace = $.parseJSON(container.attr("data-valimg-replace")) !== false;
container2.removeClass("img-validation-valid").addClass("img-validation-error");
Then don't forget to bind it to the model:
error.data("unobtrusiveContainer", container2);
Finally, empty it in the if (replace) code block:
if (replace) {
container.empty();
container2.empty();
error.removeClass("input-validation-error").appendTo(container);
}
else {
error.hide();
}
On success, remember to hide it:
var container2 = error.data("unobtrusiveContainer"),
replace = $.parseJSON(container.attr("data-valimg-replace"));
if (container2) {
container2.addClass("img-validation-valid").removeClass("img-validation-error");
error.removeData("unobtrusiveContainer");
if (replace) {
container2.empty();
}
}
If you take a look at the onError and onSuccess functions in the file, you should be able to find out where you can put them in.
In your view, add the following line of code to each form input there's validation for:
<img class="img-validation-valid" data-valimg-replace="true" data-valimg-for="<replace with field name here, ie. Name>" src="required.png" />
I've only tested this with the [Required] attribute, but it works. I'm also pretty sure you can use this for generating other stuff as well, not just images.

Validation of DropDownListFor is not working in MVC3?

Validation is Working on Other Input type text element but not working on DropDownListFor
Class Purchase Input Property Code
[Required]
public string LedgerId { get; set; }
Class View Model Code
PurchaseViewModel purchaseVM = new PurchaseViewModel
{
// PurchaseInput=purchaseInput,
Ledger = uw.LedgerRepository.Get().Select(x => new SelectListItem { Value = x.Id.ToString(), Text = x.LedgerName }),
};
View
<div class="column">
<div class="labelField">
#Html.LabelFor(model => model.PurchaseInput.LedgerId, "Party")
</div>
<div class="ItemField">
#Html.DropDownListFor(model => model.PurchaseInput.LedgerId, new SelectList(Model.Ledger, "Value", "Text"))
#Html.ValidationMessageFor(model => model.PurchaseInput.LedgerId)
</div>
</div>
On the face of it, it seems that you do not have an empty item in your select list. The validation will only trigger if the user selects a dropdown item with string length of zero. If you examine the Html source can you see the validation attributes on the dropdown ( depending on whether you are using unobtrusive validation or not)?
Yes, there are problems with validation of DropDownListFor. look at this link. They get validation data manually from metadata - http://forums.asp.net/t/1649193.aspx
Although this is a workaround, at least it fires some sort of validation. Try:
#Html.DropDownListFor(model => model.PurchaseInput.LedgerId, new SelectList(Model.Ledger, "Value", "Text"), new { #class = "required" })

How to add unobtrusive validation for html template fields whats not in the model

i got a template created for custom zip code field.
My template code is below:
#{
string model = Model ?? string.Empty;
string zipFirst = "";
string zipSecond = "";
if(!String.IsNullOrEmpty(model) && !String.IsNullOrWhiteSpace(model))
{
var values = model.Split('-');
if(values.Count() == 2)
{
zipFirst = values[0] ?? string.Empty;
zipSecond = values[1] ?? string.Empty;
}
}
var pName = ViewData.ModelMetadata.PropertyName;
var zipAreaId = "#" + pName + "zipcodearea";
}
<div id="#(pName)zipcodearea">
<input type="text" maxlength="5" id="zipcodefirst" name="#(pName)codefirst" value="#(zipFirst)" style="width:136px"/> -
<input type="text" maxlength="4" id="zipcodesecond" name="#(pName)codesecond" value="#(zipSecond)" style="width:120px"/>
#Html.HiddenFor(m => m, new { #class = "generatedZipField"})
</div>
<script type="text/javascript">
jQuery(function ($) {
$('#(zipAreaId) #zipcodefirst').autotab({ target: '#(pName)codesecond', format: 'numeric' });
$('#(zipAreaId) #zipcodesecond').autotab({ previous: '#(pName)codefirst', format: 'numeric' });
$('#(zipAreaId)').zipcode();
});
</script>
And i use it like this:
[UIHint("ZipCode")]
[Display(Name = "Zip Code")]
public string Zip { get; set; }
Like you see in my template i got two fields whats not included in the model.
It is #zipcodefirst and #zipcodesecond.
What i need to achieve is to have two separate fields for full us zip code.
When user fill both fields im using jquery widget for merging them into one string and inserting it into hidden field in template. after form submited value in hidden field getting sent into server.
Whats the problem?
I need to add mvc unobtrusive validation for them two fields whats not in the model #zipcodefirst and #zipcodesecond.
validation rules
zipcodefirst field must be filled in first
then zipcodefirst field is filled you can fill second field
second field must have 4 digits in it
first field must have five digits
cant fill second field while first one is empty or incorectly filled
Im strugling with validation part for quite a while now.... :(
How i could achieve that thru mvc3 unobtrusive validation tools?
any help will be highly apreciated guys.
Add unobrusive data data validation on the textbox by adding data-val="true" and use a regular expression for your zip code.
<input type="text" data-val="true" data-val-regex="Invalid zip code format" data-val-regex-pattern="YOUR REGEXP HERE" />
UPDATE
If you also want it to be required you can add the data-val-required attribute.
<input type="text" data-val="true" data-val-requred="Zip code is required" data-val-regex="Invalid zip code format" data-val-regex-pattern="YOUR REGEXP HERE" />
More information about validation in MVC 3:
http://bradwilson.typepad.com/blog/2010/10/mvc3-unobtrusive-validation.html

How to get model's field name in custom editor template

I'm building my first custom editor template for a text area control. My code so far is -
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<dynamic>" %>
<%= Html.TextAreaFor( Model => Model , 2, 30,
new { #class = "html", #placeholder = ViewData.ModelMetadata.Watermark }) %>
It's not much so far, but it does work. But we need to add a character counter field to show remaining number of characters that the user can type in. I know how to do all the JavaScript to make this work.
So to keep naming system same, I'm going to add a control named ".charCounter" to display number of remaining characters left. My problem is that I cannot figure out the correct syntax to be able to retrieve the field name for the model.
The final version will look something like (JavaScript omitted) -
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<dynamic>" %>
<%= Html.TextAreaFor( Model => Model , 2, 30,
new { #class = "html", #placeholder = ViewData.ModelMetadata.Watermark }) %>
<span class="xxx">Remaining characters -
<input readonly type="text" name="<fieldName>.charCounter" />
</span>
You could use ViewData.TemplateInfo.HtmlFieldPrefix, like this:
<input
readonly="readonly"
type="text"
name="<%= ViewData.TemplateInfo.HtmlFieldPrefix %>.charCounter"
/>
I ran into an issue where the PropertyName wasn't returning the Model prefix eg Contact.FirstName. I was able to have it return the HTML field Id using this:
#ViewData.TemplateInfo.GetFullHtmlFieldId("")
Returns:
Contact_FirstName
Respectively you can return the field name using:
#ViewData.TemplateInfo.GetFullHtmlFieldName("")
Returns:
Contact.FirstName
ViewData.ModelMetadata.PropertyName works nicely in MVC3.
In razor:
<input readonly="readonly" type="text" name="#ViewData.ModelMetadata.PropertyName">
Getting just the model name/Id (e.g BirthDate):
ViewData.ModelMetadata.PropertyName;
Getting the model name with prefixes for complex objects (e.g Identity.Person.BirthDate):
#Html.NameFor(m => Model);
or
ViewData.TemplateInfo.HtmlFieldPrefix;
Getting the model Id with prefixes for complex objects (e.g Identity_Person_BirthDate):
#Html.IdFor(m => Model)
or
#Html.IdForModel()

TextBoxFor vs EditorFor, and htmlAttributes vs additionalViewData

I have created a default MVC 3 project (using razor), in order to demonstrate an issue.
On the login page, there is a line:
#Html.TextBoxFor(m => m.UserName)
if I change this to:
#Html.TextBoxFor(m => m.UserName, new { title = "ABC" })
Then the it is rendered as (with a title attribute):
<input data-val="true" data-val-required="The User name field is required." id="UserName" name="UserName" title="ABC" type="text" value="" />
However, if I make it an EditorFor:
#Html.EditorFor(m => m.UserName, new { title = "ABC" })
Then it gets rendered (without a title attribute) as:
<input class="text-box single-line" data-val="true" data-val-required="The User name field is required." id="UserName" name="UserName" type="text" value="" />
So in summary, the title attribute is lost when I use EditorFor.
I know that the second parameter for TextBoxFor is called htmlAttributes, and for EditorFor it is additionalViewData, however I've seen examples where EditorFor can render attributes supplied with this parameter.
Can anyone please explain what I am doing wrong, and how I can have a title attribute when using EditorFor?
I think I found a little nicer solution to it. EditorFor takes in additionalViewData as a parameter. If you give it a parameter named "htmlAttributes" with the attributes, then we can do interesting things with it:
#Html.EditorFor(model => model.EmailAddress,
new { htmlAttributes = new { #class = "span4",
maxlength = 128,
required = true,
placeholder = "Email Address",
title = "A valid email address is required (i.e. user#domain.com)" } })
In the template (in this case, EmailAddress.cshtml) you can then provide a few default attributes:
#Html.TextBox("",
ViewData.TemplateInfo.FormattedModelValue,
Html.MergeHtmlAttributes(new { type = "email" }))
The magic comes together through this helper method:
public static IDictionary<string, object> MergeHtmlAttributes<TModel>(this HtmlHelper<TModel> htmlHelper, object htmlAttributes)
{
var attributes = htmlHelper.ViewData.ContainsKey("htmlAttributes")
? HtmlHelper.AnonymousObjectToHtmlAttributes(htmlHelper.ViewData["htmlAttributes"])
: new RouteValueDictionary();
if (htmlAttributes != null)
{
foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(htmlAttributes))
{
var key = property.Name.Replace('_', '-');
if (!attributes.ContainsKey(key))
{
attributes.Add(key, property.GetValue(htmlAttributes));
}
}
}
return attributes;
}
Of course you could modify it to render the attributes as well if you are doing raw HTML:
public static MvcHtmlString RenderHtmlAttributes<TModel>(this HtmlHelper<TModel> htmlHelper, object htmlAttributes)
{
var attributes = htmlHelper.ViewData.ContainsKey("htmlAttributes")
? HtmlHelper.AnonymousObjectToHtmlAttributes(htmlHelper.ViewData["htmlAttributes"])
: new RouteValueDictionary();
if (htmlAttributes != null)
{
foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(htmlAttributes))
{
var key = property.Name.Replace('_', '-');
if (!attributes.ContainsKey(key))
{
attributes.Add(key, property.GetValue(htmlAttributes));
}
}
}
return MvcHtmlString.Create(String.Join(" ",
attributes.Keys.Select(key =>
String.Format("{0}=\"{1}\"", key, htmlHelper.Encode(attributes[key])))));
}
In MVC3 you can add a title (and other htmlAttributes) using this kind of workaround if you create a custom EditorFor template. This case is prepared for the value to be optional, editorFor calls are not required to include the object additionalViewData
#Html.EditorFor(m => m.UserName, "CustomTemplate", new { title = "ABC" })
EditorTemplates/CustomTemplate.cshtml
#{
string s = "";
if (ViewData["title"] != null) {
// The ViewData["name"] is the name of the property in the addtionalViewData...
s = ViewData["title"].ToString();
}
}
#Html.TextBox("", ViewData.TemplateInfo.FormattedModelValue, new { title = s })
I did something very similar to include an optional class in an EditorTemplate. You can add as many items to the addtionalViewData as you like but you need to handle each on in the EditorFor template.
You may take a look at the following blog post which illustrates how to implement a custom metadata provider and use data annotations on your view model in order to define html properties such as class, maxlength, title, ... This could then be used in conjunction with the templated helpers.

Resources