The Scenario:
I'm building a site in Umbraco 6 with MVC - I'm fairly new to Umbraco but I've done everything so far by following tutorials etc, and for the most part everything works nicely.
So I have a "contact us" form built as a partial view, rendered with the following code:
@using (Html.BeginUmbracoForm("SendEmail", "ContactFormSurface"))
{
which posts back to my ContactFormSurfaceController:
public class ContactFormSurfaceController : Umbraco.Web.Mvc.SurfaceController
{
[HttpPost]
public ActionResult SendEmail(ContactFormModel model)
{
Now, my ContactFormModel inherits from the Umbraco RenderModel, and I am "hijacking" the route for my Contact Us view in a separate ContactFormController:
public class ContactFormController : RenderMvcController
{
//
// GET: /Contact-us/
public override ActionResult Index(RenderModel model)
{
var contactFormModel = new ContactFormModel(model);
return CurrentTemplate(contactFormModel);
}
I want this so that I can have flexible headers and submit button text within the contact form based on Umbraco content. My ContactFormModel takes a RenderModel in it's constructor so that it has access to the underlying Umbraco content:
public class ContactFormModel : RenderModel
{
#region Ctr
public ContactFormModel(RenderModel model) : base(model.Content, model.CurrentCulture) {}
#endregion
#region Contact Form Fields
[Display(Name = "Your Name")]
[Required]
public string Name { get; set; }
The Problem:
When the form posts back to my surface controller SendEmail method, it appears there is an attempt to instantiate a new ContactFormModel, and I get a YSOD with the following exception:
No parameterless constructor defined for this object.
My first thought was, ok, I'll supply a parameterless constructor, since I don't actually need access to the Umbraco content within the SendEmail surface controller method, I only want that when initially rendering the view. But that's not so easy, since the base RenderModel requires an IPublishedContent object passed to it's constructor. I tried just passing null, and also:
public ContactFormModel() :base(new DynamicPublishedContent(null)) {}
but that results in a "Value cannot be null" exception.
I then tried changing my Umbraco form declaration to:
@using (Html.BeginUmbracoForm("SendEmail", "ContactFormSurface", new {model = @Model}))
to ensure that the ContactFormModel being passed to the view is sent back to the surface controller. This gets past the YSOD, but within the surface controller SendEmail method, "model" is null.
So there's a couple of things I don't really understand:
Why is there an attempt to call a parameterless constructor on my ContactFormModel in the first place? Why is my ContactFormModel from the view not just available in the surface controller method, since that is what I've specified?
When I explicitly add the model to the route values for the form post, why does it come through as null?
It feels like there must be simple solution to this, and I'm maybe missing something fundamental. Searching the forums there are plenty of examples of hijacking routes and inheriting from RenderModel, and also using a custom model and surface controller to process a form post, but not the 2 things combined, when the custom model inherits from RenderModel.
If I can't find a solution to this, I'll have to resort to not inheriting from RenderModel and hence not allowing any editable content within the contact us form, which seems to defeat the object of Umbraco. Or, create another model purely for use with the surface controller that doesn't inherit from RenderModel but duplicates all the fields in my ContactFormModel, which would be plain crazy!
Thanks for any ideas or advice.