Stumped on MVC XForm

Joshua Folkerts
Member since: 2008
 

I have added propertyfor(x=>x.pageform) and the form displays as expected.  the issue i a running into is the current submission of the form.  the following is the error i am recieving.

Exception details:
InvalidOperationException: The RouteData must contain an item named 'action' with a non-empty string value.
Stack trace:

[InvalidOperationException: The RouteData must contain an item named 'action' with a non-empty string value.]
   at System.Web.Routing.RouteData.GetRequiredString(String valueName)
   at System.Web.Mvc.ViewResultBase.ExecuteResult(ControllerContext context)
   at System.Web.Mvc.ControllerActionInvoker.InvokeActionResult(ControllerContext controllerContext, ActionResult actionResult)
   at System.Web.Mvc.ControllerActionInvoker.<>c__DisplayClass1a.b__17()
   at System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilter(IResultFilter filter, ResultExecutingContext preContext, Func`1 continuation)
   at System.Web.Mvc.ControllerActionInvoker.<>c__DisplayClass1a.<>c__DisplayClass1c.b__19()
   at System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilter(IResultFilter filter, ResultExecutingContext preContext, Func`1 continuation)
   at System.Web.Mvc.ControllerActionInvoker.<>c__DisplayClass1a.<>c__DisplayClass1c.b__19()
   at System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilter(IResultFilter filter, ResultExecutingContext preContext, Func`1 continuation)
   at System.Web.Mvc.ControllerActionInvoker.<>c__DisplayClass1a.<>c__DisplayClass1c.b__19()
   at System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilter(IResultFilter filter, ResultExecutingContext preContext, Func`1 continuation)
   at System.Web.Mvc.ControllerActionInvoker.<>c__DisplayClass1a.<>c__DisplayClass1c.b__19()
   at System.Web.Mvc.ControllerActionInvoker.InvokeActionResultWithFilters(ControllerContext controllerContext, IList`1 filters, ActionResult actionResult)
   at System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName)
   at EPiServer.Web.Mvc.XForms.XFormActionBaseResult.InvokeAction(Controller controller, String actionName)
   at EPiServer.Web.Mvc.XForms.XFormFailedActionResult.ExecuteResult(ControllerContext context)
   at EPiServer.Web.Mvc.ActionControllerBase.HandleUnknownAction(String actionName)
   at System.Web.Mvc.Controller.<>c__DisplayClass1d.b__18(IAsyncResult asyncResult)
   at System.Web.Mvc.Async.AsyncResultWrapper.<>c__DisplayClass4.b__3(IAsyncResult ar)
   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult`1.End()
   at System.Web.Mvc.Controller.EndExecuteCore(IAsyncResult asyncResult)
   at System.Web.Mvc.Async.AsyncResultWrapper.<>c__DisplayClass4.b__3(IAsyncResult ar)
   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult`1.End()
   at System.Web.Mvc.Controller.EndExecute(IAsyncResult asyncResult)
   at System.Web.Mvc.Controller.System.Web.Mvc.Async.IAsyncController.EndExecute(IAsyncResult asyncResult)
   at System.Web.Mvc.MvcHandler.<>c__DisplayClass8.b__3(IAsyncResult asyncResult)
   at System.Web.Mvc.Async.AsyncResultWrapper.<>c__DisplayClass4.b__3(IAsyncResult ar)
   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult`1.End()
   at System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult)
   at System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult result)
   at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
   at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)

The RouteData must contain an item named 'action' with a non-empty string value

   What am i misssing.  Thanks in advance Ladies and Gents

#64533 Dec 28, 2012 15:23
  •  

    Just peeked a little in one of our EPiServer 7 MVC projects, and as far as I can see we have a couple of extra methods in our controller:

    IContentRepository _dataFactory;
    
    private readonly XFormPageUnknownActionHandler _xformHandler;
    
    public ArticlePageController(IContentRepository dataFactory, XFormPageUnknownActionHandler xformHandler)
    {
        _dataFactory = dataFactory;
        _xformHandler = xformHandler;
    }
    
    [AcceptVerbs(HttpVerbs.Post)]
    public virtual ActionResult Success(ArticlePage currentPage, XFormPostedData xFormPostedData)
    {
        return View("Index", new ArticlePageViewModel().InjectFrom(currentPage));
    }
    
    [AcceptVerbs(HttpVerbs.Post)]
    public virtual ActionResult Failed(ArticlePage currentPage, XFormPostedData xFormPostedData)
    {
        return View("Index", new ArticlePageViewModel().InjectFrom(currentPage));
    }
    
            [AcceptVerbs(HttpVerbs.Post)]
            public virtual ActionResult DoSubmit(XFormPostedData xFormpostedData)
            {
                return _xformHandler.HandleAction(this);
            }

    Not sure what the currentPage parameter is doing there though.. My guess is the method you're missing is the DoSubmit controller method. The InjectFrom is just some automapping stuff.

    Hope this helps

    Frederik

    #64543 Dec 30, 2012 13:45
  • Joshua Folkerts
    Member since: 2008
     

    Hey Fredrik,  I have added the two actions, but on my form submission, or propertyfor, are you passing any parameters into the helper,

    @html.PropertyFor(x=>x.PageForm, new{xxxxx=xxxx} .  I am still getting the same result when trying to submit.  I am new to how xforms are now handled in episerver mvc.  Where do i map the propertyfor xform to use DoSubmit for my action result etc.  I am a bit unclear on this.  Thanks Fredrik.

    #64545 Dec 30, 2012 20:35
  •  

    Hi Joshua

    See if this helps:

    @using EPiServer.XForms.Util
    @model EPiServer.XForms.XForm
    
    <section id="form">
        @Html.ValidationSummary()
        @Html.DisplayForModel(new { XFormParameters = new XFormParameters() { SuccessAction = "Success", FailedAction = "Failed" } })
    </section>

    Frederik   

    #64546 Dec 30, 2012 21:29
  • Joshua Folkerts
    Member since: 2008
     

    That did it.  thanks for you time and help on this Frederik.  Its much appreciated.

    #64550 Dec 30, 2012 23:52
  • Marija Jemuovic
    Member since: 2010
     

    Hi, how do I do the same for an XForm inside a block?

    I have tried c/p Success, Failed and DoSubmit methods to my BlockController, however when I submit data, I get this page (404):

    http://localhost:12347/XFormBlock/XFormPost?XFormId=9ac95421-bc25-41a5-b19c-3c6c79f04ad9&postAction=XFormPost&failedAction=Failed&successAction=Success

     

    #65005 Jan 17, 2013 12:22
  • Azzlack
    Member since: 2010
     

    @Marija

    It is only possible to post to an instance of a page. If you look into the decompiled code for XForms, you'll see that it requires an instance of the current page at some point.

    That does not mean it's not possible, you just need to post to the current page controller instead of the XFormBlock controller.

    #65041 Jan 18, 2013 9:11
  • Marija Jemuovic
    Member since: 2010
     

    Hi, thank you for your answer. I have realized that in the meantime.

    However, when I put the code for Success, Failed and DoSubmit in, let's say EventPageController and just leave the View as Fredrik pointed out, my URL looks like this (and gives 404):

    http://localhost:12347/XFormBlock/DoSubmit?XFormId=9ac95421-bc25-41a5-b19c-3c6c79f04ad9&postAction=DoSubmit&failedAction=Failed&successAction=Success

    So, I suppose I should specify the controller on the view:

    @Html.ValidationSummary()
    @Html.DisplayForModel(new { XFormParameters = new XFormParameters() { SuccessAction = "Success", FailedAction = "Failed", PostAction = "DoSubmit", PostController = "EventPageController"} })

    This gives me another 404:

    http://localhost:12347/EventPageController/DoSubmit?XFormId=9ac95421-bc25-41a5-b19c-3c6c79f04ad9&postAction=DoSubmit&postController=EventPageController&failedAction=Failed&successAction=Success

    What is supposed to be specified in PostController or maybe I am doing it completely wrong?

    Thank you!

     

    #65043 Jan 18, 2013 9:50
  • Azzlack
    Member since: 2010
     

    Heres what I have in my controller (a base controller inherited by all page controllers):

    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult XFormPost(XFormPostedData xFormpostedData)
    {
    	var formData = new XFormPageHelper().GetXFormData(thisxFormpostedData);
    
    var
     result = XFormActionHelper.DoAction(formDataxFormpostedDatatrue); return this.RedirectToAction("Index"); }
    #65051 Edited, Jan 18, 2013 11:35
  • Marija Jemuovic
    Member since: 2010
     

    Hi, still no luck :(

    When you Ctrl+Click on "Index"  take you? This is red (not existing) for me.

    Is this how your BaseController looks like?

    public class BaseController<T> : PageController<T> where T : PageData
        {
            [AcceptVerbs(HttpVerbs.Post)]
            public ActionResult XFormPost(XFormPostedData xFormPostedData)
            {
                var formData = new XFormPageHelper().GetXFormData(this, xFormPostedData);
    
                var result = XFormActionHelper.DoAction(formData, xFormPostedData, true);
    
                return this.RedirectToAction("Index");
            }
        }   
    #65062 Jan 18, 2013 14:10
  • Marija Jemuovic
    Member since: 2010
     

    Does anyone have some insight on this?

    I have moved with my other tasks, I still don't know how to deal with having XForm on a block working.

    #65283 Jan 24, 2013 15:01
  • Azzlack
    Member since: 2010
     

    Sorry for replying so late.

    I've just written a blog post explaining how I did it for our project, please take a look if you're still looking for a solution.

    http://www.eyecatch.no/blog/2013/01/using-xforms-and-mvc-in-an-episerver-7-block/

    #65359 Edited, Jan 28, 2013 10:51
  • Marija Jemuovic
    Member since: 2010
     

    Thx a lot!

    #65369 Jan 28, 2013 11:07
  •  

    Nice article Azzlack!

    #65376 Jan 28, 2013 14:06
  •  

    Hey... yeah, good article, however, has anyone worked out how to turn on the validation?

    The HtmlHelper doesn't generate any validation, its also not checked on the server side and input items marked as must contain a value are posted without any errors.

    Spent ages crawling through the SDK, any ideas?

    #65580 Feb 01, 2013 15:38
  • Marija Jemuovic
    Member since: 2010
     

    When you put a break point in Failed, does it hit it?


    It should be that just returning a view model from Failed, displays the validation errors.

    #65581 Feb 01, 2013 15:53
  •  

    I've tried that yes...  Both Success and Fail.  It still just runs Index.  I've double checked the parameters, they are all fine.  Its strange as the Css values you set for input fields also don't get rendered to the screen.

    I've been looking through the SDK and tried loads of different methods to at least try and retrieve the Validation that was set for the fields, but can't find anything....   Oh well, i'll keep looking

    #65582 Feb 01, 2013 16:05
  • Marija Jemuovic
    Member since: 2010
     

    Yes, this has happened to me while trying out the code. If you are using an XForm on page (not on a block), I can send you my whole code to try it out. Let me know if you need it.

    #65591 Feb 01, 2013 16:14
  •  

    That would be great if you could.....  it is on a Block, however, if it gets Validation working properly i'll definitely take a look and then see if I can work out where its going wrong....

    Thanks!

    #65593 Feb 01, 2013 16:22
  • Marija Jemuovic
    Member since: 2010
     

    Controller:

    public class TextPageController : MogulBaseController<TextPage>
        {
            private readonly XFormPageUnknownActionHandler _xformHandler;
    
            public TextPageController(XFormPageUnknownActionHandler xformHandler)
            {
                _xformHandler = xformHandler;
            }
    
            public ActionResult Index(TextPage currentPage)
            {
                var viewModel = new TextPageViewModel();
                Mapper.Map(currentPage, viewModel);
    
                return View(viewModel);
            }
            
            [AcceptVerbs(HttpVerbs.Post)]
            public virtual ActionResult Success(TextPage currentPage, XFormPostedData xFormPostedData)
            {
                var viewModel = new TextPageViewModel();
                Mapper.Map(currentPage, viewModel);
    
                viewModel.ShowThankYouView = true;
                viewModel.ThankYouForSubmittingFormText = LocalizationService.Current.GetString("/xform/thankyoutext");
    
                return View("Index", viewModel);
            }
    
            [AcceptVerbs(HttpVerbs.Post)]
            public virtual ActionResult Failed(TextPage currentPage, XFormPostedData xFormPostedData)
            {
                var viewModel = new TextPageViewModel();
                Mapper.Map(currentPage, viewModel);
    
                viewModel.ShowThankYouView = false;
    
                return View("Index", viewModel);
            }
    
            /// <summary>
            /// Handles the XForm postback
            /// </summary>
            /// <param name="xFormpostedData">The posted xform data.</param>
            /// <returns>The current view.</returns>
            [AcceptVerbs(HttpVerbs.Post)]
            public ActionResult XFormPost(XFormPostedData xFormpostedData)
            {
                return _xformHandler.HandleAction(this);
            }
        }

    View:

    @using EPiServer.Web.Mvc.Html
    @using EPiServer.XForms.Util
    @model Cornerstone.Web.Views.TextPage.TextPageViewModel
    
    ...
                @if (@Model.ShowThankYouView)
                {
                    @Model.ThankYouForSubmittingFormText
                }
                else
                {
                    @Html.ValidationSummary()
                    @Html.PropertyFor(m => m.Form, new { XFormParameters = new XFormParameters() { SuccessAction = "Success", FailedAction = "Failed", PostAction = "XFormPost" } })
                }
    ...

    View model has a property public XForm Form { get; set; }, and page type has:

            [Display(
                GroupName = SystemTabNames.Content,
                Order = 400
            )]
            public virtual XForm Form { get; set; }

       Please not that I do NOT have any code in the MogulBaseController. I didn't copy what Azzlack posted earlier 

    Hope this helps!

      

    #65596 Feb 01, 2013 16:28
  •  

    Thanks for that... i'll have a look and let you know how i can get on!

    #65604 Feb 01, 2013 17:21
  • Chris Sharp
    Member since: 2011
     

    I've come up with a solution that supports XForms in block types with the validation still fully working...

    http://blog.nansen.com/2013/03/creating-xform-block-in-episerver-7-mvc.html

    #66592 Edited, Mar 05, 2013 22:16
  • Sayeed Choudhury
    Member since: 2012
     

    Did anyone get Xform working in blocks with validation. I have followed instructions at Azzlack's blog but still no validation.

    #78968 Dec 05, 2013 17:02
  • Chris Sharp
    Member since: 2011
     
  • Azzlack
    Member since: 2010
     

    @Sayeed What exactly is not working? We are using the same code with success here.

    #78994 Dec 06, 2013 9:44
  • Sayeed Choudhury
    Member since: 2012
     

    Here's my code:

    Form Block View

    @using EPiServer.XForms.Util
    @using EPiServer.Web.Mvc.Html
    @using EPiServer.Templates.Logicalis.Models.Blocks
    @model XFormViewModel
    
    <div class="section section--panel section--secondary event--form">
        <h2 @Html.EditAttributes(m => m.Title)>@Model.Title</h2>
        @Html.ValidationSummary()
        
        @using (Html.BeginXForm(Model.Form, new { action = Model.ActionUrl, @class = "form xform" }))
        {
            @Html.PropertyFor(x => x.Form)    
        }    
    </div>

    Form Block View Model

    using EPiServer.XForms;
    
    public class XFormViewModel
    {
        public string Title { get; set; }
        public XForm Form { get; set; }
        public string ActionUrl { get; set; }
    }

    Form Block

    [ContentType(GroupName = Global.GroupNames.Specialized, 
    	GUID = "FA326346-4D4C-4E82-AFE8-C36279006179", 
    	DisplayName = "Form Block", 
    	Description = "Form Block")]
    public class FormBlock : SiteBlockData
    {
        [Display(GroupName = SystemTabNames.Content, Order = 1, Name = "Title")]
        [CultureSpecific]
        public virtual string Title { get; set; }
    
        [Display(GroupName = SystemTabNames.Content, Order = 2, Name = "The Form", Description = "Select a Form")]
        [CultureSpecific]
        public virtual XForm Form { get; set; }
    }

    Default Controller which inherits from BaseController class. The Default COntroller handles requests for pages that don't have their own controller.

    public class DefaultPageController : PageControllerBase<SitePageData>
    {
        public ViewResult Index(SitePageData currentPage)
        {
            if (currentPage == null)
            {
                currentPage = PageContext.Page as SitePageData;
            }
    
            var model = CreateModel(currentPage);
            return this.View(string.Format("~/Views/{0}/Index.cshtml", currentPage.GetOriginalType().Name), model);
        }
    
        private static IPageViewModel<SitePageData> CreateModel(SitePageData page)
        {
            var type = typeof(PageViewModel<>).MakeGenericType(page.GetOriginalType());
            return Activator.CreateInstance(type, page) as IPageViewModel<SitePageData>;
        }
    }

    Base controller is exactaly the same as in your post.

    The problem I am having is that I am not getting validation messages on form postbacks in blocks. Forms on pages work fine.

    #79007 Dec 06, 2013 10:25
  • Sayeed Choudhury
    Member since: 2012
     

    Ok. I got it partially working. I have removed the code from the Base Controller to the DefaultController. It now works for all pages that are using the DefaultController. However, for pages that use their own controller e.g. NewsController, it still doesn't work. I am getting this error:

    Server Error in '/' Application.
    
    The RouteData must contain an item named 'action' with a non-empty string value.
    
    Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code. 
    
    Exception Details: System.InvalidOperationException: The RouteData must contain an item named 'action' with a non-empty string value.
    
    Source Error: 
    
    An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.
    
    Stack Trace: 
    
    
    [InvalidOperationException: The RouteData must contain an item named 'action' with a non-empty string value.]
       System.Web.Routing.RouteData.GetRequiredString(String valueName) +232
       System.Web.Mvc.ViewResultBase.ExecuteResult(ControllerContext context) +91
       System.Web.Mvc.<>c__DisplayClass1a.<InvokeActionResultWithFilters>b__17() +33
       System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilter(IResultFilter filter, ResultExecutingContext preContext, Func`1 continuation) +613
       System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilter(IResultFilter filter, ResultExecutingContext preContext, Func`1 continuation) +613
       System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilter(IResultFilter filter, ResultExecutingContext preContext, Func`1 continuation) +613
       System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilter(IResultFilter filter, ResultExecutingContext preContext, Func`1 continuation) +613
       System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilter(IResultFilter filter, ResultExecutingContext preContext, Func`1 continuation) +613
       System.Web.Mvc.ControllerActionInvoker.InvokeActionResultWithFilters(ControllerContext controllerContext, IList`1 filters, ActionResult actionResult) +263
       System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName) +676
       System.Web.Mvc.<>c__DisplayClass1a.<InvokeActionResultWithFilters>b__17() +33
       System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilter(IResultFilter filter, ResultExecutingContext preContext, Func`1 continuation) +613
       System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilter(IResultFilter filter, ResultExecutingContext preContext, Func`1 continuation) +613
       System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilter(IResultFilter filter, ResultExecutingContext preContext, Func`1 continuation) +613
       System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilter(IResultFilter filter, ResultExecutingContext preContext, Func`1 continuation) +613
       System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilter(IResultFilter filter, ResultExecutingContext preContext, Func`1 continuation) +613
       System.Web.Mvc.ControllerActionInvoker.InvokeActionResultWithFilters(ControllerContext controllerContext, IList`1 filters, ActionResult actionResult) +263
       System.Web.Mvc.Async.<>c__DisplayClass25.<BeginInvokeAction>b__22(IAsyncResult asyncResult) +240
       System.Web.Mvc.<>c__DisplayClass1d.<BeginExecuteCore>b__18(IAsyncResult asyncResult) +28
       System.Web.Mvc.Async.<>c__DisplayClass4.<MakeVoidDelegate>b__3(IAsyncResult ar) +15
       System.Web.Mvc.Controller.EndExecuteCore(IAsyncResult asyncResult) +63
       System.Web.Mvc.Async.<>c__DisplayClass4.<MakeVoidDelegate>b__3(IAsyncResult ar) +15
       System.Web.Mvc.<>c__DisplayClass8.<BeginProcessRequest>b__3(IAsyncResult asyncResult) +42
       System.Web.Mvc.Async.<>c__DisplayClass4.<MakeVoidDelegate>b__3(IAsyncResult ar) +15
       System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +1799
       System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +3300
       System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +1536

        

    #79010 Dec 06, 2013 11:42