I can't figure out how to do this very simple thing: My page contains a set of textboxes that a user can fill out to add an item to a list. Then the item shows up in a dropdown list.
At that point, I want the "add" textboxes to be cleared. This is the behavior expected by most users, I think. The item has been added; now the textboxes should be empty, ready for the next item to be entered.
However, I can't seem to clear them when I am using Html helpers, e.g., Html.Textbox(...). I like these controls because of the way they "remember" the input in case of input error. However, unlike webforms controls, you can't set them programmatically. They continue to retain the values until the user enters something else.
Is there any way around this behavior? I thought of clearing them in javascript, but I don't want to do that if there are any errors.
UPDATE some of the code;
One of my textboxes in the view:
<h6 style="margin-top: 0px">Add custom email template:</h6>
<div style="margin-top: 10px">
<div class="label">Name:</div>
<%= Html.TextBox("addName", "", new { #class="formtext", style="width: 400px" }) %>
<div class="alerttext"><%= Html.ValidationMessage("addName") %></div>
</div>
The class I am using for model binding:
public class ManageEmailTemplatesSubmittedData
{
[RegularExpression(RegExpressions.templateNameRestrict, ErrorMessage="Names should begin with a character and consist of only characters and numbers")]
public string addName { get; set; }
[RegularExpression(RegExpressions.freeTextRestrict, ErrorMessage = "Invalid entry; please omit unusual characters")]
public string addDescription { get; set; }
[RegularExpression(RegExpressions.freeTextRestrict, ErrorMessage = "Invalid entry; please omit unusual characters")]
public string addSubject { get; set; }
[RegularExpression(RegExpressions.freeTextRestrict, ErrorMessage = "Invalid entry; please omit unusual characters")]
public string addTemplate { get; set; }
public string templates { get; set; }
[RegularExpression(RegExpressions.templateNameRestrict, ErrorMessage = "Names should begin with a character and consist of only characters and numbers")]
public string editName { get; set; }
[RegularExpression(RegExpressions.freeTextRestrict, ErrorMessage="Invalid entry; please omit unusual characters")]
public string editDescription { get; set; }
[RegularExpression(RegExpressions.freeTextRestrict, ErrorMessage = "Invalid entry; please omit unusual characters")]
public string editSubject { get; set; }
[RegularExpression(RegExpressions.freeTextRestrict, ErrorMessage = "Invalid entry; please omit unusual characters")]
public string editTemplate { get; set; }
}
My action:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult CustomEmails(SubmitButtons buttons, ManageEmailTemplatesSubmittedData data)
{
bool saved = false;
string selectedTemplate = data.templates;
if (ModelState.IsValid)
{
ButtonStyles buttonStyles = ButtonStylesCreator.GetSelectListButtonStyles(rc.persistedData.loggedInUser.userType);
Notification notification = new Notification(rc);
if (buttons.addTemplate == buttonStyles.addEmailTemplateButtonValue)
{
// add an email template
notification.SaveCustomTemplate(data.addName, data.addName, data.addTemplate, data.addSubject, data.addDescription);
saved = true;
}
else if (buttons.saveTemplate == buttonStyles.saveTemplateValue)
{
// update an email template
notification.SaveCustomTemplate(data.templates, data.editName, data.editTemplate, data.editSubject, data.editDescription);
selectedTemplate = "";
saved = true;
}
}
ConfigureEmailsModelBuilder builder = new ConfigureEmailsModelBuilder(rc, rc.persistedData.loggedInUser.userID, selectedTemplate, true, saved);
return View(builder.Build());
}
ConfigureEmailsModelBuilder constructs the view model, which includes a SelectList that is the dropdown list of the items that have been added. (The view is strongly typed to the type generated by builder.Build).
The HTMLHelper's first look at the ModelState and ViewData to see if any values match their key and then finally use whatever value you provide them.
If you need to reset the textboxe's value you also need to clear the ModelState entry with the matching key. Another alternative is redirecting to the same page instead of simply rendering a view via javascript or with MVC.
This is working for me on an MVC3 site log on page.
ModelState.Clear();
model.UserName = string.Empty;
model.Password = string.Empty;
ModelState.AddModelError("", "The user name or password provided is incorrect.");
This will clear the login textboxes used for password and username, and keep any model errors.
Related
I am displaying radio buttons using enum class.
public enum RegisteredBy
{
[Display(Name = "Customer", Order = 0)]
H,
[Display(Name = "Dealer/Contractor", Order = 1)]
S,
}
When i am rendering this on my view and on submit I am not selected any radio button. Even though it is taking "H" as default value. So that it is not showing any validation message.
#using ConsumerProductRegistration.Models;
#using ProductRegistration.Models.Enums;
#model ProductRegistration.Models.Registration
#Html.RadioButtonFor(m => m.RegisteredBy, RegisteredBy.H, new { id = "RegisteredByCustomer" })
#Html.Label("Customer")<br />
#Html.RadioButtonFor(m => m.RegisteredBy, RegisteredBy.S, new { id = "RegisteredByDealer" })
#Html.Label("Dealer/Contractor")
#Html.ValidationMessageFor(m => m.RegisteredBy)
In Model:
public class Registration
{
[Required(ErrorMessage = "Select at least one option")]
[Display(Name = "Registered by*")]
public RegisteredBy RegisteredBy { get; set; }
}
In view:
public ActionResult CustomerInfo(Registration registration)
{
return View(registration);
}
please suggest me.If user does not select we should show the error message.
The default underlying type of the enumeration elements is int. By default, the first enumerator has the value 0, and the value of each successive enumerator is increased by 1.
When you are not selecting anything and posting the form, the default value 0 is automatically getting set (default value of integer).
In this case, you can make your property nullable with [Required] attribute which sends null as value when nothing is selected. And as it is decorated with [Required] attribute, it will give you required field validation error.
[Required]
public RegisteredBy? RegisteredBy { get; set; }
I have a class user which looks like this:
public class User
{
public int UserId { get; set; }
[Required(ErrorMessage = "A username is required.")]
[StringLength(20, ErrorMessage = "Your username must be 4-20 characters.", MinimumLength = 4)]
[RegularExpression("^[a-zA-Z0-9]*$", ErrorMessage = "Your username can only consist of letters and numbers.")]
[Remote("UsernameExists", "RemoteValidation", ErrorMessage = "Username is already taken")]
public string Username { get; set; }
[Required(ErrorMessage = "A password is required.")]
[MinLength(4, ErrorMessage = "Your password must have at least 4 letters.")]
public string Password { get; set; }
[Required(ErrorMessage = "An email address is required.")]
public string Email { get; set; }
}
For the Register functionality I have created a ViewModel that holds a User object and a string for the password confirmation:
public class RegistrationViewModel
{
public User User { get; set; }
[DisplayName("Password confirmation")]
[Required, Compare("User.Password", ErrorMessage = "The password do not match")]
public string PasswordConfirmation { get; set; }
}
The first problem I run into is that I can't seem to get the validation for Compare("User.Password") to work as it does not seem to find the property on the user. Is there any way to validate the PasswordConfirmation property against the User.Password property?
The second problem is the Remote validation of the Username field. I followed David Hayden's tutorial at http://davidhayden.com/blog/dave/archive/2011/01/04/ASPNETMVC3RemoteValidationTutorial.aspx but the parameter username in the UsernameExists method is always null. Am I missing something here?
Edit:
I'm sorry but I was actually not clear enough on the error I receive for the password comparison. It works fine when filling in the fields, if the passwords do not match I will receive an error. However, when submitting the form I get the following error in the validation summary: Could not find a property named UserToRegister.Password.
Edit 2:
I have figured out part of the problem thanks to Joe's post. The remote validator posts back URL/?UserToRegister.Username=temp which obviously does not match the username parameter of my controller action. In order to map my action parameter to UserToRegister.Username the following is required:
public ActionResult UsernameExists([Bind(Prefix = "UserToRegister.Username")]string username)
This now correctly passes the parameter to the method. However I still get the error when using the Compare attribute on the password field.
Thanks.
The issue with the validation of the PasswordConfigurmation property against the User.Password property is caused by a bug in in the 'jquery.validate.unobtrusive.js' file.
Originally, the jquery 'equalTo' function is:
adapters.add("equalto", ["other"], function (options) {
var prefix = getModelPrefix(options.element.name),
other = options.params.other,
fullOtherName = appendModelPrefix(other, prefix),
element = $(options.form).find(":input[name=" + fullOtherName + "]")[0];
setValidationValues(options, "equalTo", element);
});
You just need to modify this line:
element = $(options.form).find(":input[name=" + fullOtherName + "]")[0];
to:
element = $(options.form).find(":input[name='" + fullOtherName + "']")[0];
Note the addition on the single quotes around the 'fullOtherName' selector. Once you've made this change, the client side validation works as expected.
For the remote validation part nothing jumps out at me. It might be helpful to open up firebug and see what the request that is being fired looks like. You should see something roughly like this if everything is properly wired up...
http://localhost:14547/[Controller]/[ActionName]?[ParamName]=[paramValue]
From the request you provided, you can either use a prefix like you ended up doing or you can make your action take a user named UserToRegister and then within the action access the UserName property. This is the recommended way of dealing with remote validation of objects and might be a little easier to think about than using a Bind attribute.
For the compare validation, it appears that on the client side validation is succeeding but on the server side validation fails because the validation context does not contain a property named User.Password, only a property named User.
Inheritance seems like a standard way of adding functionality in this case. How about having your RegistrationViewModel derive from the UserViewModel:
public class RegistrationViewModel : UserViewModel
{
[DisplayName("Password confirmation")]
[Required]
[Compare("Password", ErrorMessage = "The password do not match")]
public string PasswordConfirmation { get; set; }
}
and:
public ActionResult UsernameExists(string Username)
{
...
}
EDIT: upgraded this question to MVC 2.0
With asp.net MVC 2.0 is there an existing method of creating Validation Summary that makes sense for models containing collections? If not I can create my own validation summary
Example Model:
public class GroupDetailsViewModel
{
public string GroupName { get; set; }
public int NumberOfPeople { get; set; }
public List<Person> People{ get; set; }
}
public class Person
{
[Required(ErrorMessage = "Please enter your Email Address")]
[RegularExpression(#"^([a-zA-Z0-9_\-\.]+)#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$", ErrorMessage = "Please enter a valid Email Address")]
public string EmailAddress { get; set; }
[Required(ErrorMessage = "Please enter your Phone Number")]
public string Phone { get; set; }
[Required(ErrorMessage = "Please enter your First Name")]
public string FirstName { get; set; }
[Required(ErrorMessage = "Please enter your Last Name")]
public string LastName { get; set; }
}
The existing summary <%=Html.ValidationSummary %> if nothing is entered looks like this.
The following error(s) must be corrected before proceeding to the next step
* Please enter your Email Address
* Please enter your Phone Number
* Please enter your First Name
* Please enter your Last Name
* Please enter your Email Address
* Please enter your Phone Number
* Please enter your First Name
* Please enter your Last Name
The design calls for headings to be inserted like this:
The following error(s) must be corrected before proceeding to the next step
Person 1
* Please enter your Email Address
* Please enter your Phone Number
* Please enter your First Name
* Please enter your Last Name
Person 2
* Please enter your Email Address
* Please enter your Phone Number
* Please enter your First Name
* Please enter your Last Name
Answer Based on Pharcyde's answer.
public static MvcHtmlString NestedValidationSummary(this HtmlHelper helper)
{
if (helper.ViewData.ModelState.IsValid)
return MvcHtmlString.Empty;
// create datastructure to group error messages under a given key (blank key is for general errors)
var errors = new Dictionary<string,List<string>>();
foreach (KeyValuePair<string, ModelState> keyPair in helper.ViewData.ModelState)
{
foreach (ModelError error in keyPair.Value.Errors)
{
//determine the 'key' for the group in which this error belongs
var key = keyPair.Key.Split(']')[0];
if (key.Contains("People["))
key = "Person " + key.Split('[')[1];
else
key = string.Empty;
if(!errors.ContainsKey(key))
errors.Add(key,new List<string>());
//now add message using error.ErrorMessage property
errors[key].Add(error.ErrorMessage);
}
}
// generate the HTML
var ul = new TagBuilder("ul");
foreach (KeyValuePair<string, List<string>> errorPair in errors.OrderBy(p=>p.Key))
{
var li = new TagBuilder("li");
if(!string.IsNullOrEmpty(errorPair.Key))
li.InnerHtml += string.Format("<p class=\"no-bottom-margin\"><strong>{0}</strong></p>",errorPair.Key);
var innerUl = new TagBuilder("ul");
foreach (var message in errorPair.Value)
{
var innerLi = new TagBuilder("li");
innerLi.InnerHtml = message;
innerUl.InnerHtml += innerLi.ToString(TagRenderMode.Normal);
}
li.InnerHtml += innerUl.ToString(TagRenderMode.Normal);
ul.InnerHtml += li.ToString(TagRenderMode.Normal);
}
return MvcHtmlString.Create(ul.ToString(TagRenderMode.Normal));
}
You are going to have to extend the HtmlHelper methods and roll your own. Heres the bit of code that is important for your situation where you need a group by:
//HtmlHelper being extended
if(helper.ViewData.ModelState.IsValid)
{
foreach(KeyValuePair<string,ModelState> keyPair in helper.ViewData.ModelState)
{
//add division for group by here using keyPair.Key property (would be named "Person" in your case).
foreach(ModelError error in keyPair.Value.Errors)
{
//now add message using error.ErrorMessage property
}
}
}
I've been looking at the file MicrosoftMvcJQueryValidation.js which is the layer between your page and the jquery.validate object in ASP.NET MVC 2 Beta.
It will allow any type of validation rule supported by jquery.validate and had additional special handling for regularexpressions, strings, ranges and required fields. If it is a generic/unknown rule type it will just pass through the parameters like this :
default:
__MVC_ApplyValidator_Unknown(rulesObj,
thisRule.ValidationType, thisRule.ValidationParameters);
break;
However - I cannot seem to figure out how to inject additional rules into the JSON that is generated by the framework, such as 'email'. Normally the rules just come from the attributes such as [Required].
I know there are lots of extensivbility points to replace the whole validation metadata provider - but I'm looking for a simple way.
How can I use - for instance the 'email' or 'creditcard' validators in conjunction with a simple model like this:
public class LoginDetails
{
public bool Editable { get; set; }
[Required(ErrorMessage="Please enter your email")]
public string Username { get; set; }
[Required(ErrorMessage="Please enter your password")]
public string Password { get; set; }
}
For different types of data you can keep the Required message but also add different attributes below them. For Email and Credit Car a Regular Expression like this would probably work best.
[Required(ErrorMessage="Please enter your email")]
[RegularExpression(#"\b[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,4}\b",
ErrorMessage="Please enter a valid email address.")]
public string Email { get; set; }
[Required(ErrorMessage="Please enter your password")]
[RegularExpression(#"^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\d{3})\d{11})$.",
ErrorMessage="Please enter a valid Credit Card.")]
public string CreditCard { get; set; }
Check out this, this and this link for more info.
Use a DataAnnotationsModelValidator
Like this.
Use these includes
/Scripts/jquery-1.3.2.js
/Scripts/jquery.validate.js
/Scripts/MicrosoftMvcJQueryValidation.js
Use a validation function
<script type="text/javascript">
$.validator.addMethod("price", function(value, element, paras) {
if (value.length == 0) {
return true;
}
if (value > paras.minValue) {
var cents = value - Math.floor(value);
if (cents >= 0.99 && cents < 0.995) {
return true;
}
}
return false;
});
</script>
There appears to be something of a hole in the way DataAnnotations works in that a user entering in some text into a field that will go into an int will never reach the DataAnnotations code. It kicks off a model binding error and displays the error to the user "The value 'a' is not valid for the XXXX field."
Anyway, it's all very nice that it automatically handles this situation, but I actually want to display an error message indicating the problem eg. "The value 'a' is not numeric. Please enter in a numeric value for the XXXX field".
I have tried the solutions set out How to replace the default ModelState error message in Asp.net MVC 2? and ASP.NET MVC - Custom validation message for value types, but I can't get them to work.
It appears that my resource file is not being read at all, since here (http://msdn.microsoft.com/en-us/library/system.web.mvc.defaultmodelbinder.resourceclasskey.aspx) it states "If the property is set to an invalid class key (such as a resource file that does not exist), MVC throws an exception." and even if I change the line to DefaultModelBinder.ResourceClassKey = "asdfasdhfk" there is no exception.
Anyone have any ideas?
EDIT: Here is some code. All of it is working minus my Messages.resx file's messages are not being used. The code for Messages.resx is auto generated so I won't include it.
So entering "a" into ProcessOrder results in a generic message rather than what I have entered into Messages.resx for PropertyValueInvalid (and InvalidPropertyValue for good measure).
Application_Start method
protected void Application_Start()
{
RegisterRoutes(RouteTable.Routes);
ModelBinders.Binders.DefaultBinder = new Microsoft.Web.Mvc.DataAnnotations.DataAnnotationsModelBinder(); //set dataanooations to be used
DefaultModelBinder.ResourceClassKey = "Messages"; //set data annotations to look in messages.resx for the default messages
ValidationExtensions.ResourceClassKey = "Messages";
}
Entity Class
[MetadataType(typeof(GLMetaData))]
public partial class GL
{
}
public class GLMetaData
{
public int TransRefId { get; set; }
[DisplayName("Process Order")]
public int? ProcessOrder { get; set; }
[DisplayName("Trans Type")]
[StringLength(50)]
public string TransType { get; set; }
[StringLength(100)]
public string Description { get; set; }
[DisplayName("GL Code")]
[StringLength(20)]
public string GLCode { get; set; }
[DisplayName("Agents Credit No")]
[StringLength(50)]
public string AgentsCreditNo { get; set; }
[Required]
public bool Active { get; set; }
}
Controller Action:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(GL glToBeUpdated)
{
try
{
if (!ModelState.IsValid)
return View(glToBeUpdated);
//set auto properties
glToBeUpdated.UpdateDate = DateTime.Now;
glToBeUpdated.UpdateUser = this.CurrentUser;
glDataLayer.update(glToBeUpdated);
glDataLayer.submitChanges();
return RedirectToAction("Index");
}
catch
{
glDataLayer.abortChanges();
throw;
}
}
What I did to combat a similar issue was to clear the model state, validate against ModelState["XXXX"].Value.AttemptedValue instead of against the nulled value caused by an trying to put an invalid value into the Model's property, populating the error messages and resetting the Model values.
That way I can have the error messages I want and if necessary offer more than one ("a value is required" or "the value must be numeric").
I have battled this for most of the day on MVC4 RC. No matter what i set
DefaultModelBinder.ResourceClassKey
to it never seemed to work. It also never threw an exception when I assigned junk.
This is what I was using to assign the value (to no avail):
DefaultModelBinder.ResourceClassKey = typeof(App_GlobalResources.ValidationMessages).Name;
In the end I decided to tackle this error message on the client side and override the data attribute that jQuery uses to display the message.
#Html.TextBoxFor(m => m.Amount, new Dictionary<string,object>(){{"data-val-number","Invalid Number"}})
this is working how I need it to.
Ironically this works too:
#Html.TextBoxFor(m => m.Amount, new Dictionary<string, object>() {{ "data-val-number", HttpContext.GetGlobalResourceObject("ValidationMessages", "PropertyValueInvalid") } })
Here I have taken Contact number field as string but with Range Attribute so can provide numeric validatioin to use if from your Resource file .
[Required(ErrorMessageResourceType = typeof(Global), ErrorMessageResourceName = "ContactNumberRequired")]
[Range(0, int.MaxValue, ErrorMessageResourceType = typeof(Global), ErrorMessageResourceName = "ValidContactNumber")]
[Display(Name = "Contact Number")]
public string ContactNumber { get; set; }
So now here provided ErrorMessageResourceName as key . You can customize and use it also in Multi Language