2014-04-15 12 views
6

behandelt Ich habe Sitecore MVC betrachtet, aber ich bin fest, wie man einen Fall behandelt, wo meine Seite zwei Controller-Renderings hat und jedes ein Formular enthält. Ich möchte, dass die einzelnen Controller ihre HttpPost behandeln und die ganze Seite nach dem Post zurückgeben.Sitecore MVC - Wie man mehrere Formen auf Seite

Ich habe ein einfaches Beispiel eingerichtet. Beide Controller sind ähnlich:

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(); 
    } 
} 

die Ansichten wie folgt aussehen:

@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> 
} 

Mit diesem Setup beide Formen reichen ihre Daten aber die zurückgegebene Seite besteht nur der Teilansicht und nicht die ganze Seite .

Wenn ich die Linie @Html.Sitecore().FormHandler("Example", "Index") mit @Html.Sitecore().FormHandler() ersetzen dann die ganze Seite zurückgegeben wird, aber die Post Aktion für beide Formen ist verarbeitet.

Keines der beiden Szenarien ist ideal. Ich muss etwas vermissen und würde einen Zeiger schätzen.

Antwort

12

Leider gibt es mehrere Möglichkeiten, wie Sie mit Sitecore MVC und Sitecore integrieren bietet nicht viele Best-Practice Beispiele. Ein Beispiel finden Sie here.

In unseren Projekten tun wir es ein bisschen anders, weil wir so viel wie möglich die Konventionen usw. von Standard ASP.NET MVC verwenden möchten. Ich versuche, ein komplettes einfaches Beispiel in diesen Beitrag aufzunehmen.

Wir fügen zwei verschiedene versteckte Felder in das Formular mit der aktuellen Aktion und den Stromregler, sieht die Ansicht wie folgt aus:

@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" /> 
} 

Mit diesem einfachen Ansichtsmodell:

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

Dann haben wir hat ein benutzerdefiniertes Attribut erstellt, das überprüft, ob der aktuelle Controller/die gleiche Aktion wie der gebuchte ist:

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; 
    } 
} 

Die Controller-Aktionen wird dann das neue Attribut:

namespace Website.Controllers 
{ 
    public class TestController : Controller 
    { 
     public ActionResult Index() 
     { 
      return View(); 
     } 

     [HttpPost] 
     [ValidateFormHandler] 
     public ActionResult Index(TestViewModel model) 
     { 
      return View(model); 
     } 
    } 
} 

Wir die Ansicht immer zurückkehren, indem ASP.NET MVC aufgelöst. Per Konvention ist dies die Ansicht mit dem gleichen Namen wie die Aktion innerhalb des Ordners mit dem gleichen Namen wie der Controller.

Dieser Ansatz funktioniert sehr gut für uns. Wenn Sie die AntiForgeryToken hinzufügen möchten, funktioniert das auch gut.

+0

Danke @ kevin-brechbuhl - du machst eigentlich was ich versuche. Die Zeile '@ Html.Sitecore(). FormHandler (" Beispiel "," Index ")' tut im Grunde genau das, was Sie sagen (dh fügt die Controller- und Aktionsnamen als versteckte Felder hinzu) und scheint das Verhalten zu verursachen, mit dem Sie modelliert haben ein Attribut, das intern auftreten soll. Wenn ich diese Zeile einfüge, wird nur eine der HttpPost-Aktionen verarbeitet. Aber an dem Punkt, an dem ich die Ansicht von dieser Aktion zurücksende ('return View (Modell);' oben), verliere ich den Rest der Seite. Es wird nur der Inhalt dieser Ansicht im Browser gerendert, nicht die ganze Seite. Irgendeine Idee warum? – getsetcode

+0

@getsetcode Wenn ich mich richtig erinnere, ist das wegen dem Sitecore FormHandler und den versteckten Feldern von Sitecore. Sitecore rendert diese Aktion nur, wenn die versteckten Felder von Sitecore verfügbar sind. Deshalb erstellen wir eigene versteckte Felder und verwenden nicht die von Sitecore. Versuchen Sie den '@ Html.Sitecore(). FormHandler (" Example "," Index ")' 'zu entfernen. –

+0

Nochmals vielen Dank @ Kevin-Brechbuhl für Ihre Hilfe. Ich habe versucht, was Sie empfehlen, und bin jetzt auf die Ausnahme "System.InvalidOperationException: Konnte Aktionsmethode nicht aufrufen", die ich denke, macht Sinn, weil eine der zwei HttpPost-Aktionen nicht aufgrund Ihrer ValidateFormHandler getroffen wird. Ich fühle, dass ich nah bin, aber ist das ein weiterer Schritt, den du verpasst hast? Es scheint, dass Sitecore beide Post-Aktionen treffen will, ob ich es mag oder nicht :( – getsetcode

0

Sie sollten die Hauptindexanzeige mit zwei Teilansicht erstellen, wie folgt aus:

Sie können wie diese

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

    public RegisterViewModel RegisterModel { get; set; } 
} 

dann das separate Modell Hauptansicht Modell definieren:

public class RegisterViewModel 
{ 
// Your model properties 
    } 

public class LoginViewModel 
{ 
    // Your model properties 
} 

dann definieren Sie Ihre Aktion für die Hauptansicht als:

public ActionResult MainView() 
    { 

     MainViewModel model = new MainViewModel 
     { 
      LoginModel = new LoginViewModel(), 
      RegisterModel = new RegisterViewModel() 
     }; 

     return View(model); 
    } 

Ihre Hauptansicht als

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

nach, dass Sie Ihre Ansichten saperately erstellen können, dank

+0

Danke, aber können Sie mehr Details zur Verfügung stellen? Es gibt keine Hauptindexansicht. Das ist Sitecore, also habe ich zwei MVC-Ansichten, die zu meinem Layout als Controller-Renderings hinzugefügt wurden. Sie haben jeweils ihren eigenen Controller. Ich bin mir nicht sicher, wie dein Vorschlag relevant ist ...? – getsetcode

+0

siehe Update – Ni3

+1

@getsetcode Punkt steht noch. Dies ist keine Sitecore-Lösung. In normalen MVC ja! Aber Sitecore MVC ist nicht normal. lol –

0

Sitecore Habitat funktioniert ähnlich wie oben, verwendet jedoch eine eindeutige Rendering-ID.

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); 
    } 
} 

Link to repo