I've merged the create account view and the log in view in the same view. So it's a view with two forms, but they get mixed when I submit. If I try to log in and there's an error that is displayed with:
Html.ValidationSummary()
both forms get the error. And I started to rename fields to loginPassword, createPassword, because otherwise when I submit and the password is missing, it's marked as missing on both side.
What would be the way to separate these two forms so they can work indenpendently on the same view/page?
I had to deal with the same problem. I found that there is no way to separate the validation messages using the built in ValidationSummary(). Here are two suggestions:
- Position the validation summary in an area where it could apply to both forms. For example, if the login and sign up forms are side by side, position the validation summary in a div centered above both forms. I found an example of this style on the Mahalo login page.
- In the appropriate controller action methods, add something to the ViewData indicating which action was called. In the view there will be a ValidationSummary for each form, but each will be conditionally rendered based on what you added to the ViewData.
Either way the form fields should be uniquely named.
I went with solution #1 because I was satisfied with the way I was able to get it to look. But if you need the validation summary to appear in two different locations depending on which form was submitted, go with #2.
Ah yes, I've had to do exactly this before. The way I found was to set a flag in the ViewData detailing which form was posted and then I created my own extension method for ValidationSummary.
The code isn't with me right now so I'll try my best to do some air code for it now, it's obviously just an concept of how to do it so take it at face value.
To start with I would use the same setup as tvanfosson suggested with his 'EntryPageModel'.
View - note Html.MyValidationSummary
<% using(Html.BeginForm("NewAccount", "Account")) %>
<% { %>
<%= Html.MyValidationSummary("NewAccountForm") %>
<%= Html.TextBox("NewAccount.FirstName") %>
<%= Html.TextBox("NewAccount.LastName") %>
<%= Html.TextBox("NewAccount.Email") %>
<%= Html.Password("NewAccount.Password") %>
<%= Html.Password("NewAccount.ConfirmPassword") %>
<% } %>
<% using(Html.BeginForm("Login", "Account")) %>
<% { %>
<%= Html.MyValidationSummary("LoginForm") %>
<%= Html.TextBox("Login.Email") %>
<%= Html.Password("Login.Password") %>
<% } %>
Controller - note ViewData["PostedForm"]
public class Account : Controller
{
private EntryPageModel _viewModel;
public ActionResult NewAccount(FormCollection formValues)
{
try
{
//binding and validation for _viewModel.NewAccount
}
catch
{
ViewData["PostedForm"] = "NewAccountForm";
return View("RegisterAndLogin", _viewModel);
}
}
public ActionResult Login(FormCollection formValues)
{
try
{
//binding and validation for _viewModel.Login
}
catch
{
ViewData["PostedForm"] = "LoginForm";
return View("RegisterAndLogin", _viewModel); //You'll want to pass in a model
}
}
}
Custom html extension
namespace System.Web.Mvc
{
public static class HtmlExtensions
{
public static string MyValidationSummary(this HtmlHelper html, string formName)
{
if (!string.IsNullOrEmpty(html.ViewData["PostedForm"])
&& (html.ViewData["PostedForm"] == formName))
{
return html.ValidationSummary();
}
return "";
}
}
}
HTHs,
Charles
The input elements do need different names/ids even if they are in different forms. Unless they have different names, it will trigger the validation logic for each control since it matches based on the name of the control. I think you are on the right track by changing the names to differentiate them.
I'd set it up with a compound model, perhaps so that you could do something like (note this is incomplete):
<%= Html.TextBox( "Login.Name" ) %>
<%= Html.TextBox( "Login.Password" ) %>
<%= Html.TextBox( "NewAccount.Name" ) %>
<%= Html.TextBox( "NewAccount.Password" ) %>
<%= Html.TextBox( "NewAccount.ConfirmPassword" ) %>
On the server side, use the prefix option for the binder
public ActionResult Login( [Bind(Prefix="Login")]AccountModel model )
{
...
}
And your model would look like:
public class AccountModel
{
public string Name { get; set; }
public string Password { get; set; }
public string ConfirmPassword { get; set; }
}
public class EntryPageModel
{
public AccountModel Login { get; set; }
public AccountModel NewAccount { get; set; }
}
If the forms are posting to completely different actions, then your ModelStateDictionary should only contain the errors that were provided by the action that was invoked.
Can you post the relevant code?
I'm not sure if there is a way to split the ValidationSummary().
For your forms, you could create model classes that you would bind against, with the various fields. It wouldn't gain you much over what you've already got, though.