Sitecore MVC - how to handle multiple forms per page

I watched Sitecore MVC, but I was stuck on how to handle the case where my page has two controller repositories and each of them contains a form. I want individual controllers to process HttpPost and return the whole page after posting.

I created a simple example. Both controllers are similar:

public class ExampleController : Sitecore.Mvc.Controllers.SitecoreController { public override ActionResult Index() { return View("Index"); } [HttpPost] public ActionResult Index(string formPostData) { ViewBag.SaveForLater = formPostData; return Index(); } } 

The views are as follows:

 @using Sitecore.Mvc @using (Html.BeginRouteForm(Sitecore.Mvc.Configuration.MvcSettings.SitecoreRouteName, FormMethod.Post)) { @Html.AntiForgeryToken() var term = ViewBag.SaveForLater as string; if (!string.IsNullOrEmpty(term)) { <p>Submitted: @term</p> } <p> @Html.Sitecore().FormHandler("Example", "Index") <input type="text" name="formPostData" placeholder="Enter something" /> <input type="submit" name="submit" value="Search" /> </p> } 

With this setting, both forms present their data, but the returned page consists only of a partial view, and not for the entire page.

If I replace the line @Html.Sitecore().FormHandler("Example", "Index") with @Html.Sitecore().FormHandler() , then the whole page is returned, but the post action for both forms is processed.

None of the scenarios are perfect. I need to miss something and appreciate the pointer.

+6
source share
3 answers

Unfortunately, there are several ways that you can integrate with Sitecore MVC and Sitecore, it does not contain many examples of best practice. For example, you can find it here .

In our projects, we do this a little differently, because we want to use as many agreements as possible, etc. from ASP.NET MVC by default. I am trying to include a complete simple example in this post.

We add two different hidden fields to the form with the current action and the current controller, the view is as follows:

 @model Website.Models.TestViewModel @using (Html.BeginForm()) { @Html.LabelFor(model => model.Text) @Html.TextBoxFor(model => model.Text) <input type="submit" value="submit" /> <input type="hidden" name="fhController" value="TestController" /> <input type="hidden" name="fhAction" value="Index" /> } 

With this simple ViewModel:

 namespace Website.Models { public class TestViewModel { public string Text { get; set; } } } 

Then we created a custom attribute that checks if the current controller / action matches the message:

 public class ValidateFormHandler : ActionMethodSelectorAttribute { public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo) { var controller = controllerContext.HttpContext.Request.Form["fhController"]; var action = controllerContext.HttpContext.Request.Form["fhAction"]; return !string.IsNullOrWhiteSpace(controller) && !string.IsNullOrWhiteSpace(action) && controller == controllerContext.Controller.GetType().Name && methodInfo.Name == action; } } 

Then the controller actions receive a new attribute:

 namespace Website.Controllers { public class TestController : Controller { public ActionResult Index() { return View(); } [HttpPost] [ValidateFormHandler] public ActionResult Index(TestViewModel model) { return View(model); } } } 

We always return the view permitted by ASP.NET MVC. By convention, this view is with the same name as the action in the folder with the same name as the controller.

This approach works very well for us. If you want to add AntiForgeryToken , this also works great.

+12
source

You must create the main view of the pointer, which has two partial views, for example:

You can define a basic view model, for example,

 public class MainViewModel { public LoginViewModel LoginModel { get; set; } public RegisterViewModel RegisterModel { get; set; } } 

then a separate model:

  public class RegisterViewModel { // Your model properties } public class LoginViewModel { // Your model properties } 

then define the action for the main view as:

  public ActionResult MainView() { MainViewModel model = new MainViewModel { LoginModel = new LoginViewModel(), RegisterModel = new RegisterViewModel() }; return View(model); } 

Your basic idea is how

  @Html.Partial("_Login", Model.LoginModel) @Html.Partial("_Register", Model.RegisterModel) 

after that you can orient your views, thanks

0
source

Sitecore Habitat does this similarly above, but uses a unique rendering identifier.

 public class ValidateRenderingIdAttribute : ActionMethodSelectorAttribute { public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo) { var ignoreCase = StringComparison.InvariantCultureIgnoreCase; var httpRequest = controllerContext.HttpContext.Request; var isWebFormsForMarketersRequest = httpRequest.Form.AllKeys .Any(key => key.StartsWith("wffm", ignoreCase) && key.EndsWith("Id", ignoreCase)); if (isWebFormsForMarketersRequest) { return false; } string renderingId; if (!httpRequest.GetHttpMethodOverride().Equals(HttpVerbs.Post.ToString(), ignoreCase) || string.IsNullOrEmpty(renderingId = httpRequest.Form["uid"])) { return true; } var renderingContext = RenderingContext.CurrentOrNull; if (renderingContext == null) { return false; } Guid id; return Guid.TryParse(renderingId, out id) && id.Equals(renderingContext.Rendering.UniqueId); } } 

Repo Link

0
source

All Articles