I've searched all the available tutorials I can find, and I'm still having trouble with Umbraco Surface Controllers. I've created a bare-bones Surface Controller example which sorta works, but has some issues. Here's my code so far, questions to follow:
ContactformModel1.cs:
public class ContactFormModel1
{
public string Email { get; set; }
public string Name { get; set; }
public string HoneyPot { get; set; }
public string Title { get; set; }
public string Last { get; set; }
public string First { get; set; }
public string Addr { get; set; }
public string Phone { get; set; }
public string Time { get; set; }
public string Comment { get; set; }
}
ContactSurfaceController.cs:
public class ContactSurfaceController : Umbraco.Web.Mvc.SurfaceController
{
public ActionResult Index()
{
return Content("this is some test content...");
}
[HttpGet]
[ActionName("ContactForm")]
public ActionResult ContactFormGet(ContactFormModel1 model)
{
return PartialView("~/Views/ContactSurface/Contact1.cshtml", model);
}
[HttpPost]
[ActionName("ContactForm")]
public ActionResult ContactFormPost(ContactFormModel1 model)
{
// Return the form, just append some exclamation points to the email address
model.Email += "!!!!";
return ContactFormGet(model);
}
public ActionResult SayOK(ContactFormModel1 model)
{
return Content("OK");
}
}
Contact.cshtml:
@model ContactFormModel1
@using (Html.BeginUmbracoForm<ContactSurfaceController>("ContactForm"))
{
@Html.EditorFor(x => Model)
<input type="submit" />
}
ContactMacroPartial.cshtml:
@inherits Umbraco.Web.Macros.PartialViewMacroPage
@Html.Action("ContactForm", "ContactSurface")
My Questions:
I'm pretty sure that return ContactFormGet(model)
is wrong in the
ContactFormPost method, but everything else I've tried throws an error.
When I try return RedirectToCurrentUmbracoPage()
, I get Cannot
find the Umbraco route definition in the route values, the request
must be made in the context of an Umbraco request
.
When I try return CurrentUmbracoPage()
, I get Can only use
UmbracoPageResult in the context of an Http POST when using a
SurfaceController form
.
The routing appears to work correctly (when I put a breakpoint inside ContactFormPost, the debugger stops there). But when the form comes back, I get the exact values I submitted. I don't see the !!! appended to the email address. (Note, this bit of code is just for debugging, it's not meant to do anything useful).
How do I call the "SayOK" method in the controller? When I change the BeginUmbracoForm method to point to SayOK, I still get stuck in the ContactFormPost method.
I'm sure I'm missing something incredibly stupid, but I can't figure this out for the life of me.
I wanted to take a moment to say how I resolved this. After playing around some more, I realized that I didn't really state my problem clearly. Basically, all I'm trying to do is embed an MVC form inside a Partial View Macro, so that it could be used in the content of a page (not embedded in the template).
I could get this solution to work, but I really didn't like how much logic the author put inside the View file. So I adapted his solution this way:
Partial View Macro (cshtml) file:
@inherits Umbraco.Web.Macros.PartialViewMacroPage
@using Intrepiware.Models
@{
bool isPostback = !String.IsNullOrEmpty(Request.Form["submit-button"]);
if(isPostback)
{
@Html.Action("CreateComment", "ContactSurface", Request.Form)
}
else
{
@Html.Partial("~/Views/Partials/ContactForm.cshtml", new ContactFormModel())
}
}
Form Partial View (cshtml) file:
@using Intrepiware.Models
@using Intrepiware.Controllers
@model ContactFormModel
<p>
<span style="color: red;">@TempData["Errors"]</span>
</p>
<p>
@TempData["Success"]
</p>
<div id="cp_contact_form">
@using(Html.BeginUmbracoForm("CreateComment", "BlogPostSurface"))
{
@* Form code goes here *@
}
ContactSurfaceController.cs file:
public class ContactSurfaceController : Umbraco.Web.Mvc.SurfaceController
{
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult ubCreateComment(ContactFormModel model)
{
if (processComment(model) == false)
return CurrentUmbracoPage();
else
return RedirectToCurrentUmbracoPage();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CreateComment(ContactFormModel model)
{
if(processComment(model) == true)
{
TempData["Success"] = "Thank you for your interest. We will be in contact with you shortly.";
ModelState.Clear();
}
return PartialView("~/Views/Partials/ContactForm.cshtml");
}
private bool processComment(ContactFormModel model)
{
// Handle the model validation and processing; return true if success
}
}
The controller is designed so that the form can be embedded either in the template or a Partial View Macro. If it's embedded in a template, the form should post to ubCreateComment
; if it's in a macro, post to CreateComment
.
I'm almost positive there's a better/more correct way of doing this, but I ran out of time to work on the project. If someone has a better solution, please post it!
One final question/note: You'll notice that the partial view macro posts Request.Form to the ContactSurfaceController.CreateComment, and MVC magically serializes it for me. That's safe, yeah? If so, doesn't MVC rock? :)
You are using a ChildAction because you are specifying @Html.Action("ContactForm", "ContactSurface")
and because of this, in your View you need to:
Html.BeginForm(...)
and not 'Html.BeginUmbracoForm(...)'If you do this, then the form will post back to itself as expected.
See the documentation here for further help.
Edit:
Just saw the final part to your question. If you intend SayOK
to be your 'thank you' message, I would just call it from your HttpPost
action instead of returning the initial view.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With