-2

Wonder if someone could help me out here......

Basically what I'm trying to do if on the posting of a form, I'm doing some custom validation and if the data being submitted doesn't conform to the business then I'd like to stop processing (obviously!) and pass the incoming model back to the view that contains the form so that the user doesn't have to re-key all of their data.

The way that I'm set this up is (This is using Umbraco as a CMS):

1) I've created the model/properties. 2) I've then created a strongly-typed partial view and selected the model class that I'd just created (named AwardsCeremony) 3) I've then created a controller with the following Index ActionResult:

    public ActionResult Index()
    {
        TempData["priceList"] = getPrices();
        TempData["qualificationList"] = getQualifications();

        return PartialView("AwardsCeremony", new AwardsCeremonyViewModel());
    }

4) Then in the Umbraco template I'm calling the controller

@Html.Action("Index", "AwardsCeremonySurface")

(this umbraco inherits it's styling, etc from the Master template (in Umbraco)).

In the view (on the HTTPPOST event) I'm calling the processBooking actionresult on the controller.

@using (Html.BeginUmbracoForm("processBooking", "AwardsCeremonySurface", null, new { @class = "form-horizontal" }))

This ActionResult then does the error checking and that's where the problems start (!), I've removed my custom validation for ease of reading.

    [HttpPost]
    public ActionResult processBooking(AwardsCeremonyViewModel model)
    {
        //Do the custom error handling

        if (myInvalidForm == true)
        {
            return PartialView("AwardsCeremony",model);
        }

        //Process the booking

        return RedirectToCurrentUmbracoPage();
    }

What's happening is that when the partial view is returned back the styling of the page is lost, the form displayed but with none of the inherited styling, classes, etc from the Umbraco Master template and I just don't know how to fix. Could someone point me in the right direction please?

Thanks, Craig

tereško
  • 58,060
  • 25
  • 98
  • 150
SxChoc
  • 619
  • 3
  • 10
  • 26

2 Answers2

1

Implementing a Form in Umbraco (based on code in one of my projects - your form processing in HandleContactUs will most likely differ of course):

The Model is just standard MVC.

The controller:

public class FormsController : SurfaceController
{
    [ChildActionOnly]
    public ActionResult ContactUs()
    {
        HtmlHelper.UnobtrusiveJavaScriptEnabled = true;
        HtmlHelper.ClientValidationEnabled = true;

        ContactForm model = null;
        if (TempData.ContainsKey("ContactForm"))
        {
            model = TempData["ContactForm"] as ContactForm;
        }
        if (model == null)
        {
            model = new ContactForm { Page = CurrentPage };
        }
        return PartialView(model);
    }

    [HttpPost]
    public ActionResult HandleContactUs(ContactForm model)
    {
        if (ModelState.IsValid)
        {
            string errorMsg = string.Empty;

            TempData["ContactFormSuccess"] = model.SendMail(Umbraco, out errorMsg);
            TempData["ContactFormErrorMessage"] = errorMsg;
        }
        else
        {
            TempData["ContactFormSuccess"] = false;
        }
        TempData["ContactForm"] = model;

        if (!Request.IsAjaxRequest())
            RedirectToCurrentUmbracoUrl();

        return PartialView(model);
    }
}

Notes:

  1. The form is rendered by the ContactUs Action while it's handled by the HandleContactUs Post Action.
  2. The handler action checks for ajax post - if it's not posted back then we do a RedirectToUmbracoUrl() otherwise we just return the partial view as we're using javascript to replace the form with the handled view (usually a thank you message etc.)
  3. You actually don't need to include the keyword Surface in your controller name - while the docs still indicate that you do, it's been removed as a requirement some time ago.

The view:

~\Views\Forms\ContactUs.cshtml:

Using standard UmbracoBeginForm:

@using (Html.BeginUmbracoForm<FormsController>("HandleContactUs", null, 
        new { @class = "form-horizontal" })) 
{

}

Using MS MVC Ajax.BeginForm:

@using (Ajax.BeginForm("HandleContactUs", "Forms", null,  
        new AjaxOptions { UpdateTargetId = "contactForm", 
                          OnBegin = "contactBegin", 
                          OnFailure = "contactFailure" }, 
        new { @class = "form-horizontal" }) ) 
{

}

Of course, you'll also need the javascript callback functions contactBegin, contactFailure or whatever your AjaxOptions configuration is.

I've used this approach on several projects without any problems.

Robert Foster
  • 2,317
  • 18
  • 28
  • Cheers Robert, sorry bud I've only just picked this up. I'll give this a whirl over the weekend and report back – SxChoc Oct 15 '15 at 20:13
-1

Of course the styling is lost. You're literally returning a partial view as the response, which is not going to include your site layout. You want to return a full ViewResult, i.e. return View(model), and the view should be the same view you used for your GET action, not the partial.

Chris Pratt
  • 232,153
  • 36
  • 385
  • 444
  • Cheers bud, I understand why the styling is being lost I just don't know what to do about it (!) if I user return View(model) then I get the error of The view 'processBooking' or its master was not found or no view engine supports the searched locations. The following locations were searched: – SxChoc Oct 12 '15 at 14:24
  • The view should be same as the one you used for your GET request. If you're relying on conventions-based view loading in your GET action and your POST action is not named the same, then you'll need to pass the view name explicitly: `return View("GetView", model);` – Chris Pratt Oct 12 '15 at 14:52
  • Not sure that I'm understanding to be honest mate, if you're suggesting that the syntax I should be using is return View("AwardsCeremony",model); then I'm getting the following error The model item passed into the dictionary is of type 'ICASolution.Models.AwardsCeremonyViewModel', but this dictionary requires a model item of type 'Umbraco.Web.Models.RenderModel'. – SxChoc Oct 12 '15 at 15:08
  • Okay, well, then Umbraco requires a slightly different return object, but it should still let you specify a specific view. Consult their documentation. The main point is that you need to return a full view result, not a partial. – Chris Pratt Oct 12 '15 at 15:50
  • He's not returning a partial view as the response, he's redirecting to the Current Umbraco Page correctly. Something else is going on here. I take that back - I just saw the conditional return. – Robert Foster Oct 12 '15 at 22:27
  • However you don't want to return a full ViewResult either - you need to return `RedirectToCurrentUmbracoPage()` or one of its friends. – Robert Foster Oct 12 '15 at 22:29
  • If I use RedirectToCurrentUmbracoPage() then I don't think that I can pass the model to it. So the form isn't populated....... – SxChoc Oct 13 '15 at 08:14
  • No, if a redirect is involved, then data can only be passed via the query string, which is inappropriate for something as complex as a full object instance. However, you shouldn't be redirecting in this scenario: just returning the previous view. I'm not familiar with Umbraco, but surely it has something that merely returns a view with some model data passed into it. Likely, the method you need was used in your GET action, so see what that's doing. – Chris Pratt Oct 13 '15 at 13:05
  • You can still pass the model around using `RedirectToCurrentUmbracoPage()` - set a TempData value first. – Robert Foster Oct 14 '15 at 00:43
  • @RobertFoster: In some situations that might be valid, but here, it would just be a hack. The OP has a simple PRG (Post-Redirect-Get) workflow here and is trying to redirect instead of returning the view back on error, and losing the model data. The solution is to simply return the view again, not redirect, and certainly not involve TempData. – Chris Pratt Oct 14 '15 at 13:01
  • This is an Umbraco environment - there are ways to make it behave more like Vanilla MVC, but passing back a full View doesn't work unless you perform Route Hijacking (which has it's own complications). If the page template is to be retained, the only way to do it is by returning `RedirectToUmbracoPage()`. TempData is the suggested way of retaining the data in this case if the data is to be kept. – Robert Foster Oct 14 '15 at 14:16