views:

600

answers:

5

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?

A: 

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?

Ben Scheirman
But Html.ValidationSummary() picks all the errors, for any form.
J. Pablo Fernández
Regarding code, you can just look at the log in and create account actions of a freshly created project. Now I have a logInOrCreateAccount() method that shows a view with two forms, one pointing to logIn() an the other to createAccount().
J. Pablo Fernández
Oh I see. Yeah the validation summary is not form-specific.You could put this in a central location at the top of the page or just omit it completely.It wouldn't be hard to write your own that takes a prefix either.
Ben Scheirman
A: 

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.

GalacticCowboy
I'm ok with the name change, it's clean but it's all right. It's the ValidationSummary that I can't work around now.
J. Pablo Fernández
+1  A: 

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; }
}
tvanfosson
Oh! Right. I'll leave the ids and names different, still, ValidationSummary catches all in both forms.
J. Pablo Fernández
I've updated with some ideas on how to do it.
tvanfosson
...and I think you only need one validation summary per page. I think it is intended to be a summary of all the errors in the model state dictionary.
tvanfosson
Thanks for the code. I need to show the validation errors for the log in action in the log in form and in create account action in the create account form. Showing the errors twice or away from the form is not very friendly for the user.
J. Pablo Fernández
Oh, and I don't really have a model, it's just a form and arguments in the action methods. That's the way the template generates it. Should I create models?
J. Pablo Fernández
+2  A: 

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:

  1. 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.
  2. 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.

solidbeats
+2  A: 

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

Charlino
That's what should have been forged in the ValidationSummary() ... if there were just a method to detect in which View, thus calling action, that ValidationSummary() was called then it could work without explicit action argument !
jalchr