Copied to clipboard

Flag this post as spam?

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


  • Kevin Farrugia 22 posts 54 karma points
    Aug 17, 2013 @ 21:54
    Kevin Farrugia
    0

    MVC actions in Umbraco 6.10+

    Hi all,

    Hope you're all doing great.

    I have recently started experimenting with Umbraco v6 but I'm having difficult getting even the simplest things to work. I have been searching the forums and the internet and I'm not managing to find what I'm looking for, so I'm not sure if Umbraco v6 with MVC is still not widely used or if my problems are so basic (or wierd) no one writes about them. Anyway, here goes...

    In one line: I am trying to handle an action on a custom controller for which there doesn't exist an Umbraco document type. What does this mean? Usually when using a custom controller, such as my custom controller HomeController : Umbraco.Web.Mvc.RenderMvcController; Umbraco matches the document Home with the HomeController, therefore if I have another document type Contact, I could also have another custom controller named ContactController, which inherits RenderMvcController as above. This I have managed to work with and succeeded in creating a contact page, a home page and others. The brick wall I have encountered is when I have a partial view for which there does not exist an Umbraco document type. Suppose all my pages also has a textbox where the user could input their e-mail address to be added onto the mailing list. In ordinary MVC, we would have an HTML.BeginForm("Newsletter", "Home") and in the HomeController we would have the Newsletter action which would handle the user's HttpPost.

    How should this be implemented in Umbraco? From what I have understood, it is required that a document type (per controller) and template (per action) are required; which wouldn't make any sense in this case.

     

    Thank you in advance for your input,


    Kevin Farrugia

    http://www.incredible-web.com

  • Charles Afford 1163 posts 1709 karma points
    Aug 17, 2013 @ 21:57
    Charles Afford
    0

    What you appear to be referring to there is route hijacking (GET) action before the view/ partial is rendered.  I think you are trying to get to the (POST) action?  Is this correct.  I will try and help :).  Charlie

  • David Brendel 792 posts 2970 karma points MVP 3x c-trib
    Aug 17, 2013 @ 23:03
    David Brendel
    0

    I would suggest you to have a look at surface controllers.

    These are exactly what you want. With them you can create forms and handle get for partial views and also post to handle form data.

    Also if you want to create forms with umbraco then use Html.BeginUmbracoForm instead of the normal Html.BeginForm.

     

    Hope i could help.

  • Charles Afford 1163 posts 1709 karma points
    Aug 17, 2013 @ 23:17
    Charles Afford
    0

    Hi David what do you mean here?  handle the get for the partial view?  

    I would not use the HTML.BeginUmbracoForm it adds complexity using Html.BeginForm is fine.  Also you could use Ajax. 

    Have a look at http://our.umbraco.org/documentation/Reference/Mvc/surface-controllers

    It really feels like you are trying to do Route Hijacking and you want to doing a post to a controller action

  • Kevin Farrugia 22 posts 54 karma points
    Aug 17, 2013 @ 23:38
    Kevin Farrugia
    0

    Hi guys,

    Thanks to both of you for answering so quickly! :)

    In regards to BeginUmbracoForm vs BeginForm, I had initally started using BeginUmbracoForm however when using custom models I felt things where getting more complicated than they should be and after some reading around I understood that they are equally usable. (in fact on other pages I am working perfectly well with BeginForm) =)

    I had also used the surface controllers when I had a contact form, where the whole page was being submitted and the page has a representation in Umbraco in the form of a template and document type. However when I wanted to submit a partial view, I wasn't managing to get my head around it. If I was to use the SurfaceControllers, how would my partial view look in order to post to the SurfaceController?

    In ordinary MVC (not Umbraco) I would have something of the sort which would post to the Newsletter action on the Home controller:

        @Html.BeginForm("Newsletter","Home"){
            <input name="honeypot" type="text" class="honeypot" />    
            <ul>
                <li>
                    @Html.TextBoxFor(m => m.Name, new Dictionary<string, Object> { { "placeholder", "Your Name" } })</li>
                <li>
                    @Html.TextBoxFor(m => m.Email, new Dictionary<string, Object> { { "placeholder", "Your Email Address" } })</li>
            </ul>    
            <input type="submit" value="Submit" />
        }

     

    From the bit that I know, I want to do Route Hijacking and have followed something similar to the bottom section of this article (http://our.umbraco.org/documentation/reference/mvc/custom-controllers) however I am not managing and my understanding is that there has to be a document type and template corresponding to the action you wish to post to.

    Thanks again,

    Kevin

  • Charles Afford 1163 posts 1709 karma points
    Aug 17, 2013 @ 23:53
    Charles Afford
    0

    Your partialview would look like a view as in your post to the contoller action would be the same.  its just in the controller action you would post back to the partialView rather than the view.

    Could you tell me what functinoality you are trying to achive on your webpage.  I may be better just to use ajax and only rerender the partial bit of the page.  :).  Charlie

  • Kevin Farrugia 22 posts 54 karma points
    Aug 18, 2013 @ 00:35
    Kevin Farrugia
    0

    Charlie,

    I am trying to do a newsletter partial view which is visible on ALL pages. Submitting this little form (just contains the e-mail address field) will store the data in the Umbraco members and then redirect to a thank you page.

    I agree that it's probably easier using AJAX because I could just post to the URL I wish and submit the data I wish (I usually use $.post instead of @Ajax.BeginForm), however I believe in progressive enhancement and always build websites that work without JavaScript first.

    I will continue experimenting, because it's possible that i'm worn out and not thinking straight, but this should be something very straighforward with ordinary MVC.

    Thanks

    Kevin 

  • Charles Afford 1163 posts 1709 karma points
    Aug 18, 2013 @ 01:39
    Charles Afford
    0

    Yea just create a partial and call it in your views.  $.post?  Why not just use @Ajax.beginForm?  I am sure i am missing something, it just seems you are making things 1000000000 x more complicated than they neec to be?  Charlie.

  • Charles Afford 1163 posts 1709 karma points
    Aug 18, 2013 @ 01:41
    Charles Afford
    0

    All you need is a partial that will be your form

    Call the partial from your views.

    Use AJAX POST to post to your controller action, do some logic in a class somewhere with your form data and return result to your controller action

    based on result post back result to your partial view

    render the result

    (You will need a property for your UmbracoNode ID and Node becuase when you post back using AJAX in a partial you lose the Umbraco context.

    Charlie :)

  • Kevin Farrugia 22 posts 54 karma points
    Aug 20, 2013 @ 19:28
    Kevin Farrugia
    0

    Thanks for your help Charles and sorry I didn't reply earlier! :)

    What about without AJAX, I am using Html.BeginForm and would like to post to a controller an action that do not exist as a document type/template in Umbraco. The html renders an empty action attribute. Using AJAX is cutting corners in my opinion if you don't have the basics working perfectly and considering this is my first website using Umbraco 6, I want to make sure I have understood how everything works. :)

    <form action="" method="post">
    </form>

    In fact, you would get this to correctly write an action by using the RenderMvc controller:

    @using (Html.BeginForm("Newsletter", "RenderMvc"))

    which obviously is not what I am looking for since I want my controller to handle the action. :)

  • Kevin Farrugia 22 posts 54 karma points
    Aug 23, 2013 @ 01:26
    Kevin Farrugia
    0

    The problem was that in the global although I was doing everything seemingly correct, I had to change the Inherits to my namespace instead of Umbraco's:

    <%@ Application Codebehind="Global.asax.cs" Inherits="MyApplication.MvcApplication" Language="C#" %>

     

    Ultimately there were other minor bugs which were causing me to not to be able to debug correctly (and not notice what was actually wrong since I had even read about this 'issue') but now it's all plain sailing, as you said, using a SurfaceController. :)

     

    Thanks again Charles & David.

  • Kevin Farrugia 22 posts 54 karma points
    Aug 29, 2013 @ 19:28
    Kevin Farrugia
    0

    Unfortunately I am still not done with this issue and I am slightly dissapointed that something quite straightforward has taken me so much time. =/

    I have followed the OneContact example (http://umbraco.com/follow-us/blog-archive/2013/7.aspx) to create a contact form, and have been successful. This part was straightforward, as the contact form exists both as a Template and a Partial. This means that to post the contact form. you need to be on the page

    http://localhost/contact/

    Which is perfectly fine if you are doing a contact us page. However, one of the great advantages of MVC partials is that, similarly to user controls; they could be used as part of another view. Therefore if the contact partial was included on another page (for example on the homepage: http://www.localhost/), submitting the form would work correctly ONLY if you return redirect; meaning that whilst 

    return RedirectToCurrentUmbracoPage();

    would work; if you want to maintain the ViewData (and ModelState...etc) and use:

    return CurrentUmbracoPage();

    you would end up with a blank page (similarly to what would happen if you delete the view).

    What am I doing wrong? 


    Thanks as always for your help,

    Kevin

  • Charles Afford 1163 posts 1709 karma points
    Aug 29, 2013 @ 19:37
    Charles Afford
    0

    Out of intrest how come you are mixing Razor markup with webforms?  Do you not want to go down MVC route?  Charlie.

  • Charles Afford 1163 posts 1709 karma points
    Aug 29, 2013 @ 19:39
    Charles Afford
    0

    Just got a notification about a post here but the post has gone? 

  • Kevin Farrugia 22 posts 54 karma points
    Aug 29, 2013 @ 19:45
    Kevin Farrugia
    0

    Hi Charles,

    I don't know why the post has gone to be honest. If you could kindly paste it here please so that others would be able to follow (I didn't keep a copy).

    I am using only Razor and want a full MVC solution. I used the OneContact example because it's easily available for anyone else. :)

    Is it possible I contact you privately through Skype or Gmail chat...etc?

    Thanks

    Kevin 

  • Kevin Farrugia 5 posts 25 karma points
    Aug 29, 2013 @ 19:48
    Kevin Farrugia
    0

    Hi Charles,

    I don't know why the post has gone to be honest. If you could kindly paste it here please so that others would be able to follow (I didn't keep a copy).

    I am using only Razor and want a full MVC solution. I used the OneContact example because it's easily available for anyone else. :)

    Is it possible I contact you privately through Skype or Gmail chat...etc?

    Thanks

    Kevin 

  • Charles Afford 1163 posts 1709 karma points
    Aug 29, 2013 @ 19:49
    Charles Afford
    0

    Sure if you email me @[email protected]. We can skype and screen share.

    Unfortunately I am still not done with this issue and I am slightly dissapointed that something quite straightforward has taken me so much time. =/

    I have followed the OneContact example (http://umbraco.com/follow-us/blog-archive/2013/7.aspx) to create a contact form, and have been successful. This part was straightforward, as the contact form exists both as a Template and a Partial.

    This means that to post the contact form. you need to be on the page http://localhost/contact/ Which is perfectly fine if you are doing a contact us page. However, one of the great advantages of MVC partials is that, similarly to user controls; they could be used as part of another view.

    Therefore if the contact partial was included on another page (for example on the homepage: http://www.localhost/), submitting the form would work correctly ONLY if you return redirect; meaning that whilst return RedirectToCurrentUmbracoPage(); would work; if you want to maintain the ViewData (and ModelState...etc) and use: return CurrentUmbracoPage(); you would end up with a blank page (similarly to what would happen if you delete the view).

    What am I doing wrong?

    Thanks as always for your help, Kevin

  • Charles Afford 1163 posts 1709 karma points
    Aug 29, 2013 @ 20:00
    Charles Afford
    0

    The biggest problem is that i am posting things and advice and you are not listening/implementing (sorry to be blunt)

    Again.

    DO NOT USE THESE

    CurrentUmbracoPage() and RedirectToCurrentUmbracoPage()

    DO USE

    Standard MVC to return to partial or View (if your post is from partial then return to partial)

    You need to use AJAX if you want to reload a partial view in the context of a view and maintain state

    So instead of @using (Html.BeginForm... use @using (AJAX.BeginForm)

    Does that make sense. I think these will help you with the problems you are having :)

    Please tell me if i am wrong or you have more problems

  • Charles Afford 1163 posts 1709 karma points
    Aug 29, 2013 @ 20:02
    Charles Afford
    0

    Email me [email protected].

    We can skype/screen share and i will help you :)

  • Kevin Farrugia 22 posts 54 karma points
    Aug 30, 2013 @ 00:51
    Kevin Farrugia
    0

    So first of all I want to thank Charles for his patience and help.

    I have finally found a solution which keeps me happy (one of my requirements was that the form could be submitted without JavaScript, hence without using AJAX). Using AJAX facilitates the process, however although it would work, it wasn't 100% what I was looking for.

    Now my working solution utilizes the BeginUmbracoForm in the Partial, therefore we have something of the sort:

    @using (Html.BeginUmbracoForm<OneContact.Controllers.NewsletterController>("Index"))
        {        
          <ul>
                <li>
                    @Html.TextBoxFor(m => m.Name, new Dictionary<string, Object> { { "placeholder", "Your Name" } })</li>
                <li>
                    @Html.TextBoxFor(m => m.Email, new Dictionary<string, Object> { { "placeholder", "Your Email Address" } })</li>
            </ul>    
            <input type="submit" value="Submit" />
        }

    According to the route hijacking which I have in place, this will post to my SurfaceController NewsletterController. This is where I was encountering most difficulty, because the Newsletter Partial could be appear on any page, yet I wanted to preserve the Umbraco content. Using

    return CurrentUmbracoPage()

    was returning a blank page, because the Newsletter did not have a representation in Umbraco in the form of a Template. Whilst using 

    return RedirectToCurrentUmbracoPage()

    was successfully reloading the correct page from where the form was submitted (remember this could be submitted from any page on the site), however it was not preserving the ViewData (since it is a redirect). Finally I came up with the following:

                if (!ModelState.IsValid)
                {
                    // reload the view with the same IPublishedContent
                    if (TempData["ViewPath"] != null)
                    {
                        return View((string)TempData["ViewPath"], CurrentPage);
                    }
     
                    // fallback uses redirect (losing the ViewData)
                    return RedirectToCurrentUmbracoPage();
                }
     
                // redirect to the thank you page
                return Redirect("~/thank-you");

    However one more thing, how is the TempData["ViewPath"] populated? Simple, in the Views to which we would want to return, such as the Home, Contact, Sitemap...etc (not Partials) we include the following line:

    @{TempData["ViewPath"] = @Html.ViewVirtualPath()}

    which in turn uses the helper as explained on: http://stackoverflow.com/questions/14062257/mvc-3-how-to-tell-what-view-a-controller-action-is-being-called-from/18522059#18522059

     

    Hope it's clear and hope it will prove useful for someone else.

    Kevin

  • Kevin Farrugia 22 posts 54 karma points
    Aug 30, 2013 @ 00:54
    Kevin Farrugia
    0

    So first of all I want to thank Charles for his patience and help.

    I have finally found a solution which keeps me happy (one of my requirements was that the form could be submitted without JavaScript, hence without using AJAX). Using AJAX facilitates the process, however although it would work, it wasn't 100% what I was looking for.

    Now my working solution utilizes the BeginUmbracoForm in the Partial, therefore we have something of the sort:

    @using (Html.BeginUmbracoForm<OneContact.Controllers.NewsletterController>("Index"))
        {        
          <ul>
                <li>
                    @Html.TextBoxFor(m => m.Name, new Dictionary<string, Object> { { "placeholder", "Your Name" } })</li>
                <li>
                    @Html.TextBoxFor(m => m.Email, new Dictionary<string, Object> { { "placeholder", "Your Email Address" } })</li>
            </ul>    
            <input type="submit" value="Submit" />
        }

    According to the route hijacking which I have in place, this will post to my SurfaceController NewsletterController. This is where I was encountering most difficulty, because the Newsletter Partial could be appear on any page, yet I wanted to preserve the Umbraco content. Using

    return CurrentUmbracoPage()

    was returning a blank page, because the Newsletter did not have a representation in Umbraco in the form of a Template. Whilst using 

    return RedirectToCurrentUmbracoPage()

    was successfully reloading the correct page from where the form was submitted (remember this could be submitted from any page on the site), however it was not preserving the ViewData (since it is a redirect). Finally I came up with the following:

                if (!ModelState.IsValid)
                {
                    // reload the view with the same IPublishedContent
                    if (TempData["ViewPath"] != null)
                    {
                        return View((string)TempData["ViewPath"], CurrentPage);
                    }
     
                    // fallback uses redirect (losing the ViewData)
                    return RedirectToCurrentUmbracoPage();
                }
     
                // redirect to the thank you page
                return Redirect("~/thank-you");

    However one more thing, how is the TempData["ViewPath"] populated? Simple, in the Views to which we would want to return, such as the Home, Contact, Sitemap...etc (not Partials) we include the following line:

    @{TempData["ViewPath"] = @Html.ViewVirtualPath()}

    which in turn uses the helper as explained on: http://stackoverflow.com/questions/14062257/mvc-3-how-to-tell-what-view-a-controller-action-is-being-called-from/18522059#18522059

     

    Hope it's clear and hope it will prove useful for someone else.

    Kevin

  • Kevin Farrugia 22 posts 54 karma points
    Aug 30, 2013 @ 00:56
    Kevin Farrugia
    0

    So first of all I want to thank Charles for his patience and help.

    I have finally found a solution which keeps me happy (one of my requirements was that the form could be submitted without JavaScript, hence without using AJAX). Using AJAX facilitates the process, however although it would work, it wasn't 100% what I was looking for.

    Now my working solution utilizes the BeginUmbracoForm in the Partial, therefore we have something of the sort:

    @using (Html.BeginUmbracoForm

    • @Html.TextBoxFor(m => m.Name, new Dictionary
    • @Html.TextBoxFor(m => m.Email, new Dictionary

    } According to the route hijacking which I have in place, this will post to my SurfaceController NewsletterController. This is where I was encountering most difficulty, because the Newsletter Partial could be appear on any page, yet I wanted to preserve the Umbraco content. Using

    return CurrentUmbracoPage() was returning a blank page, because the Newsletter did not have a representation in Umbraco in the form of a Template. Whilst using

    return RedirectToCurrentUmbracoPage() was successfully reloading the correct page from where the form was submitted (remember this could be submitted from any page on the site), however it was not preserving the ViewData (since it is a redirect). Finally I came up with the following:

            if (!ModelState.IsValid)
            {
                // reload the view with the same IPublishedContent
                if (TempData["ViewPath"] != null)
                {
                    return View((string)TempData["ViewPath"], CurrentPage);
                }
    
                // fallback uses redirect (losing the ViewData)
                return RedirectToCurrentUmbracoPage();
            }
    
            // redirect to the thank you page
            return Redirect("~/thank-you");
    

    However one more thing, how is the TempData["ViewPath"] populated? Simple, in the Views to which we would want to return, such as the Home, Contact, Sitemap...etc (not Partials) we include the following line:

    @{TempData["ViewPath"] = @Html.ViewVirtualPath()} which in turn uses the helper as explained on: http://stackoverflow.com/questions/14062257/mvc-3-how-to-tell-what-view-a-controller-action-is-being-called-from/18522059#18522059

    Hope it's clear and hope it will prove useful for someone else.

    Kevin

  • Kevin Farrugia 5 posts 25 karma points
    Aug 30, 2013 @ 00:58
    Kevin Farrugia
    0

    So first of all I want to thank Charles for his patience and help.

    I have finally found a solution which keeps me happy (one of my requirements was that the form could be submitted without JavaScript, hence without using AJAX). Using AJAX facilitates the process, however although it would work, it wasn't 100% what I was looking for.

    Now my working solution utilizes the BeginUmbracoForm in the Partial, therefore we have something of the sort:

    @using (Html.BeginUmbracoForm<OneContact.Controllers.NewsletterController>("Index"))
        {        
          <ul>
                <li>
                    @Html.TextBoxFor(m => m.Name, new Dictionary<string, Object> { { "placeholder", "Your Name" } })</li>
                <li>
                    @Html.TextBoxFor(m => m.Email, new Dictionary<string, Object> { { "placeholder", "Your Email Address" } })</li>
            </ul>    
            <input type="submit" value="Submit" />
        }

    According to the route hijacking which I have in place, this will post to my SurfaceController NewsletterController. This is where I was encountering most difficulty, because the Newsletter Partial could be appear on any page, yet I wanted to preserve the Umbraco content. Using

    return CurrentUmbracoPage()

    was returning a blank page, because the Newsletter did not have a representation in Umbraco in the form of a Template. Whilst using 

    return RedirectToCurrentUmbracoPage()

    was successfully reloading the correct page from where the form was submitted (remember this could be submitted from any page on the site), however it was not preserving the ViewData (since it is a redirect). Finally I came up with the following:

                if (!ModelState.IsValid)
                {
                    // reload the view with the same IPublishedContent
                    if (TempData["ViewPath"] != null)
                    {
                        return View((string)TempData["ViewPath"], CurrentPage);
                    }
     
                    // fallback uses redirect (losing the ViewData)
                    return RedirectToCurrentUmbracoPage();
                }
     
                // redirect to the thank you page
                return Redirect("~/thank-you");

    However one more thing, how is the TempData["ViewPath"] populated? Simple, in the Views to which we would want to return, such as the Home, Contact, Sitemap...etc (not Partials) we include the following line:

    @{TempData["ViewPath"] = @Html.ViewVirtualPath()}

    which in turn uses the helper as explained on: http://stackoverflow.com/questions/14062257/mvc-3-how-to-tell-what-view-a-controller-action-is-being-called-from/18522059#18522059

     

    Hope it's clear and hope it will prove useful for someone else.

    Kevin

Please Sign in or register to post replies

Write your reply to:

Draft