Reputation: 75073
imagine this situation:
in the default MVC3 project, create a new complex type in the AccountModels.cs
public class GlobalAccount
{
public GlobalAccount()
{
this.LogOn = new LogOnModel();
this.Register = new RegisterModel();
}
public LogOnModel LogOn { get; set; }
public RegisterModel Register { get; set; }
}
In the RegisterModel
change the UserName
to:
[Required]
[Remote("UserNameExists", "Validation", "", ErrorMessage = "Username is already taken.")]
[RegularExpression(@"(\S)+", ErrorMessage = "White space is not allowed.")]
[Display(Name = "Username (spaces will be stripped, must be at least 6 characters long)")]
public string UserName { get; set; }
The UserNameExists
method in a Validation
controller is as follow:
public class ValidationController : Controller
{
public JsonResult UserNameExists(string UserName)
{
string user = null;
if (!String.IsNullOrWhiteSpace(UserName) && UserName.Length >= 6)
user = UserName == "abcdef" ? "ok" : null;
return user == null ?
Json(true, JsonRequestBehavior.AllowGet) :
Json(string.Format("{0} is not available.", UserName), JsonRequestBehavior.AllowGet);
}
}
Now in the Register View, use the GlobalAccount
Model instead of the RegisterModel
the username input box will be like:
@model Your.NameSpace.Models.GlobalAccount
and
<div class="field fade-label">
@Html.LabelFor(model => model.Register.UserName, new { @class = "text" })
@Html.TextBoxFor(model => model.Register.UserName, new { spellcheck = "false", size = "30" })
</div>
this will result in something like this, in the HTML
<div class="field fade-label">
<label class="text" for="Register_UserName"><span>Username (spaces will be stripped, must be at least 6 characters long)</span></label>
<input data-val="true" data-val-regex="White space is not allowed." data-val-regex-pattern="(\S)+" data-val-remote="Username is already taken." data-val-remote-additionalfields="*.UserName" data-val-remote-url="/beta/Validation/UserNameExists" data-val-required="The Username (spaces will be stripped, must be at least 6 characters long) field is required." id="Register_UserName" name="Register.UserName" size="30" spellcheck="false" type="text" value="">
</div>
If you use FireBug to check what's going on ... the Remote Validation is sending the attribute name instead of the attribute id to the Validation method (the UserNameExists
one) as:
Register.UserName
instead of Register_UserName
So I can't fetch this value ... ever :(
Is this really a bug or is something that someone already found and I couldn't get from Googling it?
Here is a simple image of the actual problem:
Upvotes: 3
Views: 2803
Reputation: 573
This is the simpliest way I found to do it, just adding data-val-- attributes in HtmlAttributes of DropDownListFor, inside the view. The following method works with RemoteValidation too, if you do not need remote validation, simply remove the elements containing data-val-remote-*:
@Html.DropDownListFor(m => m.yourlistID, (IEnumerable<SelectListItem>)ViewBag.YourListID, String.Empty,
new Dictionary<string, object>() { { "data-val", "true" },
{ "data-val-remote-url", "/Validation/yourremoteval" },
{ "data-val-remote-type", "POST" }, { "data-val-remote-additionalfield", "youradditionalfieldtovalidate" } })
I hope it may help. Best Regards!
Upvotes: 0
Reputation: 4655
I know this is marked as answered, but as I'm having the same issue I thought I would contribute another variation that is working for me.
The class in my case is "Food" and the field I'm attempting to remote validate is "Name". The textbox is being created by an EditorFor control:
@Html.EditorFor(model => model.Name)
Remote validation is set on the Food class field:
[Remote("FoodNameExists")]
public string Name { get; set; }
And this calls a method:
public ActionResult FoodNameExists(string Name) {
As per the original question, rather than this being passed to the FoodNameExists method as "Name", or even "Food_Name", which is the Id value created by the EditorFor helper, it is getting passed as the name attribute which is "Food.Name"... which of course is not something I can set as an input parameter.
So, my hack is simply to ignore the input parameters and look in the QueryString:
var name = Request.QueryString["Food.Name"];
...this returns the correct value, which I validate against and I'm off to the races.
Upvotes: 2
Reputation: 1038750
How about:
public ActionResult UserNameExists(
[Bind(Include = "UserName")]RegisterModel register
)
{
string user = null;
if (!String.IsNullOrWhiteSpace(register.UserName) && register.UserName.Length >= 6)
user = register.UserName == "abcdef" ? "ok" : null;
return user == null ?
Json(true, JsonRequestBehavior.AllowGet) :
Json(string.Format("{0} is not available.", register.UserName), JsonRequestBehavior.AllowGet);
}
Another possibility is to define a special view model:
public class UserNameExistsViewModel
{
public string UserName { get; set; }
}
and then:
public ActionResult UserNameExists(UserNameExistsViewModel register)
{
string user = null;
if (!String.IsNullOrWhiteSpace(register.UserName) && register.UserName.Length >= 6)
user = register.UserName == "abcdef" ? "ok" : null;
return user == null ?
Json(true, JsonRequestBehavior.AllowGet) :
Json(string.Format("{0} is not available.", register.UserName), JsonRequestBehavior.AllowGet);
}
What is annoying is that the following doesn't work:
public ActionResult UserNameExists(
[Bind(Prefix = "Register")]string UserName
)
Go figure :-) I would probably go with a custom view model. It looks cleanest.
Upvotes: 9