Copied to clipboard

Flag this post as spam?

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


  • Robert Foster 393 posts 1459 karma points admin c-trib
    Jan 15, 2013 @ 04:36
    Robert Foster
    0

    SurfaceController Form Post with feedback

    Hi,

    Not really sure where in the forums this topic should go under, this is a continuation of a discussion I started in the developer group, but as pointed out, really belongs here.

    The Scenario

    I want to post back (not ajax this time) a form and re-render the form with the contents the user submitted + a success/failure message.  I use @Html.Action(..) to render the form in a content template.

     

    My Controller looks like this:

            [ChildActionOnly]
    public ActionResult ContactUs(SiteContactForm model)
    {
    HtmlHelper.UnobtrusiveJavaScriptEnabled = true;
    HtmlHelper.ClientValidationEnabled = true;

    // Check if already handled. Copy the View Data back from the parent context and
    // return the partial view instead of processing again.
    if (ControllerContext.ParentActionViewContext != null)
    {
    foreach (var d in ControllerContext.ParentActionViewContext.ViewData)
    ViewData[d.Key] = d.Value;
    }

    return PartialView(model);
    }

    [HttpPost]
    public ActionResult ContactUsHandler(SiteContactForm model)
    {

    if (ModelState.IsValid)
    {
    string errorMsg = string.Empty;

    model.Page = CurrentPage;
    ViewData["ContactFormSuccess"] = model.SendMail(Umbraco, out errorMsg);
    ViewData["ContactFormErrorMessage"] = errorMsg;
    }
    else
    {
    ViewData["ContactFormSuccess"] = false;
    }

    // We want to return to the actual page.
    return CurrentUmbracoPage();
    }

    On the MVC page template I include the view like so:

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

    The Child View includes this:

    @using (Html.BeginUmbracoForm<BuyLara.MVC.Controllers.ContactController>("ContactUsHandler"))
    {
    @Html.ValidationSummary(true)

    Render form including any validation/feedback messages etc.

    }

    Now, the action renders the first method correctly, and on post the second method is called as I would expect followed by the first method again due to the following line:

                return CurrentUmbracoPage();

    The Problem

    The problem I was having was that if I didn't check for the ControllerContext.ParentActionViewContext and copy the ViewData values back in the first method, then I didn't get any of the ViewData values when the form is posted back and then re-displayed. 

    Another problem that I'm now having is that the model is initialised but with the ModelState.IsValid set to false, which means my form is automatically alerting the user that required fields are empty etc.  I had to modify the original method by adding the following right before returning to get around this problem:

                if (!ViewData.ContainsKey("ContactFormSuccess"))
    {
    ModelState.Clear();
    }

    I'm thinking there has to be a better way to retain the ViewData/Render the form post feedback without having to jump through so many hoops to get there... any suggestions?

  • Adrian Inman 35 posts 59 karma points
    Jan 17, 2013 @ 08:55
    Adrian Inman
    0

    I have a similar problem, but am getting 

    Can only use UmbracoPageResult in the context of an Http POST when using a SurfaceController form

    Which isn't very helpful. I can't find any guides on Surface Controller forms with Umbraco 4.10+

    HELP!

  • Robert Foster 393 posts 1459 karma points admin c-trib
    Jan 17, 2013 @ 09:01
    Robert Foster
    0

    Hi Adrian,

    Are you using Html.BeginUmbracoForm or Html.BeginForm on your View?  and what does your controller class look like?

  • Adrian Inman 35 posts 59 karma points
    Jan 17, 2013 @ 09:03
    Adrian Inman
    0

    using (Html.BeginUmbracoForm("StaffAbsenceQuoteForm", "StaffAbsenceQuoteFormSurface", FormMethod.Post, new { @class = "custom" }))

    is what I'm doing in the View

    Controller below. I don't want to redirect to current, I would prefer to redirect elsewhere, but that just doesn't work. Tips on documentation most welcome. I have given up and am using logic int the view to control the output based on the TempData entry

    public class StaffAbsenceQuoteFormSurfaceController : SurfaceController

        {

            //

            // GET: /StaffAbsenceQuoteForm/

            [HttpGet]

            public ActionResult StaffAbsenceQuoteForm()

            {

                ViewBag.TeacherExcess = GenerateExcessPeriods();

                ViewBag.SupportExcess = GenerateExcessPeriods();

                ViewBag.OtherExcess = GenerateExcessPeriods();

                return PartialView("StaffAbsenceQuoteForm");//, new StaffAbsenceQuoteFormModel());

            }

     

            [HttpPost]

            public ActionResult StaffAbsenceQuoteForm(StaffAbsenceQuoteFormModel model)

            {

     

                TempData["Outcome"] = true;

                

                    return this.RedirectToCurrentUmbracoPage();

                

            }

        }

  • Robert Foster 393 posts 1459 karma points admin c-trib
    Jan 17, 2013 @ 09:40
    Robert Foster
    0

    What are you inheriting from in your View?

    You could try something similar to what I've done - change your HttpPost handler in your controller to say StaffAbsenceQuoteFormHandler, then change your Http.BeginUmbracoForm to something like the following:

    using (Html.BeginUmbracoForm<namespace.StaffAbsenceQuoteFormSurface>("StaffAbsenceQuoteFormHAndler", null, new { @class = "custom" }))

    change namespace.StaffAbsenceQuoteFormSurface by replacing namespace so that it's fully qualified...

    And you can do

    return CurrentUmbracoPage();

    instead of

    return RedirectToCurrentUmbracoPage();

    in your Handler... Don't really have the time right now to look up the difference... what version of Umbraco are you using?

  • Shannon Deminick 1486 posts 4986 karma points hq
    Jan 18, 2013 @ 00:30
    Shannon Deminick
    6

    Hi guys, not sure if you've seen the current documentation on MVC forms here: http://our.umbraco.org/documentation/Reference/Mvc/forms. Much of the terms are explained in the inline code comments there.

    I will be updating this documentation with tutorials on the various ways of creating forms with MVC and Umbraco. It is nearly identical to creating forms in a regular MVC application, though I realize that some people may not be 100% familar with doing that either so will try to explain here breifly with some examples.

    There are a few different ways to create forms with MVC in Umbraco. For each of these different ways we are going to use a custom model defined as:

    public class TestFormModel
    {
        public string Name { get; set; }
    }

    And for all of these different ways we will always be using a SurfaceController class of 'TestSurfaceController' and posting values to an Action called 'PostVals' and using a Partial View to render our form. In my examples, that are working on my computer in 4.10 this partial view is located in ~/Views/Partials/Form.cshtml.

    The markup for the form looks like this (the form is auto-scaffold for brevity):

    @model TestFormModel
    
    @using (Html.BeginUmbracoForm<TestSurfaceController>("PostVals"))
    {
        if (TempData.ContainsKey("SuccessMessage"))
        {
            <strong>@TempData["SuccessMessage"]</strong>
        }
        @Html.EditorFor(x => Model)
        <input type="submit" />
    }

    The code for the 'PostVals' action looks like this (please read the inline comments to understand what it is doing):

    public ActionResult PostVals(TestFormModel model)
    {
        //For this zany test, we are going to force an invalid post
        //when the Name == 'showerror' (case insensitive)
        if (model.Name.InvariantEquals("showerror"))
        {
            ModelState.AddModelError("Name", "Invalid name");
        }       
        if (ModelState.IsValid)
        {
            //We are redirecting because it is valid, only TempData is available on redirects. 
            //Redirecting when a form submission is valid is standard web practice, unfortunately
            //Webforms by default doesn't follow these guidelines.
            //Show a success message.
            TempData.Add("SuccessMessage", "Form submitted successfully with name: " + model.Name);
            return RedirectToCurrentUmbracoPage();
        }
        else
        {
            //When a form submission is not valid, we will just return the current umbraco page
            //without redirecting, this will preserve the ViewData which includes the ModelState
            //with it's error collection.
            return CurrentUmbracoPage();
        }
    }

    #1. Partial view to render your form. This is probably the easiest and quickest. In your current page's view you would render your form using:

    @Html.Partial("Form", new TestFormModel())

    With all of the above already in place, this will 'just work'. 

    #2. Rendering a form using a child action. Sometimes you may want to do this if you need to pre-process or lookup an existing model that is used to render your form. This requires another action on our current TestSurfaceController which looks like this:

    [ChildActionOnly]
    public ActionResult RenderForm()
    {
        //TODO: Perhaps you want to lookup this model from a database or something
        // for this example, we'll pre-populate the name
        return PartialView("Form", new TestFormModel { Name = "Some guy" });
    }

    We use [ChildActionOnly] because this is only valid to be used as a ChildAction, if you don't do this then your action becomes publicly routable via a URL which you don't want. Also notice that you are returning a PartialView which is near identical to what we were doing above when rendering a partial view directly. 

    The next step is to render the form using a child action. We just do this:

    @Html.Action("RenderForm", "LocalSurface")

    With all of this implemented, the form will work as well as validation.

    In 6.0 we have a new Partial View Macro Engine which you could use to render a form and pass in macro parameters to it. You would still use one of the two above methods to achieve this. This will be part of the tutorials I will write up soon.

    I hope this helps shed some light on your issues.

    Shan

     

  • Robert Foster 393 posts 1459 karma points admin c-trib
    Jan 18, 2013 @ 03:45
    Robert Foster
    0

    Hi Shannon,

    Great post.  Originally I wanted to return the form data back to the form, but I've since reconsidered that requirement and have cut down the complexity of my code (by implementing the controller as you suggest).

    However, in some instances it is useful to be able to return the form data to the form on postback using the model, and using

    returnRedirectToCurrentUmbracoPage();

    won't work for that case as the form data is lost in the redirection.  My initial solution in those cases does exactly what I was after.

    Thanks,

    Rob.

  • Shannon Deminick 1486 posts 4986 karma points hq
    Jan 18, 2013 @ 22:31
    Shannon Deminick
    0

    Hi @Robert,

    I'm not sure what you are doing with your form data but assuming you are stroring it someplace. In many cases what would happen is you would submit the form, store the data in a database and then redirect to a page and include a query string with the ID of the record in the database. Then in your child action, you can check if an ID parameter was passed in and if so, go and load that record and pre-populate your form. That would work instead of maintaining your form data over a redirect. An alternative way to acheive this would be to store your model in TempData which is available after a redirect and then use this model to pre-populate your form. And another alternative is instead of using a query string you could store the ID in TempData and re-lookup the record based on that in your child action.

    Cheers,

    Shan

  • Robert Foster 393 posts 1459 karma points admin c-trib
    Jan 19, 2013 @ 00:15
    Robert Foster
    0

    Hi @Shannon,

    I'm not persisting the form post, just using it and discarding - so storing it in TempData would be the way to go - don't know why I didn't think of it earlier...

    Ta,

    Rob.

  • Shannon Deminick 1486 posts 4986 karma points hq
    Jan 21, 2013 @ 16:43
    Shannon Deminick
    0

    I've posted the first tutorial to the docs here:

    http://our.umbraco.org/documentation/Reference/Mvc/forms/turorial-partial-views

     

  • Adrian Inman 35 posts 59 karma points
    Jan 28, 2013 @ 11:20
    Adrian Inman
    1

    Tutorial is a start, but really needs to discuss how to deal with a post back too.

    I have a form and a surface controller, if things validate on input that's fine, but a serverside check is used to check for existing user. If one is found, I would rather have the current view change its display as appropriate. Using TempData is messy as this means it isn't possible to get back to the original location. The tutorial says to return CurrentUmbracoPage, however when I do this, I get an error message complaining: 

    Can only use UmbracoPageResult in the context of an Http POST when using a SurfaceController form

    Whis is both annoying and confusing as it is during an Http POST.

    I have marked my controller method with [HttpPost] but this doesn't seem to make a difference.

  • Adrian Inman 35 posts 59 karma points
    Jan 28, 2013 @ 11:23
    Adrian Inman
    0

    The only thing I can think is that I am bringing it together using @Html.Action(ViewName,ControllerName) is this the problem?

  • Adrian Inman 35 posts 59 karma points
    Jan 28, 2013 @ 11:39
    Adrian Inman
    0

    I think I'm getting somewhere. I have ensured I am following #2 in the instructions above. I have renamed my methods so I don't need to use the [ActionName] attribute and have added [ChildActionOnly] to both. However, I now get:

     

    The action 'MemberRegister' is accessible only by a child request.

     

    Help!

  • Adrian Inman 35 posts 59 karma points
    Jan 28, 2013 @ 11:43
    Adrian Inman
    0

    If I switch to using @Html.Partial to pull the view in, I get a different error message as it is upset my model isn't the right type. My view is strongly typed and I would prefer to keep it that way.

     

  • Adrian Inman 35 posts 59 karma points
    Jan 28, 2013 @ 12:31
    Adrian Inman
    0

    Hi Rob, I've done what you suggested about the Handler thing and ended up where I started. This is really frustrating now.

    It doesn't error when I return a PartialView, but all I get back is the partialview, no outer page content etc.

  • Robert Foster 393 posts 1459 karma points admin c-trib
    Jan 28, 2013 @ 13:04
    Robert Foster
    0

    @Shannon - just a small thing - you may want to amend the name of that content node - spelling mistake in the word Tutorial in the url (reads Turorial)

  • Edwin van Koppen 156 posts 270 karma points
    Mar 01, 2013 @ 12:04
    Edwin van Koppen
    0

    Got the same problem now

    Can only use UmbracoPageResult in the context of an Http POST when using a SurfaceController form

     

    [ChildActionOnly]
            public ActionResult FormContact() {
                return PartialView("ContactForm");
            }
            [HttpPost]
            public ActionResult FormContact(ContactForm Form) {
                if (!ModelState.IsValid) {
                    return CurrentUmbracoPage();
                }
                return RedirectToCurrentUmbracoPage();
            }

    If i run everything to a debugger i see that the page is not valid so it returns the CurrentUmbracoPage, but after that i goes again into the FormContact(ContactForm Form) again and not the FormContact() version.

  • Shannon Deminick 1486 posts 4986 karma points hq
    Mar 01, 2013 @ 14:35
    Shannon Deminick
    0

    @Edwin, what version are you using ? There's an issue fixed in 4.11.5 that might be related to your error : http://issues.umbraco.org/issue/U4-1537

    @Adrian, can you post a full example of what you are trying to acheive ?

  • Edwin van Koppen 156 posts 270 karma points
    Mar 01, 2013 @ 15:08
    Edwin van Koppen
    0

    Version 6.. do i need version 6.0.1? I already see it.. 6.0.1!

  • Edwin van Koppen 156 posts 270 karma points
    Mar 01, 2013 @ 15:25
    Edwin van Koppen
    0

    No still same error in 6.0.1

  • Shannon Deminick 1486 posts 4986 karma points hq
    Mar 01, 2013 @ 15:29
    Shannon Deminick
    2

    @Edwin can you try just changing your action name for your HttpPost action to something different like: PostFormContact and post to there

     

  • Edwin van Koppen 156 posts 270 karma points
    Mar 01, 2013 @ 16:09
    Edwin van Koppen
    0

    That did it @Shannon! Nice to have this finished for the weekend! Still for a action it is better to have this fixed, most people will name the actions the same..

  • Shannon Deminick 1486 posts 4986 karma points hq
    Mar 01, 2013 @ 16:10
    Shannon Deminick
    1

    @Edwin, yup now just need to figure out what was going on there. Are you able to log an issue on the tracker with steps to reproduce ?

  • Edwin van Koppen 156 posts 270 karma points
    Mar 01, 2013 @ 16:10
    Edwin van Koppen
    0

    How do you get a thank you message back with a return RedirectToCurrentUmbracoPage()?

  • Shannon Deminick 1486 posts 4986 karma points hq
    Mar 01, 2013 @ 16:11
    Shannon Deminick
    1

    TempData is available on redirects, that is pretty much why it exists.

  • Edwin van Koppen 156 posts 270 karma points
    Mar 01, 2013 @ 16:20
    Edwin van Koppen
    0

    Ah will search for that.. thank!

    And... http://issues.umbraco.org/issue/U4-1819 my first issue!

  • Mounhim Tahtahi 41 posts 62 karma points
    May 18, 2013 @ 17:28
    Mounhim Tahtahi
    0

    I am totally lost here and getting lost.

    It is stated that:

     

                    //When a form submission is not valid, we will just return the current umbraco page
                   
    //without redirecting, this will preserve the ViewData which includes the ModelState
                   
    //with it's error collection.
                   
    returnCurrentUmbracoPage();

     

    Two things: If I use currentUmbracoPage() my page comes back empty losing al the inputted data. The same goes for any created TempData or ViewBag for some feedback.

    I don't know why. Maybe someone can shed a light on my setup:

    Extension Project containing the following controller declaration:

        [PluginController("MyPlugin")]
        public class AccountSurfaceController : SurfaceController

     

    I have got the following Get Method

            [ChildActionOnly]
            [AllowAnonymous]
            public ActionResult ShowRegistrationForm()
            { return View("RegisterForm", new RegisterViewModel{ConfigurationCode = configurationCode});}
    //I tried also return PartialView. Both with same results.

     

    I have the following Post Method ( a bit abbreviated)

            [HttpPost]
            [AllowAnonymous]
            [NotChildAction]
            public ActionResult Register(RegisterViewModel model)
            {
                ViewBag.Errors = "Your form had errors and is not submitted";
                return CurrentUmbracoPage();
          }
    I have a template that has the following line:
    @Html.Action("ShowRegistrationForm","AccountSurface",new {area="MyPlugin"});
    And finally I have a view called RegisterForm with the form on it. The  header is:
    @inherits Umbraco.Web.Mvc.UmbracoViewPage
    And the form itself starts with the following tag:
    @using (Html.BeginUmbracoForm("Register", "AccountSurface","MyPlugin"))

     

    So in summary: I have a seperate Get and Post (different names). The form gets rendered nicely. The data is also posted to the correct method. But if I want to revert the user back to his entered data and maybe give some feedback in the ViewBag or TempData, the return CurrentUmbracoPage renders an empty form with empty ViewBag and ViewData.

    I hope some one can help...

     

    EDIT: By the way when debugging I see that the post is executed and the the get gets executed. When I am in the get I can see that the ModelState is not empty and contains the used values: (1) So would I have to read the Modelstate and refill the model and pass that on to the View? and (2) ViewBag and TempData are empty however. So still need help:)

    EDIT: Since the modelstate is persisted it looks like I cant use ViewBag and ViewData but must use ModelState.AddError to the ModelState. When getting into the Get Method after being in the Post Method, I have to read all values out of the modelstate, recompose my model and check for Modelstate errors to compose the ViewBag or TempData. I havent tried it, but this will work. However it seems quite un logical to me and a lot of coding.

    UPDATE: I might have made a huge mistake. At the top of my view I have knockout initialising the viewmodel. Guess what it alway instantiated when loading the page. Thus making the knockout viewmodel empty. That is the reason why I can't see my values on the screen. Which leaves me with only one item left to do. Provide feedback to the user. I will use ModelState and add an item to it. In the get I can check whether that is filled and passed on in the ViewBag and remove the key from the ModelState.

  • Shannon Deminick 1486 posts 4986 karma points hq
    May 21, 2013 @ 02:14
    Shannon Deminick
    0

    I think this post might help you:

    http://our.umbraco.org/forum/developers/api-questions/36614-411-Using-SurfaceController-Child-Action-with-Post?p=2

    From the sounds of it, it looks like you are rendering a ChildAction in your template, you then POST some information to your controller, re-render the Umbraco template which then re-renders your ChildAction. 

    When you add viewdata in your controller when you POST you are adding this to the 'main' controller (controlled by Umbraco). Then you are rendering a ChildAction (sub controller), for which has a new instance of it's own view data. If you want to access the view data for the 'main' controller from your 'sub' controller (or view) then you need to access it like:

    @ViewContext.ParentActionViewContext.ViewData["ErrorMessage"]

    Also, when you are returning a view from a ChildAction you should always be returning PartialView, not View as this affects where ViewEngine's look for your view files.

  • Mounhim Tahtahi 41 posts 62 karma points
    May 21, 2013 @ 08:32
    Mounhim Tahtahi
    0

    Hi Shannon,

    Thank you for the explanation. The ViewContext did indeed work for me. I also changed View into PartialView. I still have one question left:

    In regular MVC I could have in the post action something like:

    if (!serversidevalidations) {return View(myModel);}

    In that case the post action would rerender my view with model already populated.

    I guess return CurrentUmbracoPage() will not do that for me as you have stated that the umbraco template will be rerendered thus rerendering the get child action.

    Because at this moment I have in the get method checking for the ViewBag if it contains e.g. PostedWithErrors = true, if so I then recreate the model with the values from the ModelState.

    Another issue with this by the way is that View.ParentActionViewContext is not accessible from the Controller as it is non-static in static context. I am gonna look that one up.

    EDIT: Last line can be discarded. Instead of ParentActionViewContext I know use ControllerContext.ParentActionViewContext. Which works. And now I put the model in the post action in the ViewData["Model", model] if something went wrong. And in the get method I check for viewdata result. If it is there I then use model = ViewData["Model"] (with casting) and everything works nice. 

  • CodeCaster 6 posts 27 karma points
    May 30, 2013 @ 10:39
    CodeCaster
    1

    @Shannon: 

    > I'm not sure what you are doing with your form data but assuming you are stroring it someplace. In many cases what would happen is you would submit the form, store the data in a database and then redirect to a page and include a query string with the ID of the record in the database. 

    That is an invalid assumption. Like others here, we are trying to implement something like a search form, using an MVC application. We cannot and do not want to implement this search in Umbraco. We want to be able to:

    • Display an empty form (GET)
    • Display the filled form with validation errors (POST)
    • Display the filled form and the search results (POST)

    We would ultimately like to do this without redirecting, TempData/ViewData/ViewBag tricks, manually setting form fields and so on.

    Our template looks like this:

    @inherits UmbracoTemplatePage
    @{
        Layout = "~/Views/master.cshtml";
    }
    
    @Html.Action("Search", "SearchSurface")
    

    We have one view with an EditorTemplate for the criteria, and another view with a DisplayTemplate for the results. The Model class has a SearchCriteria and a SearchResults property, which makes our Search view look like this:

       
    @Html.EditorFor(m => m.SearchCriteria)
    @Html.DisplayFor(m => m.SearchResults)
    The SearchCriteria EditorTemplate contains an @using (Html.BeginUmbracoForm...) statement and the SearchResults DisplayTemplate contains, well, code to display search results.
    Now upon a POST of these criteria, our controller does a search and returns a new model where Model.SearchCriteria contains the POSTed (and corrected) search terms, while Model.SearchResults contains the results.
    We now want to display both the form and the results, through Umbraco (because the latter renders our site menu and so on), after a POST:
    • When we return View(model); we bypass Umbraco, so our master.cshtml-Layout fails to render (got passed a SearchModel, expects 'Umbraco.Web.Models.RenderModel'
    • When we return CurrentUmbracoPage();, which looks like what we need, the @Html.Action() line throws the error: "Can only use UmbracoPageResult in the context of an Http POST when using a SurfaceController form", because obviously it's requesting a GET method in a POST context. How to fix this?
    • When we return RedirectToCurrentUmbracoPage(); the ViewData dictionary is empty, so our form (and its validation errors) are empty. Also the Model.SearchResults are empty.
    The last one, RedirectToCurrentUmbracoPage(), unfortunately seems like the most feasible option, since it's about the only thing we can return without erroring, though it doesn't return a thing.We can put our SearchResults in the TempData, but how do we repopulate the form (rendered by @Html.Action()) after POST?
  • Shannon Deminick 1486 posts 4986 karma points hq
    May 31, 2013 @ 02:41
    Shannon Deminick
    0

    @CodeCaster:

    If you are returning an Umbraco view directly from your controller, then of course you will need to return the model that the view is expecting, that is just how MVC works. If you want to integrate Umbraco with your POSTed result then you need to use CurrentUmbracoPage when the data is invalid or RedirectToCurrentUmbracoPage when the data is valid. This is synonymous with MVC: use "return View()" for invalid data and "return RedirectToAction()" for valid data.

    The easiest way to perform a search is to use a query string as the search criteria and then you render the search results in a GET request and you can still validate the search terms using this method as well.

    However, if you really want to do a POST and then show search results from the result of a POST without redirecting to a page with a query strings (or simply just navigating to a page with the query string search terms), then you can use "return CurrentUmbracoPage()". Keep in mind that this will enable people to just press refresh on their browser and it will resubmit the POST. The way to acheive this:

    In your POST action you can add anything you want to the ViewData (or ViewBag) and this *will* be available in your view. It is very important to remember that if your view is the result of a ChildAction, then you need to access the ViewData by using ParentActionViewContext.ViewData... just like you would in normal MVC when rendering a ChildAction. You can then just pass the SearchResults model found in ViewData into a Partial View to render the results with a stronlgy typed model.

  • CodeCaster 6 posts 27 karma points
    May 31, 2013 @ 09:34
    CodeCaster
    0

    Shannon, thanks for your reply.

    Can you perhaps explain how to build a form that GETs to the current Umbraco URL? Because Html.BeginUmbracoForm() doesn't seem to have an overload with an HttpMethod parameter; it POSTs by default.

    Setting method="get" in the HtmlAttributes doesn't work (method="post" is still printed), and Html.BeginForm() puts an action like "/umbraco/Surface/SearchSurface/" which obviously bypasses Umbraco and thus does not render the layout we want.

    Edit: wait, is this MVC deprivation? A simple <form></form> will of course do that: default GET to the same page.

     

  • Shannon Deminick 1486 posts 4986 karma points hq
    May 31, 2013 @ 16:47
    Shannon Deminick
    0

    Its something that needs to be added to the API but you can still just use @Html.BeginForm with a GET to wire it up to your SurfaceController. 

  • CodeCaster 6 posts 27 karma points
    May 31, 2013 @ 16:48
    CodeCaster
    0

    Like I said that won't work:  Html.BeginForm() puts an action like "/umbraco/Surface/SearchSurface/" which obviously bypasses Umbraco and thus does not render the layout we want. The form action should be the same as the current Umbraco page URL, which MVC's Html.BeginForm() has no notion of. 

  • Shannon Deminick 1486 posts 4986 karma points hq
    Jun 01, 2013 @ 02:30
    Shannon Deminick
    0

    @CodeCaster... oh yeah sorry bout that :)

    Essentially all you really need to do is create a normal <form> element with the action of the same URL that is currently being rendered since it's just gonna do a redirect to the same page with query strings which is what you want.

  • Jacob 41 posts 90 karma points
    Jan 15, 2014 @ 19:23
    Jacob
    0

    Hello all.  I've been experiencing issues similar to those who've posted in this thread previously.  I continually receive the error "Can only use UmbracoPageResult in the context of an Http POST when using a SurfaceController form" whenever I attempt to return RedirectToCurrentUmbracoPage() or CurrentUmbracoPage().

    None of the prior recommended solutions have worked in my environment.  I'm also using the latest version of Umbraco, I believe version 7.

    Basically, I'm making a user login form.  My surface controller looks like this:

     

            public ActionResult Index()

            {

                return PartialView("_UserLoginPartial",new UserLoginViewModel());

            }

            [HttpPost]

            public ActionResult Index(UserLoginViewModel ulvm, FormCollection fc)

            {

    //validation will go here

                return CurrentUmbracoPage();

            }

     

     

    My login form is a partial view that looks like this:

    @using (Html.BeginUmbracoForm<UserLoginSurfaceController>("Index"))

    {

    @Html.EditorFor(m => m.ExampleModelProperty)

    // etc...

    }

     

    I'm calling the Action to render this partial view from an Umbraco Macro Partial that looks like this (this macro is called on my master content template for the Home page):

    @inherits Umbraco.Web.Macros.PartialViewMacroPage

    @Html.Action("Index", "UserLoginSurface")

     

    I've managed to get Umbraco to GET and POST to the correct ActionResults; however, I'll need to redirect the user to a login_successful page or reload the home page based on whether or not the user's login information was correct.  As I understand, to do this I'll need to send the user back to an Umbraco content page so the process can repeat itself. It seems like Umbraco is attempting to perform the redirect from within my partial view as opposed to directly from the controller, which causes an error in the partial view and ultimately the Home page reloads with an error for the Umbraco Macro Partial.

    I'd prefer not to implement a workaround using AJAX from a partial view to redirect back to an Umbraco content page, because I know the Umbraco methods should work from my controller.

    Please advise, as I've been working with Umbraco for a few months now and I've found the learning curve incredibly frusterating.

  • Shannon Deminick 1486 posts 4986 karma points hq
    Jan 16, 2014 @ 01:51
    Shannon Deminick
    0

    Hi, there's been other posts about this issue. You have 2 actions with the same name but since you are rendering one using a Child Action, MVC get's confused. Also, for any Child Action you are rendering you must attribute it correctly otherwise it becomes publicly routable via a URL which you don't want

    Since you are rendering a partial view from here, I'm assuming this is a child action, you need to attribute it as follows:

    [ChildActionOnly]
    public ActionResult Index()
    

    Then because both of your actions have the same name and you are posting from the Child Action MVC gets confused as to where it's going so in this circumstance you need to attribute your other action like this:

    [HttpPost]
    [NotChildAction]
    public ActionResult Index(UserLoginViewModel ulvm, FormCollection fc)
    

    There's documentation about this here:

    http://our.umbraco.org/documentation/Reference/Mvc/forms/turorial-child-action http://our.umbraco.org/documentation/Reference/Mvc/child-actions

  • Shannon Deminick 1486 posts 4986 karma points hq
    Jan 16, 2014 @ 01:51
  • Jacob 41 posts 90 karma points
    Jan 17, 2014 @ 14:38
    Jacob
    0

    Thanks, Shannon!  I'll give it a shot and let you know how it works.

  • Jacob 41 posts 90 karma points
    Jan 17, 2014 @ 15:44
    Jacob
    0

    Hi Shannon.  I really appreciate your input.  Unfortunately, your advice was not successful in my environment.  In an effort to keep my issue consolidated to a single thread, I believe I've best outlined my issue in the following thread:

    http://our.umbraco.org/forum/developers/api-questions/38662-(v6)-Could-not-find-a-Surface-controller-route-error?p=1#comment170069

     

     

    Please review and reply at your next convenience.  I anticipate your feedback.

  • Mark 255 posts 612 karma points
    Jan 22, 2014 @ 18:49
    Mark
    1

    I was also getting the dreaded YSOD message:

    Can only use UmbracoPageResult in the context of an Http POST when using a SurfaceController form

    I followed the Form Child Action documentation to the letter. I was pulling my hair out, tinkering everywhere. Then I stumbled onto a solution.

    If the DocumentTypeAlias is the same as the name of the surface controller (minus Controller) you get the error. E.g. DocumentTypeAlias = "CvBuilder" and surface controller named "CvBuilderController" you will always get the error when posting the form. Change one character on either and posting works.

    I know this may not happen for many people (especially if your surface controller ends "...SurfaceController") but I thought I'd mention it here in case anyone else runs into it in the future (including me!)

  • Mark 255 posts 612 karma points
    Jan 22, 2014 @ 18:52
    Mark
    0

    Forgot to mention, I am using Umbraco version 6.1.5

  • Jacob 41 posts 90 karma points
    Jan 22, 2014 @ 18:54
    Jacob
    0

    Hi, Mark.  I double checked my work and the document type alias of my Home content page is entitled "Home" while my surface controller action is entitled "Index".  I don't believe that to be the issue in my scenario.  Thanks for the input though!

     

    edit:

    My surface controller is actually called UserLoginSurfaceController.  But I'm using a basic "Home" document type, and the action result I'm trying to post back to is called Index.

  • Mark 255 posts 612 karma points
    Jan 22, 2014 @ 18:57
    Mark
    0

    @Jacob, you've slightly misunderstood me. It's not the name of the action, it's the name of the surface controller

    Is your surface controller called HomeController? If so, try changing it to HomeSurfaceController, or even HomeXController :-)

  • Jacob 41 posts 90 karma points
    Jan 22, 2014 @ 18:58
    Jacob
    0

    Sorry about the confusion, Mark.  I updated my response.

  • Mark 255 posts 612 karma points
    Jan 22, 2014 @ 19:00
    Mark
    0

    No worries. Just a thought. Might be worth @shannon fixing it or mentioning it as an issue in the documentation... would have saved me several hours...

  • Jacob 41 posts 90 karma points
    Jan 22, 2014 @ 19:06
    Jacob
    0

    Understandable. I've also spent several hours/days going through Umbraco documentation, tutorials, and videos only to find they do not work in my environment, which is currently Umbraco 7.

  • Shannon Deminick 1486 posts 4986 karma points hq
    Jan 22, 2014 @ 23:02
    Shannon Deminick
    0
  • Mark 255 posts 612 karma points
    Jan 23, 2014 @ 09:45
    Mark
    0

    Ahh, thanks @shannon. As the controller was inheriting from SurfaceController rather than RenderMvcController I didn't connect the two. Must be a routing thing...

  • Damian Green 452 posts 1433 karma points
    Jan 29, 2014 @ 11:33
    Damian Green
    0

    I have also had this issue and it turned out to be because i attributed the action for the GET with a [HttpGet].

    When you post the form it routes to the POST Action and then when the return current umbraco page fires the request comes through to the surface controller again but it is still a POST but doesnt match the POST action in the surface controller so it couldnt find a valid action result.  

    Once i removed the HttpGet it worked. Also if i added ChildActionOnly to the GET that worked also.

    Still a bit confused.

    There must be things go on in umbraco after the Child action fires that maps the model back to the one that was posted because a new model is created in the GET method that gets fired.  But on returning to the page the properties that were posted are on the model.

    Sorry if this is confusing - but ive been confused as to what was going on.  I've been using surface controllers for quite a while and not hit this before so was a little odd! 

     

     

  • Damian Green 452 posts 1433 karma points
    Feb 17, 2014 @ 16:46
    Damian Green
    1

    Also like to add..

    Ensure you call your GET/ChildActionOnly and your POST methods differently!  I had Index for them both and got the UmbracoPageResult error.

     

  • Bendik Engebretsen 104 posts 196 karma points
    Jan 18, 2017 @ 17:42
    Bendik Engebretsen
    0

    Damian is right, the problem occurs when your GET and POST actions have the same name. I renamed my POST actions to Do.... and finally got rid of the "Can only use UmbracoPageResult..." YSOD.

  • Jacob 41 posts 90 karma points
    Feb 17, 2014 @ 16:54
    Jacob
    0

    I worked around this by hijacking the Umbraco routes to specific action results within my surface controller whenever post-backs are needed.  I executed these post-backs through a JQuery trigger.

  • Tom 711 posts 948 karma points
    Mar 07, 2014 @ 02:10
    Tom
    0

    Any chance there's a scenario where you can use the ajax.beginform stuff built in to mvc and still get the umbraco current page? otherwise is it a matter of manually intercepting the form submit and posting using ajax?

  • Shannon Deminick 1486 posts 4986 karma points hq
    Mar 07, 2014 @ 02:14
    Shannon Deminick
    0

    Just like all ajax requests, they are stateless, they don't know what page is currently rendering (how would they!?)

    You just need to pass in the current page ID to your ajax action and go lookup that page using the UmbracoHelper.

  • Tom 711 posts 948 karma points
    Mar 10, 2014 @ 03:08
    Tom
    0

    What I ended up doing was use BeginUmbracoForm but append the appropriate data attributes that a usual ajax.beginform would append to get the right ajax behaviour. I then got the surface controller to send back a JsonResponse and that adds the appropriate hidden field to then use CurrentPage:

     

     @using(Html.BeginUmbracoForm<SignUpController>("Register",FormMethod.Post,new{@id="sign-up-form",@class="form quick-contact", data_ajax ="true", data_ajax_failure ="SignUpForm.showError(data)", data_ajax_loading ="#contact-load", data_ajax_method ="POST", data_ajax_complete ="SignUpForm.complete"}))
Please Sign in or register to post replies

Write your reply to:

Draft