2

I have a custom Form Controller in a DXA 1.2 web application that is working fine at request time but when I post the form data back to that Controller and return the Entity DXA throws an error as it has lost the MvcData for my custom controller.

ERROR - Error executing child request for handler 'System.Web.Mvc.HttpHandlerUtil+ServerExecuteHttpHandlerAsyncWrapper'.
System.Web.HttpException (0x80004005): Error executing child request for handler 'System.Web.Mvc.HttpHandlerUtil+ServerExecuteHttpHandlerAsyncWrapper'. ---> System.InvalidOperationException: The view 'Index' or its master was not found or no view engine supports the searched locations. The following locations were searched:
~/Views/Form/Index.aspx
~/Views/Form/Index.ascx
~/Views/Shared/Index.aspx
~/Views/Shared/Index.ascx
~/Views/Form/Index.cshtml
~/Views/Form/Index.vbhtml
~/Views/Shared/Index.cshtml
~/Views/Shared/Index.vbhtml
   at System.Web.Mvc.ViewResult.FindView(ControllerContext context)

Controller code:

public class FormController : EntityController
{
    [HandleSectionError(View = "SectionError")]
    public ActionResult Index(EntityModel entity, int containerSize = 0)
    {
        // The List action is effectively just an alias for the general Entity action (we keep it for backward compatibility).
        return Entity(entity, containerSize);
    }

    [HttpPost]
    public ActionResult Index(Form form)
    {
        //do stuff here and modify the success flag
        form.SubmittedSuccessfully = true;
        return Entity(form);
    }
}

Is there anyway to re-populate this data?

| improve this question | | | | |
  • Can you explain what you are trying to achieve with the form controller? Note that DXA Entity Controllers are not regular Controllers; they are a kind of sub-controllers which are normally invoked from a Page/Region View using the Html.DxaEntity method. – Rick Pannekoek Feb 17 '16 at 18:24
  • BTW, you don't need the Index action/method. It seems the implementation was copied from ListController, but as the comment says, ListController only has it for backwards compatibility. – Rick Pannekoek Feb 17 '16 at 19:00
  • It's just a simple form that posts to itself that will send an email with the form data and then modify the model data if the email was successful or not – Ryan Durkin Feb 17 '16 at 19:29
2

This really isn't to my liking but without any other code examples of how to do this I have had to place the MvcData in TempData

protected override ViewModel EnrichModel(ViewModel model)
{
    var form = model as Form;
    TempData["MvcData"] = form.MvcData;
    return base.EnrichModel(model);
}

Then the Form Controller picks up the post and then I re-create the MvcData using the value in TempData

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Index(Form form)
{
    form.MvcData = TempData["MvcData"] as MvcData;
    //do some business logic here
    form.SubmittedSuccessfully = true;
    return Entity(form);
}
| improve this answer | | | | |
  • 2
    Ouch! That's pretty insecure. (I see no ValidateAntiForgeryToken). Maybe TempData is a better alternative? Set it in the initial Index method, read it in the posted Index method (and set it again there if you need it again). Eg: TempData["mvc-data"] = MvcData; – Albert Romkes Feb 23 '16 at 12:28
  • I agree with the ouch! I've added extra security to it but didn't want to post the entire code. I'll take a look at the TempData option. – Ryan Durkin Feb 23 '16 at 13:20
  • 1
    Code update above Albert as your idea to use TempData is better – Ryan Durkin Feb 23 '16 at 15:15
1

The easiest way to achieve what you want may be to let the form submit itself using an HTTP GET to URL of the Page containing the Form.

In that manner, the built-in Page Controller will handle the form submit (PageController.Page). It will delegate its work to Region Controllers and Entity Controllers. If one of the Entities on the Page is a Form (with associated FormController), the FormController.EnrichModel method will be invoked.

So, you should override the EnrichModel method in the Form Controller and there you will have access to the form data (in Query String Parameters). See, for example, the ListController.EnrichModel override.

If you really have to use an HTTP POST, I guess the built-in PageController.Page method can't be used. In that case you will have to create your own (Page) Controller and give it a similar implementation as PageController.Page. I would recommend to let your Controller inherit from PageController and let your action method delegate to PageController.Page.

| improve this answer | | | | |
  • The form needs to be a post as I don't want all the for data appearing in query string parameters. I also need this to be an Entity so that the content editor can add their form to any page on the website? – Ryan Durkin Feb 18 '16 at 8:55
  • It seems the best way to keep the Post functionality is to render the MvcData out onto hidden fields @Html.HiddenFor(x => x.MvcData.ActionName) and then they are mapped to the Form entity when all the form data is posted. – Ryan Durkin Feb 18 '16 at 9:41
  • In general, it doesn't make much sense you me to post to an Entity Controller and let it return an Entity in isolation; you want get a Page as response, right? So, you should post to a Page Controller which returns a Page which maybe contains a Form Entity. – Rick Pannekoek Feb 18 '16 at 19:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.