7

I'm using MVC and Razor for the first time and have what is hopefully a really easy question. I have two pages on the site. The first page (Page A) has a very small form on it which has an input for an email address. when the user enters their email address and clicks submit they are sent to page B using HttpPost, like so:

@using (Html.BeginForm("NewsletterSignup","Common", FormMethod.Post))
{
    <p>
        <input type="text" class="text" value="Enter your email address" id="email" name="email" />
        <input type="submit" class="submit" value="Sign Up" />
    </p>
}

On Page B there is the main form which allows the user to also enter their name and mobile number. The controller for the main form looks like so:

//newsletter signup page
public ActionResult NewsletterSignup()
{
    var model = new NewsletterSignupModel();

    return View(model);
}

[HttpPost, ActionName("NewsletterSignup")]
public ActionResult NewsletterSignupSend(NewsletterSignupModel model)
{
    if (ModelState.IsValid)
    {
        //register the user here
    }

    return View(model);
}

On the main form I have a Validationsummary and validation for each of the fields. The problem is that my contoller states that the NewsletterSignupSend action can only be executed using HttpPost. Because the form on Page A uses HttpPost when the user arrives on Page B the validation has already been run - i.e. before the user has submitted the form on Page b.

I know I'm missing a basic here - can someone steer me in the right direction?

Thanks in advance Al

UPDATE: To resolve this question I have done the following.

Form A is rendered using:

@Html.Action("MiniNewsletterSignup")

Form A has a controller method:

//mini newsletter view
        public ActionResult MiniNewsletterSignup()
        {
            var model = new MiniNewsletterSignupModel();

            return View(model);
        }

And the content of the view is:

@model Nop.Web.Models.Common.MiniNewsletterSignupModel
@using (Html.BeginForm("NewsletterSignup", "Common"))
{
    <p>
        <input type="text" class="text" value="Enter your email address" id="email" name="email" />
        <input type="submit" class="submit" value="Sign Up" />
    </p>
}

This submits the form using HttpPost off to Page B.

Page B has 2 controller methods:

//newsletter signup page
public ActionResult NewsletterSignup()
{
    var model = new NewsletterSignupModel();

    if (Request["email"] != null)
        model.Email = Request["email"];

    return View(model);
}

And:

[HttpPost, **WhenRequestContainsKey("FullName")**]
public ActionResult NewsletterSignup(NewsletterSignupModel model)
{
    if (ModelState.IsValid)
    {
        //process here
    }

    return View(model);
}

You will notice I have added the Selector WhenRequestContainsKey which I found at http://softwaredevelopmentsolutions.blogspot.co.uk/2011/06/aspnet-mvc-3-partial-form-validation-on.html. This means this code is only called when there is a field in the Request called FullName, which on our site only exists on Page B.

This seems to work as I wanted it, but Im not sure why - why for example does this stop the validation taking place until the form is posted back on Page B - there is nothing in the method that calls a validate method???

Is there anything wrong with the way Ive implemented this?

Thanks Al

higgsy
  • 1,991
  • 8
  • 30
  • 47

2 Answers2

9

I think you are missing a post action - if you try to follow the pattern GET-POST-Redirect you should avoid these issues (i.e. every post should redirect to a GET action):

FirstAction()
{
    return View();
}

[HttpPost]
FirstAction()
{
    //Save email address
    return Redirect("NewsletterSignup","Common");
}

And your first form becomes:

@using (Html.BeginForm())
{
    <p>
        <input type="text" class="text" value="Enter your email address" id="email" name="email" />
        <input type="submit" class="submit" value="Sign Up" />
    </p>
}
Paddy
  • 33,309
  • 15
  • 79
  • 114
  • Hi Paddy - thanks for the response. The problem with your suggestion is that the first form passes across the email address entered into the text field - this will be lost if using a Redirect. – higgsy Mar 27 '12 at 11:10
  • You could pop the email address into TempData and access it on the next GET: http://msdn.microsoft.com/en-us/library/dd394711.aspx (search for TempData). – Paddy Mar 27 '12 at 11:57
  • Hi Paddy - I tried what you suggested. However the FirstAction now produces the error message "Child actions are not allowed to perform redirect actions.". I am invoking the first form using @Html.Action("MiniNewsletterSignup") – higgsy Mar 28 '12 at 11:08
  • OK, it would seem that you can't redirect from within a RenderAction: http://stackoverflow.com/questions/2056421/why-are-redirect-results-not-allowed-in-child-actions-in-asp-net-mvc-2 – Paddy Mar 28 '12 at 11:11
  • Ok - so back to my original question. I'm only looking to submit the contents of 1 form to another, and then in the second form validate the input only when the submit button has been clicked - something we've been doing in Classic ASP and .NET for years - surely this is something developers need to do all the time. Any thoughts??? – higgsy Mar 28 '12 at 11:17
  • Could you update your question with a bit more detail please - in particular how you are rendering this child action on your main view, and how you are submitting it? – Paddy Mar 28 '12 at 11:24
2

Put a [HttpGet] on the first one:

[HttpGet]
public ActionResult NewsletterSignup()
{  
    var model = new NewsletterSignupModel();
    return View(model);
}
Aliostad
  • 80,612
  • 21
  • 160
  • 208