Copied to clipboard

Flag this post as spam?

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


  • Lars Dupont 3 posts 93 karma points
    Feb 16, 2018 @ 11:21
    Lars Dupont
    0

    How do I implement two SUBMIT buttons (or more in the same partial view) hitting the same Surface controller?

    Hi everybody. I'm a rookie to MVC 5 and Umbraco 7 still on my first week and my first project. Here I ran into a issue ... I'll try to explain myself.

    I have a single partial view in which I can submit some data - which works fine. Now, the problem is that I need to extend my business logic and provide the ability to delete the very same object? Here is some code snippet:

    My Controller looks like this ....

    public class AddressSurfaceController : SurfaceController
    {
        public ActionResult RenderForm(Address dto)
        {
            return PartialView($"~/Views/Partials/Customer/_AddressSurface.cshtml", dto);
        }
    
        [HttpPost]
        public ActionResult SubmitForm(Address dto)
        {
            if (!ModelState.IsValid)
            {
                return CurrentUmbracoPage();
            }
            if (dto != null)
            {
                new CustomerRepository().SaveAdressDto(dto);
            }
            return RedirectToCurrentUmbracoPage();
        }
    
        [HttpPost]
        public ActionResult Submit(Address dto)
        {
            if (!ModelState.IsValid)
            {
                return CurrentUmbracoPage();
            }
            if (dto != null)
            {
                new CustomerRepository().SaveAdressDto(dto);
            }
            return RedirectToCurrentUmbracoPage();
        }
    
        [HttpPost]
        public ActionResult Delete(Address dto)
        {
            if (!ModelState.IsValid)
            {
                return CurrentUmbracoPage();
            }
            if (dto != null)
            {
                new CustomerRepository().DeleteAddressDto(dto);
            }
            return RedirectToCurrentUmbracoPage();
        }
    }
    

    And my Partial View looks like this (some lines delete for better overview) ....

    @using IKAS.Mainsite.Website.Models.Customers
    @inherits UmbracoViewPage<Address>
    @using (Html.BeginUmbracoForm("SubmitForm", "AddressSurface", "Test", FormMethod.Post))
    {
        <table>
            <tr style="height: 0; visibility: hidden">
                <td>@Html.TextBoxFor(m => m.Id)</td>
            </tr>
            <tr style="height: 0; visibility: hidden">
                <td>@Html.TextBoxFor(m => m.CustomerId)</td>
            </tr>
            <tr>
                <td>
                    <table>
                        <tr>
                            <td>@Html.Label("Postnr")</td>
                            <td>@Html.Label("By")</td>
                            <td></td>
                        </tr>
                        <tr>
                            <td>@Html.TextBoxFor(m => m.ZipCode, new { style = "width:50px;" })</td>
                            <td>@Html.TextBoxFor(m => m.City, new { style = "width:190px;" })</td>
                            <td>@Html.ValidationMessageFor(m => m.ZipCode)@Html.ValidationMessageFor(m => m.City)</td>
                        </tr>
                    </table>
                </td>
            </tr>
            <tr>
                <td>
                    <br />
                    <p>
                        <input type="submit" formaction=@Url.Action("Submit") formmethod="post" class="btn btn-primary" />
                        <input type="submit" formaction=@Url.Action("Delete") formmethod="post" class="btn btn-danger" />                    
                        <button>submit</button>
                    </p>
                </td>
            </tr>
        </table>
    }
    

    Now, the last submit button actually hits the public ActionResult SubmitForm(Address dto) as expected :-)

    But if I try one of the other two buttons like <input type="submit" formaction=@Url.Action("Submit") formmethod="post" class="btn btn-primary" /> or <input type="submit" formaction=@Url.Action("Delete") formmethod="post" class="btn btn-danger" /> I do hit the public function in my controller ...

    ... but can't be redirected back to the Partial View !!! I get this exception {"Cannot find the Umbraco route definition in the route values, the request must be made in the context of an Umbraco request"}

    Could anyone please direct me in the right way?

    Thank in advance.

  • Marc Goodson 939 posts 6249 karma points MVP 4x c-trib
    Feb 16, 2018 @ 23:22
    Marc Goodson
    0

    Hi Lars

    It is the magic Html.BeginUmbracoForm, which has a hidden field which contains a hash that is used to route the posted form back to your Surface Controller Action...

    ... however for your additional buttons, you are setting the formaction explicitly using Url.Action (which would work in a pure mvc app) but without the hidden field Umbraco doesn't know how to route the request to the Surface Controller Action (an Umbraco thing) ...

    ... there are several ways around this...

    but I think the simplest is to use a special Umbraco UrlHelper extension called 'SurfaceAction'...

    eg

    formaction=@Url.SurfaceAction("actionName","controllerName")
    

    The Umbraco source describes this as 'Generates a URL based on the current Umbraco URL with a custom query string that will route to the specified Surface Controller.

    enter image description here

    hopefully that helps!

    Otherwise in MVC you can create an ActionMethodSelectorAttribute - see http://blog.ashmind.com/2010/03/15/multiple-submit-buttons-with-asp-net-mvc-final-solution/

  • Lars Dupont 3 posts 93 karma points
    Feb 19, 2018 @ 10:48
    Lars Dupont
    0

    Hi Marc.

    Thanks for your answer ... looked at your suggestion but still can't work it out!

    First step was looking at the formaction=@Url.SurfaceAction("actionName", "ctrlName") and found that any changes to this didn't make any difference, because the only action taking place in the SurfaceController is public ActionResult SubmitForm(Address dto) - which is dictated in top of the View @using (Html.BeginUmbracoForm("SubmitForm", "AddressSurface")) !!!

    @inherits UmbracoViewPage<IKAS.Mainsite.Website.Models.Customers.Address>
    @using (Html.BeginUmbracoForm("SubmitForm", "AddressSurface", FormMethod.Post))
    {
        <table>
            <tr style="height: 0; visibility: hidden">
                <td>@Html.TextBoxFor(m => m.Id)</td>
            </tr>
            <tr style="height: 0; visibility: hidden">
                <td>@Html.TextBoxFor(m => m.CustomerId)</td>
            </tr>
            ... 
            <tr>
                <td>
                    <br />
                    <p>
                        <input class="btn btn-primary" formaction=@Url.SurfaceAction("Submit", "AddressSurface") formmethod="post" name="Submit" type="submit" value="Gem" />
                        <input class="btn btn-danger" formaction=@Url.SurfaceAction("Delete", "AddressSurface") formmethod="post" name="Delete" type="submit" value="Slet" />
                    </p>
                </td>
            </tr>
        </table>
    }
    

    Next step was looking at the solution you kindly referenced by Andrey Shchekin.

    Also here, the logic of Umbraco was immunne to the use of [HttpParamAction] in my controller because it never recognize the action in formaction=@Url.SurfaceAction("Submit", "AddressSurface") and kept looking for the action defined in top of the View @using (Html.BeginUmbracoForm("SubmitForm", "AddressSurface", FormMethod.Post))!!!

    So ... could be nice just to solve this in pure HTML like you suggested in the first place.

    What am I missing?

  • javafun 15 posts 48 karma points c-trib
    Feb 10, 2019 @ 00:56
    javafun
    0

    Hi Lars

    I'm facing the same issue.

    Did you find out the solution? Can you share some details how to resolve this issue?

    Appreciate for your help.

  • Lars Dupont 3 posts 93 karma points
    Feb 14, 2019 @ 14:28
    Lars Dupont
    100

    Hi Javafun.

    I never had succes with pure HTML - so my solution was a mix of HTML and JavaScript up front calling the Surface Controller backend.

    Here is some backend code - no need to show the complete implementation:

     public class SatelliteAddressSurfaceController : SurfaceController
    {
        public ActionResult Index(Customer customer)
        {}
    
        [HttpPost]
        public void InsertAddress(int customerId, int addressTypeId)
        {
            using (var context = new Kundeportal_Entities())
            {
                var address = new Address
                {
                    AddressTypeID = addressTypeId,
                    CustomerID = customerId,
                };
                context.Entry(address).State = EntityState.Added;
                context.SaveChanges();                
            }
            Session.SetDataToSession<string>("State", EntityState.Added.ToString());
        }
    
        [HttpPost]
        public ActionResult SubmitForm(SatelliteAddressDto dto, int pageIndex, int increase)
        {
            SaveDto(dto);
            if (!ModelState.IsValid)
            {
                Session.SetDataToSession<int>("pageIndex", pageIndex);
                return CurrentUmbracoPage();
            }
            Session.SetDataToSession<int>("pageIndex", pageIndex + increase);
            return RedirectToCurrentUmbracoPage();
        }
    }
    

    And here is some frontend code - no need to show the complete implementation:

    <script type="text/javascript">
    
        $(document)
            .ready(function() {
    
                $('.input-validation-error:first')
                    .focus();
    
            });          
    
        function onClickInsertAddress() {
    
            $.ajax({
                type: 'POST',
                url: '@Url.Action("InsertAddress", "SatelliteAddressSurface")',
                contentType: 'application/x-www-form-urlencoded; charset=UTF-8',
                data: { customerId: @Model.CustomerID, addressTypeId: 3 }
            });
    
        };
    
        function onClickDelAddress(index) {
    
            $('[name="Addresses[' + index + '].IsDeleted"').val(true);
    
        };
    
    </script>
    

    And in my HTML code I have two buttons:

    <..button class="btn btn-danger" onclick="onClickDelAddress(@idx)">-<../button>

    <..button class="btn btn-primary" id="addAddress" onclick="onClickInsertAddress()" >+<../button>

    (pure HTML code is difficult to show!)

    We know that a Button always triggers a SubmitForm event - so the 'Delete' button first calls JavaScript function onClickDelAddress then triggers the SubmitForm event. In this way I can tell the Surface controller to delete a specific object.

    Using other public function/methode but the SubmitForm is done with $ajax - as you can see in the first JavaScript function onClickInsertAddress. Please notice; the ajax argument 'data' must match a function/methode signature to the point otherwise $ajax can't see the function/methode!

    Best Regards

Please Sign in or register to post replies

Write your reply to:

Draft