Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Stephen Maij 11 posts 36 karma points
    Nov 30, 2012 @ 12:58
    Stephen Maij
    0

    4.11 Using SurfaceController Child Action with Post

    Hi,

    I need some help with using Surface Controller's as Child action.
    The child action renders just fine, but when doing a post it breaks after returning the CurrentUmbracoPage() as ActionResul.

    The exception that is returned

    The particular return just before the exception is thrown.

     

    In the umbraco source I can find this particular code:

     

    //validate that the current page execution is not being handled by the normal umbraco routing system
                if (!context.RouteData.DataTokens.ContainsKey("umbraco-route-def"))
                {
                    throw new InvalidOperationException("Can only use " + typeof(UmbracoPageResult).Name + " in the context of an Http POST when using a SurfaceController form");
                }

    In the "Standard_Website_MVC_1.1" project I noticed that in the Contact form example it is jusing a Partial View action instead, but I think it's nicer to use child actions for this.

    So, this brings me to the next question. Is it possible to use a SurfaceController with a child action that needs to handle post form validation and return the current page ?

    Please let me know if you need more information about my setup.

    Kind regards

     

  • Timothy Beutels 11 posts 31 karma points
    Dec 02, 2012 @ 02:20
    Timothy Beutels
    0

    I think you're missing Surface in your controller name. 

    ContactFormController should be ContactFormSurfaceController, or it won't be detected as a SurfaceController and  throw the exception you mention.

    But that won't get you were you (and me) want to be either. You'll just get another exception:
    The model item passed into the dictionary is of type 'Umbraco.Web.Models.RenderModel', but this dictionary requires a model item of type 'YourModel'.

    I agree it would be a lot cleaner to be able to post from a (child)action, but that doesn't seem possible atm.
    Which kind of defeats the purpose of using MVC - the form is in a shared partial view - 'triggered' from the template's view - posting to an unrelated (surface) controller.

  • Kevin Lawrence 183 posts 350 karma points
    Dec 04, 2012 @ 20:42
    Kevin Lawrence
    0

    The error is occurring because you are calling ContactForm yet there are 2 ambiguous signatures that support calling those methods with no parameter, try specifying unique action names or specifying an ActionName attribute on one of them.

  • Stephen Maij 11 posts 36 karma points
    Dec 07, 2012 @ 17:22
    Stephen Maij
    0

    @ Timothy

    My view has this model:  @inherits Umbraco.Web.Mvc.UmbracoViewPage<Site.Core.Models.ContactFormModel>.
    Also at this moment the controller is called ContactFormSurfaceController, so thats not the case any longer.

    The view contains this

    @using(Html.BeginUmbracoForm<Site.Core.SurfaceControllers.ContactFormSurfaceController>("ContactForm"))

    I also tried the regular

    @using(Html.BeginForm())

    so the form is going to be submitted to the page's url (/contact)

     

    @Kevin

    I do have different signatures. The post action has the model passed as parameter

     

    At this point I'm using the partial as solution but I think it's not nice at all..

     

  • Stephen Maij 11 posts 36 karma points
    Dec 07, 2012 @ 17:23
    Stephen Maij
    0

    ignore this post

  • Kevin Lawrence 183 posts 350 karma points
    Dec 07, 2012 @ 17:25
    Kevin Lawrence
    0

    Hi Stephen

    I know they are unique signatures but I think they might have to be differently named, do you get this when you initially load the page or after posting?

  • Stephen Maij 11 posts 36 karma points
    Dec 07, 2012 @ 17:27
    Stephen Maij
    0

    Hi Kevin,

    Initially it works fine. This exception occures when you post the form.

  • Kevin Lawrence 183 posts 350 karma points
    Dec 07, 2012 @ 17:29
    Kevin Lawrence
    0

    Hi Stephen

    Did you trying using unique action names?

    Kev

  • Timothy Beutels 11 posts 31 karma points
    Dec 07, 2012 @ 17:34
    Timothy Beutels
    0

    Hi Kevin,

    I tried unique action names in my initial quest but it didn't matter - as expected.

    Using a Surface controller will get you past the error from the first post.
    But you'll hit the one I mentioned earlier.

    Seems like were stuck with using partial views for forms atm. 

     

  • Stephen Maij 11 posts 36 karma points
    Dec 07, 2012 @ 17:37
    Stephen Maij
    0

    It does look like using a different action name for post does work

      [ChildActionOnly]
            public ActionResult ContactForm()
            {
                var model = new ContactFormModel();

                return PartialView(model);
            }

            [HttpPost]
            //[ValidateInput(true)]
            [ActionName("ContactFormPost")]
            public ActionResult ContactForm(ContactFormModel model)
            {

     

    In the template I do this

    @Html.Action("ContactForm", "ContactFormSurface")

     

    In the ContactForm view I do this and this does work.

    @inherits Umbraco.Web.Mvc.UmbracoViewPage<Site.Core.Models.ContactFormModel>
    @using Site.Core

    @using (Html.BeginUmbracoForm<Site.Core.SurfaceControllers.ContactFormSurfaceController>("ContactFormPost"))

     

    Thanks!

     

     

  • Kevin Lawrence 183 posts 350 karma points
    Dec 07, 2012 @ 17:48
    Kevin Lawrence
    0

    Glad it works :-)

  • Timothy Beutels 11 posts 31 karma points
    Dec 07, 2012 @ 17:49
    Timothy Beutels
    0

    It won't work for me if I do something like this - you'll get the same exception again.

    if (!ModelState.IsValid) { return CurrentUmbracoPage(); }

  • Kevin Lawrence 183 posts 350 karma points
    Dec 07, 2012 @ 17:52
    Kevin Lawrence
    0

    I don't get this issue, I have a validating form and it works fine, providing Ihave unique action names, what is the exact exception you are getting after using unique actions?

  • Timothy Beutels 11 posts 31 karma points
    Dec 07, 2012 @ 18:02
    Timothy Beutels
    0
    [ChildActionOnly]
            public ActionResult Contact()
            {
                var contactModel = new ContactModel();
                return PartialView(contactModel);
            }
    
            public ActionResult ContactPost(ContactModel model)
            {    
                if (!ModelState.IsValid)
                {
                    return CurrentUmbracoPage();
                }
    

    Exception

    The model item passed into the dictionary is of type 'Umbraco.Web.Models.RenderModel', but this dictionary requires a model item of type 'ContactModel'.

    Template View

    @Html.Action("Contact", "ContactSurface")

    ContactSurface\Contact.cshtml

    @inherits Umbraco.Web.Mvc.UmbracoViewPage 
    @using(Html.BeginUmbracoForm("ContactPost")){ .. }

     

  • Kevin Lawrence 183 posts 350 karma points
    Dec 07, 2012 @ 18:03
    Kevin Lawrence
    0

    @Timothy

    You don't have a [HttpPost] attribute above your ContactPost action.

    Kev

  • Timothy Beutels 11 posts 31 karma points
    Dec 07, 2012 @ 18:06
    Timothy Beutels
    0

    Hi Kev,

    I actually do - was just struggling with the wysiwyg editor & must have deleted it by accident.

    It shouldn't make at difference either I think?

     

    /Edit: Also, the view looks like this (hoping it displays correctly this time around)

    @inherits Umbraco.Web.Mvc.UmbracoViewPage<ContactModel>
    @{
        Layout = null;
    }
    @using(Html.BeginUmbracoForm<ContactSurfaceController>("ContactPost"))
  • Kevin Lawrence 183 posts 350 karma points
    Dec 07, 2012 @ 18:10
    Kevin Lawrence
    0

    It will make a difference, but if you already have it then that's fine.

    The type in your Contact view should be:

    @inherits UmbracoViewPage
  • Timothy Beutels 11 posts 31 karma points
    Dec 07, 2012 @ 18:15
    Timothy Beutels
    0

    Jep, it's there.

    It actually executes the correct action (ContactPost) as well, but throws somewhere after it returns CurrentUmbracoPage(). 

  • Kevin Lawrence 183 posts 350 karma points
    Dec 07, 2012 @ 18:20
    Kevin Lawrence
    0

    Hmm, this is a bit baffling then, that all seems legit, feel free to email me some of the code if you want me to investigate further.

  • Kevin Lawrence 183 posts 350 karma points
    Dec 07, 2012 @ 18:21
    Kevin Lawrence
    0
  • Timothy Beutels 11 posts 31 karma points
    Dec 07, 2012 @ 18:37
    Timothy Beutels
    0

    Sounds great - where can I contact you?

    /edit: email removed

  • Kevin Lawrence 183 posts 350 karma points
    Dec 07, 2012 @ 21:23
    Kevin Lawrence
    0

    I've emailed you, but here is what I think the issue is:

    I think I’ve found a caveat here, basically your POST action should be the same name as your template, I can only guess this is because when it returns the current page it passes the model through your template (I am guessing here though), I don’t know whether this is intentional or not but it’s the only way I can get it to work, and it’s how my implementation works, here’s the new sigantures in the controller:
     

    [ChildActionOnly]

    [ActionName("ContactForm")]

    public ActionResult Contact()

    {

    return PartialView("ContactForm", new ContactModel());

    }

    [HttpPost]

    public ActionResult Contact(ContactModel model)

    {

    if (!ModelState.IsValid)

    {

    return CurrentUmbracoPage();

    }

    Then rename your Contact.cshtml partial to ContactForm.cshtml.  I have got this working successfully here using the code you sent me.
     
    Give that a go.
  • Timothy Beutels 11 posts 31 karma points
    Dec 07, 2012 @ 22:51
    Timothy Beutels
    0

    Hi Kev,

    Thanks for looking at the code - I really appreciate it.
    Your solution does seem to work!

    I still think we're onto a bug here.
    It doesn't make sense (to me at least) that the action invoked on a SurfaceController influences the rendering, and only in this specific case.

    I have to admit I'm not familiar with the new routing and rendering code yet, but this might be a good opportunity to get up to speed on it.
    Maybe it will make more sense then - in that case this behavoir should be documented and explained.

    Thanks again! 

     

  • Kevin Lawrence 183 posts 350 karma points
    Dec 08, 2012 @ 13:51
    Kevin Lawrence
    0

    Hi Timothy

    Agreed, I think this is a bug, it should make no difference what the action is called, might be worth one of us logging this behaviour here:

    http://issues.umbraco.org/dashboard/#tab=Hints

    Kev

  • Stephen Maij 11 posts 36 karma points
    Dec 13, 2012 @ 10:00
    Stephen Maij
    0

    Hi All,

    I have replaced all the form's with the solution,  but now I ran in another quirk.
    It seems that the ViewData is no longer passed to the model in this setup.

    So when I do for example:

    ViewData["ErrorMessage"] = "Something";
    return CurrentUmbracoPage();

    It ends up empty.

    I will create an issue for it on the bug tracker.

  • Shannon Deminick 1524 posts 5269 karma points MVP 2x
    Jan 22, 2013 @ 23:24
    Shannon Deminick
    1

    When you execute a @Html.Action command, MVC does not pass/merge up the current view's ViewData dictionary to the ChildAction, though it does merge up the ModelState dictionary which is why validation works (though I cannot seem to find out where this occurs in the MVC source). If you want to access the ViewData that you've set on the master ViewContext's on a ChildAction being rendered from the master's ViewContext then you need to use:

    @ViewContext.ParentActionViewContext.ViewData["ErrorMessage"]

    The ParentActionViewContext in this example is the ViewContext that is rendering the Umbraco template, not the ChildAction. That is because when you POST (whether inside of Umbraco or normal MVC), you are posting to a new Action and the rendering process starts from scratch, when you validate your model, update the ViewData, etc... this all happens on what will become the 'master' ViewContext when the view renders. This view then will render your ChildAction.

    Hope that helps.

  • Jon R. Humphrey 164 posts 455 karma points c-trib
    Feb 24, 2013 @ 14:20
    Jon R. Humphrey
    0

    @Shannon, et al,

    Sorry to hijack but out of curiosity, why wouldn't this work for either

    "ViewContext.ParentActionViewContext.ViewData["Prompt"]"

    or

    "ViewContext.ParentActionViewContext.ViewData.ModelMetadata.Watermark"

    in order to get the value of the placeholder for a form field of a child action?

  • Shannon Deminick 1524 posts 5269 karma points MVP 2x
    Feb 25, 2013 @ 15:25
    Shannon Deminick
    0

    @Jon: Not sure I am understanding your question correctly. This will return the value for an item called "Prompt" that was set in the "parent" view context:

    ViewContext.ParentActionViewContext.ViewData["Prompt"]

    this will never work however:

    ViewContext.ParentActionViewContext.ViewData.ModelMetadata.Watermark

    because 'Watermark' is not a property of ModelMetadata (System.Web.Mvc.ModelMetadata). If you had set an item called "Watermark" in the parent view context you could retreive it by this:

    ViewContext.ParentActionViewContext.ViewData["Watermark"]

    Or by this:

    ViewContext.ParentActionViewContext.ViewBag.Watermark

    The latter is using the dynamic ViewBag property which is just a dynamic wrapper on the ViewData.

  • Jon R. Humphrey 164 posts 455 karma points c-trib
    Feb 25, 2013 @ 16:33
    Jon R. Humphrey
    0

    Shannon,

    I was trying to figure out how to use the Prompt property of the System.ComponentModel.DataAnnotations.DisplayAttribute so I could pass in placeholder text from the backoffice based on the SO conversation: http://stackoverflow.com/questions/5824124/html5-placeholders-with-net-mvc-3-razor-editorfor-extension

    However I'm having issues getting the values set in the back office from the Surface Controller into the form model anyway so until I can figure this out the placeholder is moot no matter which way I go about it!

    Thanks for your help!

    Jon

  • Dan Roma 18 posts 38 karma points
    Nov 25, 2014 @ 20:18
    Dan Roma
    0

    Is there a way to avoid postback here and display message to user? On success or error?

  • Pratham 4 posts 24 karma points
    Mar 28, 2015 @ 13:44
    Pratham
    0

    I am getting this error. "The model item passed into the dictionary is of type 'Umbraco.Web.Models.RenderModel', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable`1[abo.Models.Phone]'."

    For more details click here Click Here

    Thanks.

Please Sign in or register to post replies

Write your reply to:

Draft