3

I'm dynamically creating some forms and I'm using bootbox 4 alongside bootstrap 3 and jquery 3.1.0 in an MVC5 application.

Edit: I updated my project's jquery from 1.10.2 to 3.1.0, still have the same issue below.

Usage: click remove, bootbox confirm removal via modal, and if confirmed, call the .submit() action on the form.

Application Screenshot

The issue: I click on the delete button and it pops up a modal to confirm the action, and i click "continue" but it does nothing. There seems to be only one working submit action among all the form elements. So I'm able to successfully call submit on one element per page refresh.

I'm new to javascript, so I have a basic understanding at the moment and I'm trying figure out what I'm doing wrong with my code below. Any help would be appreciated.

My View code:

@foreach (var user in Model.Users)

    {
        <tr>
            <td>
                @user.Id
            </td>
            <td class="text-center">
                @user.FirstName @user.LastName
            </td>
            <td class="text-center">
                @Html.CheckBox("role", user.IsAdmin)
            </td>
            <td>
                @using (Html.BeginForm("UpdateUserRole", "Admin", FormMethod.Post, new {@class = "btn-inline"}))
                {
                    @Html.Hidden("id", user.Id)
                    @Html.Hidden("role", user.GetRole(user.IsAdmin))
                    <button type="submit" class="btn btn-info">Update</button>
                }
                @using (Html.BeginForm("RemoveUser", "Admin", FormMethod.Post, new {@class = "btn-inline", @id=user.Id+"Form"}))
                {
                    @Html.Hidden("id", user.Id)
                    <button data-user-id=@user.Id type="submit" class="btn btn-danger buttonElement">Remove</button>
                }
            </td>
        </tr>
    }

My script (added to bottom of View):

@section scripts
{
<script type="text/javascript">
    $(function () {
        $('form').on('click','button.buttonElement',function (e) {
            var user = $(this).attr("data-user-id");
            e.preventDefault();
            bootbox.dialog({
                message: "Do you want to continue ?", title: "Remove User",
                buttons: {
                    main: { label: "Cancel", className: "btn btn-default", callback: function () { return true; } },
                    success: { label: "Continue", className: "btn btn-default", callback: function () { $('#'+user+'Form').submit(); } }
                }
            });
        });
    });
</script>
}

Edit: Here's some HTML output:

<table class="table table-hover">
    <tbody>
        <tr>
            <td>
                Joe
            </td>
            <td class="text-center">
                Joe Cuevas
            </td>
            <td class="text-center">
                <input checked="checked" id="role" name="role" type="checkbox" value="true"><input name="role" type="hidden" value="false">
            </td>
            <td>
                <form action="/Admin/UpdateUserRole" class="btn-inline" method="post">
                    <input data-val="true" data-val-regex="Invalid!" data-val-regex-pattern="([A-Za-z0-9.-])+" data-val-required="NetworkID is required!" id="id" name="id" type="hidden" value="joe"><input id="role" name="role" type="hidden" value="Admin">                        <button type="submit" class="btn btn-info">Update</button>
                </form>                    <form action="/Admin/RemoveUser" class="btn-inline" id="joeForm" method="post">
                    <input data-val="true" data-val-regex="Invalid!" data-val-regex-pattern="([A-Za-z0-9.-])+" data-val-required="NetwordID is required!" id="id" name="id" type="hidden" value="joe">                        <button data-user-id="joe" type="submit" class="btn btn-danger buttonElement">Remove</button>
                </form>
            </td>
        </tr>
    </tbody>
</table>

Edit: Solution

It turns out that modelstate was overriding the id value of all forms generated by the foreach loop from my View, on the final HTML output. This explains why all the symptoms above were occurring. I simply added the following code to my controller before returning my viewModel and it was fixed: ModelState.Remove("id");

Joe Cuevas
  • 75
  • 1
  • 7
  • What is the HTML output of your application. I don't think there is an issue with the script. Made a little test here... https://jsfiddle.net/ymm8y0wb/ – BA_Webimax Aug 18 '16 at 19:39
  • I checked my html output and I believe I may have found the issue. I nested the forms within a table. table > tr > td > form. I'll clean up my view then reply with the outcome. – Joe Cuevas Aug 18 '16 at 20:39
  • BA_Webimax, I went ahead and added some html output to my post. – Joe Cuevas Aug 18 '16 at 22:20
  • I notice there are additional `data-` attributes that appear to be for input validation. Is it possible that the validation routine is somehow interfering with the form submission or at least not working with BootBox? Have you tried disabling the validation to see if the rest of the process works? – BA_Webimax Aug 19 '16 at 13:15
  • I've removed the data annotation from the viewModel that was producing the unnecessary input validation. That wasn't the issue though, it was my script. I've accepted an answer below as the solution. – Joe Cuevas Aug 19 '16 at 13:42

1 Answers1

2

table / tr / td / form is a fine, as far as nesting goes. You can probably simplify triggering the form submit with:

$('form').on('click','button.buttonElement',function (e) {
    var user = $(this).attr("data-user-id");
    var form = $(this).closest('form'); // <-- add this

    e.preventDefault();

    bootbox.dialog({
        message: "Do you want to continue ?", 
        title: "Remove User",
        buttons: {
            main: { 
                label: "Cancel", 
                className: "btn btn-default", 
                callback: function () { 
                    return true; 
                } 
            },
            success: { 
                label: "Continue", 
                className: "btn btn-default", 
                callback: function () { 
                    form.submit();  // <-- change this
                }
            }
         }
    });
 });

You could also simply use the bootbox.confirm function. If you want custom button text, follow the second example here, like so.

$('form').on('click','button.buttonElement',function (e) {
    var user = $(this).attr("data-user-id");
    var form = $(this).closest('form'); // <-- add this

    e.preventDefault();

    bootbox.confirm({
        message: "Do you want to continue ?", 
        title: "Remove User",
        buttons: {
            cancel: { 
                label: "Cancel", 
                className: "btn btn-default"
            },
            confirm: { 
                label: "Continue", 
                className: "btn btn-default"
            }
         },
         callback: function(result) {
             if(result == true) {
                 form.submit();
             }
         }
    });
 });

You might want to consider the fact that a user could trigger the submission of the form without clicking your continue button, in which case the delete request will occur without confirmation.

Also, Bootbox hasn't been confirmed to work with jQuery 3.x yet, although that depends more on the parent Bootstrap library version - you have to upgrade to Bootstrap 3.3.7 if you've upgraded to jQuery 3.x.

Tieson T.
  • 20,774
  • 6
  • 77
  • 92
  • I'll go ahead and try the suggested changes first chance I get. Thanks for filling me in about jQuery and Bootbox, I'm running 3.3.7 on this application. Lastly, how would a user be able to submit the form without clicking continue? I appreciate you taking the time on this question. – Joe Cuevas Aug 19 '16 at 02:09
  • It would take an educated user, but you can test this yourself by running something like `$('form').submit()` in the JavaScript console of your browser. Since you're only capturing the click event of certain buttons, there's nothing to stop those forms on the page from having their submit event triggered. – Tieson T. Aug 19 '16 at 03:10
  • You can somewhat handle that by capturing the submit event instead, and then using AJAX to post the delete request if the user confirms deletion. – Tieson T. Aug 19 '16 at 03:12