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
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.
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
That did it. thanks for you time and help on this Frederik. Its much appreciated.
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
@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.
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!
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(this, xFormpostedData);
var result = XFormActionHelper.DoAction(formData, xFormpostedData, true); return this.RedirectToAction("Index"); }
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");
}
}
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.
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/
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?
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.
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
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.
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!
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!
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
Did anyone get Xform working in blocks with validation. I have followed instructions at Azzlack's blog but still no validation.
@Sayeed... Check out http://cjsharp.com/blog/2013/04/11/creating-a-xform-block-in-episerver-7-mvc-with-working-validation-updated/. This is not tested in 7.5 yet.
@Sayeed What exactly is not working? We are using the same code with success here.
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.
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
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.
What am i misssing. Thanks in advance Ladies and Gents