Asp.Net MVC2 Clientside Validation problem with controls with prefixes - validation

The problem is: when I put 2 controls of the same type on a page I need to specify different prefixes for binding. In this case the validation rules generated right after the form are incorrect. So how to get client validation work for the case?:
the page contains:
<%
Html.RenderPartial(ViewLocations.Shared.PhoneEditPartial, new PhoneViewModel { Phone = person.PhonePhone, Prefix = "PhonePhone" });
Html.RenderPartial(ViewLocations.Shared.PhoneEditPartial, new PhoneViewModel { Phone = person.FaxPhone, Prefix = "FaxPhone" });
%>
the control ViewUserControl<PhoneViewModel>:
<%= Html.TextBox(Model.GetPrefixed("CountryCode"), Model.Phone.CountryCode) %>
<%= Html.ValidationMessage("Phone.CountryCode", new { id = Model.GetPrefixed("CountryCode"), name = Model.GetPrefixed("CountryCode") })%>
where Model.GetPrefixed("CountryCode") just returns "FaxPhone.CountryCode" or "PhonePhone.CountryCode" depending on prefix
And here is the validation rules generated after the form. They are duplicated for the field name "Phone.CountryCode". While the desired result is 2 rules (required, number) for each of the FieldNames "FaxPhone.CountryCode", "PhonePhone.CountryCode"
alt text http://www.freeimagehosting.net/uploads/37fbe720bf.png
The question is somewhat duplicate of Asp.Net MVC2 Clientside Validation and duplicate ID's problem
but the advise to manually generate ids doesn't helps.

Correct way to set the same prefixes both for textbox and validation:
<% using (Html.BeginHtmlFieldPrefixScope(Model.Prefix)) { %>
<%= Html.TextBoxFor(m => m.Address.PostCode) %>
<%= Html.ValidationMessageFor(m => m.Address.PostCode) %>
<% } %>
where
public static class HtmlPrefixScopeExtensions
{
public static IDisposable BeginHtmlFieldPrefixScope(this HtmlHelper html, string htmlFieldPrefix)
{
return new HtmlFieldPrefixScope(html.ViewData.TemplateInfo, htmlFieldPrefix);
}
private class HtmlFieldPrefixScope : IDisposable
{
private readonly TemplateInfo templateInfo;
private readonly string previousHtmlFieldPrefix;
public HtmlFieldPrefixScope(TemplateInfo templateInfo, string htmlFieldPrefix)
{
this.templateInfo = templateInfo;
previousHtmlFieldPrefix = templateInfo.HtmlFieldPrefix;
templateInfo.HtmlFieldPrefix = htmlFieldPrefix;
}
public void Dispose()
{
templateInfo.HtmlFieldPrefix = previousHtmlFieldPrefix;
}
}
}
(by chance found the solution in the code on Steve Sanderson's blog http://blog.stevensanderson.com/2010/01/28/editing-a-variable-length-list-aspnet-mvc-2-style/)
Also looks like Html.EditorFor approach should work as well as suggested here: ASP.NET MVC 2 - ViewModel Prefix

Related

#foreach loop in MVC 3

I am new to MVC 3 and have this question to start with,
I have a class defined as
Class abc
{ public string Id { get; set; }
public string str1 { get; set; }
public string Action { get; set; }
public string Name { get; set; }
public string Title {get; set;}
}
on my MVC2 aspx viewpage , I was using this class abc as model and had this code
<%
Model.ForEach(a =>
{ %>
<%= Html.ActionLink(a.Title ,
a.Action , // <-- ActionMethod
a.Name , // <-- Controller Name.
new { key = a.Id }, // <-- Route arguments.
new { title = a.str1 })%>
<br /><br />
<% }); %>
can you please help me convert this piece of code to MVC razor view ?,
#model abc
<%
Model.ForEach(a =>
{ %>
<%= Html.ActionLink(a.Title ,
a.Action , // <-- ActionMethod
a.Name , // <-- Controller Name.
new { key = a.Id }, // <-- Route arguments.
new { title = a.str1 })%>
<br /><br />
<% }); %>
when I try to use #foreach ( var abc in Model) , I get error message , need to implement Ienumerable ? How can I implement using #for Please help or give me pointers.Thanks
The model shows only one object, not a list of objects.
Therefore you should not use ForEach but access the properties directly without a loop as Model.Name etc.
If you want a list of objects, then you need to update your controller to return a list of those by using a generic list, for example.
var abcCollection = new List<abc>();
That should point you in the right direction.
user1005310,
a bit of understanding of the Razor syntax will help here. there are plenty of examples out there via Mr google. However, if you have a LOAD of code to convert, then you have a great little 'tool' out there to help (now OSS, originally developed by Telerik). Take a look at:
https://github.com/telerik/razor-converter
this is basically a convertor that takes an entire set of aspx views and converts them to Razor. I've tried it on a few test projects now and it works to 99% of my satisfaction, the other 1% is being addressed (or i can live with the minor tweaking).
I'd recommend you using a display template. This way you don't need to write any loops. So:
#model IEnumerable<abc>
#Html.DisplayForModel()
and then you define a display template which will automatically be rendered for each element of the model collection (~/Views/Shared/DisplayTemplates/abc.cshtml):
#model abc
#Html.ActionLink(
Model.Title,
Model.Action,
Model.Name,
new { key = Model.Id },
new { title = Model.str1 }
)
<br /><br />
Notice that templates work by convention. They must be placed in either the ~/Views/Shared/DisplayTemplates folder or the ~/Views/SomeController/DisplayTemplates folder depending on whether you want to reuse them between views from multiple controllers or a single controller. ASP.NET MVC first looks in the specific folder for a template and then in the Shared. The name of the file is also important. In this case your model consists of an IEnumerable<abc> where abc is the type of the elements in this collection therefore the display template must be called abc.html.
Same rules apply for editor templates. Just replace display by editor. Editor templates, as their name suggests, are suitable for putting input fields for editing a view model.

Implementing jquery datepicker using a template

I am trying to implement a jquery datepicker in my MVC 3 application. It works fine in edit mode, but when trying to use it in the create view, I get the null dictionary exception which tells me it cannot take a null value and needs a value of DateTime. Of course it's going to be null, your trying to create a new field. How can I get this to work.
I would use EditorTemplate instead of custom helper
Create new partial view Date.ascx and place it in \Views\Shared\EditorTemplates\
<%# Control Language="C#" %>
<%: Html.TextBox("", Model == null ? "" : ((DateTime)Model).ToString("yyyy-MM-dd"), new { #class = "datepicker", #readonly = "readonly" })%>
That works fine for all properties of type DateTime
A lot easier using custom helper, im using helper below in one of my application.
1.Create Helper class
namespace System.Web.Mvc.Html
{
public static class DatePickerHelper
{
public static string DatePicker(this HtmlHelper htmlHelper, string id, string name, string value)
{
StringBuilder sBuilder = new StringBuilder();
sBuilder.AppendLine("<script language=\"javascript\" type=\"text/javascript\">");
sBuilder.AppendLine("$(function () {");
sBuilder.AppendLine("$(\"#" + id + "\").datepicker({");
sBuilder.AppendLine("showOn: \"button\",");
sBuilder.AppendLine("buttonImage: \"/Content/images/icon-calendar.gif\",");
sBuilder.AppendLine("dateFormat: 'dd/mm/yy',");
sBuilder.AppendLine("buttonImageOnly: true");
sBuilder.AppendLine(" });");
sBuilder.AppendLine("});");
sBuilder.AppendLine("</script>");
sBuilder.AppendLine("<input type=\"text\" value=\"" + value + "\" id=\"" + id + "\" name=\""+name+"\" class=\"SmallTextBox\" />");
return sBuilder.ToString();
}
}
}
use it like this on your view (For creating)
in my case i have Controller call Employee, make sure you use this pattern if you are using data model { "YouControllerName.PropertyName" and "YouControllerName_PropertyName"
<%= Html.DatePicker("Employee_StartDate","Employee.StartDate","") %>
For editing
<%=Html.DatePicker("Employee_StartDate","Employee.StartDate",Model.Employee.StartDate.ToShortDateString()) %>

Html.EditorForModel and Hiding element from Edit

I'm using the following code to render an editor for my model using ASP.NET MVC 3, it works perfect, except for I don't want the user to see or edit the "Id" field in my object.
<% using (Html.BeginForm())
{ %>
<%: Html.ValidationSummary(true, "Your input has errors, please correct and try again") %>
<%: Html.EditorForModel(Model)%>
<input type="submit" value="Update" />
<% } %>
In my model for the ID Field I have the following
[Display(AutoGenerateField = false)]
public int Id{ get; private set; }
Which granted is what I thought would work based on the description of the "AutoGenerateField" parameter. However this isn't working. I don't want to have to build the whole editor just for this one little oddity....
Use [ScaffoldColumn(false)] to hide fields
You could use the [HiddenInput] attribute:
[HiddenInput(DisplayValue = false)]
[Display(AutoGenerateField = false)]
public int Id { get; private set; }

asp.net mvc 2 client side validation missing ValidationRules on custom attribute

Can't seem to get checkbox to be validate on client-side using asp.net mvc 2. Here is my code.
Model
[Serializable]
public class RegistrationModel
{
bool termsAndCondition = false;
[RequiredToBeTrue(ErrorMessage = "Need terms and service")]
public bool TermsAndConditions
{
get
{
return termsAndCondition;
}
set
{
termsAndCondition = value;
}
}
}
Custom Attribute
public class RequiredToBeTrueAttribute : RequiredAttribute
{
public override bool IsValid(object value)
{
return (value != null) && (value is bool) ? (bool)value : false;
}
}
View
<%# Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master"
Inherits="System.Web.Mvc.ViewPage<RegistrationModel>" %>
<% Html.EnableClientValidation(); %>
<% using (Html.BeginForm("Register", "Registration", new { area="Account", id = "openid_form", inRegistration = true }))
<%=Html.ValidationSummary(false) %>
blah blah blah
<div class="checkbox"><label><%= Html.CheckBoxFor(model => model.TermsAndConditions) %>I agree to the terms and conditions of use.</label></div>
<input type="submit" id="submit" name="submit" value="Join Now" />
<%
Html.ValidateFor(m => m.TermsAndConditions);
%>
<% } %>
I am trying to call Html.ValidateFor at the end to push up all error message at top of the page. However, the property "TermsAndConditions" is not getting validated on client side (works great on server side). This leads me to look at the the window.mvcClientValidationMetData method at that mvc push out and I saw the following:
{"FieldName":"TermsAndConditions","ReplaceValidationMessageContents":false,"ValidationMessageId":null,"ValidationRules":[]}
Which you can see that "ValidationRules" are empty meaning that it is trying to validate it but the error message wasn't push out to the client for some reason.
Any ideas? Any help is appreciated.
Seems like I need to do more digging first. Was hoping the new attribute will appear magically on the client side. Instead, have to write some customer javascript to wire it up. See phil hack's post for detail.
This article from Phil Haack, ASP.NET MVC 2 Custom Validation, should help point you in the right direction.
Basically you need to create your own DataAnnotationsModelValidator<RequiredToBeTrueAttribute> and then write some client side script to get it done.
HTHs,
Charles

Wondering why DisplayName attribute is ignored in LabelFor on an overridden property

today I got confused when doing a couple of <%=Html.LabelFor(m=>m.MyProperty)%> in ASP.NET MVC 2 and using the [DisplayName("Show this instead of MyProperty")] attribute from System.ComponentModel.
As it turned out, when I put the attribute on an overridden property, LabelFor didn't seem to notice it.
However, the [Required] attribute works fine on the overridden property, and the generated errormessage actually uses the DisplayNameAttribute.
This is some trivial examplecode, the more realistic scenario is that I have a databasemodel separate from the viewmodel, but for convenience, I'd like to inherit from the databasemodel, add View-only properties and decorating the viewmodel with the attributes for the UI.
public class POCOWithoutDataAnnotations
{
public virtual string PleaseOverrideMe { get; set; }
}
public class EditModel : POCOWithoutDataAnnotations
{
[Required]
[DisplayName("This should be as label for please override me!")]
public override string PleaseOverrideMe
{
get { return base.PleaseOverrideMe; }
set { base.PleaseOverrideMe = value; }
}
[Required]
[DisplayName("This property exists only in EditModel")]
public string NonOverriddenProp { get; set; }
}
The strongly typed ViewPage<EditModel> contains:
<div class="editor-label">
<%= Html.LabelFor(model => model.PleaseOverrideMe) %>
</div>
<div class="editor-field">
<%= Html.TextBoxFor(model => model.PleaseOverrideMe) %>
<%= Html.ValidationMessageFor(model => model.PleaseOverrideMe) %>
</div>
<div class="editor-label">
<%= Html.LabelFor(model => model.NonOverriddenProp) %>
</div>
<div class="editor-field">
<%= Html.TextBoxFor(model => model.NonOverriddenProp) %>
<%= Html.ValidationMessageFor(model => model.NonOverriddenProp) %>
</div>
The labels are then displayed as "PleaseOverrideMe" (not using the DisplayNameAttribute) and "This property exists only in EditModel" (using the DisplayNameAttribute) when viewing the page.
If I post with empty values, triggering the validation with this ActionMethod:
[HttpPost]
public ActionResult Edit(EditModel model)
{
if (!ModelState.IsValid)
return View(model);
return View("Thanks");
}
the <%= Html.ValidationMessageFor(model => model.PleaseOverrideMe) %> actually uses [DisplayName("This should be as label for please override me!")] attribute, and produces the default errortext "The This should be as label for please override me! field is required."
Would some friendly soul shed some light on this?
Model binding and metadata using the strongly-typed helpers looks at the declared, rather than the runtime, type of the model. I consider this a bug, but apparently the MVC team disagrees with me, as my Connect issue on this was closed as "By Design."
I ran into this problem using [DisplayName("Profile Name")] and instead used [Display(Name = "Profile Name")] which fixed the problem in my case. I'm not sure if this would be useful.
The former is from System.ComponentModel whilst the latter is from System.ComponentModel.DataAnnotations.
I had the same issue when I had a partial view strongly-typed to an interface. The interface defined a DisplayName and the class that implemented the interface tried to override it. The only way I found to get it to respect the override was to type to the implementing class. I had to either change the view's model type or cast. Unfortunately, that completely negates the benefits of using the interface as the model type. I am guessing that I will end up with some level of duplicated view markup for each implementing class while not casting within the strongly-typed "helpers".
In the remote chance that this type of workaround is even remotely helpful (not getting my hopes up), here is an example. There are certainly ways of working handling into this for all possible implementing classes that try to override a name, but it is definitely more hassle than it should be.
public interface IAddressModel {
...
[DisplayName("Province")]
public string Province { get; set; }
...
}
public class UsAddressModel : IAddressModel {
...
[DisplayName("State")]
public string Province { get; set; }
...
}
<%= Html.LabelFor(m => m.State) %> <!--"Province"-->
<%= Html.LabelFor(m => (m as UsAddressModel).State) %> <!--"State"-->
Ok, I seem to have found a workaround if you don't use the Required tag with it! just use a regular expression or length attribute to determine if there is a valid entry. Hope this helps, though it's a little late.
[RegularExpression(#"^[1-9][0-9][0-9]$")] //validates that there is at least 1 in the quantity and no more than 999
[DisplayName("Quantity:")]
public string quantity { get; set; }
Still works.
In my case I was forgotten to make it a property by using getters and setters.
Instead of
public string CompanyName;
I should have used
public string CompanyName {get;set;}

Resources