1
votes

I've been trying to validate some control in a modal dialog for days now, and despite all the examples and other posts here on SO I can't seem to get it working...

In my webpage I have a button that opens a modal dialog. That modal dialog has three required input boxes: one for text and two for positive numeric values. I want to validate the inputs when the user clicks save using the fancy bootstrap feedback scheme like these examples:

http://formvalidation.io/examples/modal/

http://1000hz.github.io/bootstrap-validator/

If the input is valid then I'll take the values and process accordingly. I haven't gotten this far though. The modal does open currently.

I know these examples use forms, but since I'm using a master page, a nested form in my content page isn't allowed. So how can I validate the input and apply the feedback style to the invalid controls when the user clicks save?

<!-- Button to trigger modal -->
<button type="button" id="btnOpenModal" class="btn btn-info" data-toggle="modal" data-target="#myModal">Add</button>

<!-- Bootstrap Modal Dialog -->
<div class="modal fade" id="myModal" data-toggle="validator" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
    <div class="modal-dialog" role="document">
        <div class="modal-content">
            <div class="modal-header">
                <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
                <h4 class="modal-title" id="myModalLabel">Here is the modal dialog</h4>
            </div>
            <div id="loginForm" class="modal-body form-horizontal">
                <h5>Describe what to do here...</h5>
                <div class="form-inline form-group">
                    <label for="mdltxtId" class="col-sm-3 control-label">Description</label>
                    <div class="col-sm-9">
                        <input type="text" class="form-control" id="mdltxtId" name="mdltxtId" placeholder="item description" />
                    </div>
                </div>
                <div class="form-inline form-group">
                    <label for="mdltxtWgt" class="col-sm-3 control-label">Weight (LB)</label>
                    <div class="col-sm-9">
                        <input type="text" pattern="^[0-9]{1,}" title="Positive number only" class="form-control" id="mdltxtWeight" name="mdltxtWeight" placeholder="weight in pounds" />
                    </div>
                </div>
                <div class="form-inline form-group">
                    <label for="mdltxtLength" class="col-sm-3 control-label">Length (IN)</label>
                    <div class="col-sm-9">
                        <input type="text" pattern="^[0-9]{1,}" title="Positive number only" class="form-control" id="mdltxtLength" name="mdltxtLength" placeholder="length in inches" />
                    </div>
                </div>
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
                <asp:Button ID="btnSave" runat="server" Text="Save" CssClass="btn btn-primary" />
                <%--<button type="submit" class="btn btn-primary">Save changes</button>--%>
            </div>
        </div>
    </div>
</div>

I probably could change the project to not use a master page to make life easier with forms - it's not required (default Web Forms project in VS2015 sets this up automatically).

Just to add...I'm primarily a VB.NET winforms developer so I'm probably missing a lot of fundamentals on ASP.NET and javascript so go easy on me.

3

3 Answers

1
votes

Using the Bootstrap Validate plugin, you cannot validate input elements that are outside of a <form></form>. There is no workaround for this limitation.

with a Form id="Form"

<form id="Form">
<div class="modal-body form-horizontal">
    <h5>Describe what to do here...</h5>
    <div class="form-inline form-group">
        <label for="mdltxtId" class="col-sm-3 control-label">Description</label>
        <div class="col-sm-9">
            <input type="text" class="form-control" id="mdltxtId" name="mdltxtId" placeholder="item description" />
        </div>
    </div>
</div>
<div class="modal-footer">
    <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
    <asp:Button ID="btnSave" runat="server" Text="Save" CssClass="btn btn-primary" />
    <%--<button type="submit" class="btn btn-primary">Save changes</button>--%>
</div>
</form>

Fiddle with Form

When form id="Form" changed into a div id="Form" and now the same code cannot be validate by Bootstrap Validate plugin. The plugin does nothing without a <form></form>.

<div id="Form" class="modal-body form-horizontal">
    <h5>Describe what to do here...</h5>
    <div class="form-inline form-group">
        <label for="mdltxtId" class="col-sm-3 control-label">Description</label>
        <div class="col-sm-9">
            <input type="text" class="form-control" id="mdltxtId" name="mdltxtId" placeholder="item description" />
        </div>
    </div>
</div>
<div class="modal-footer">
    <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
    <asp:Button ID="btnSave" runat="server" Text="Save" CssClass="btn btn-primary" />
    <%--<button type="submit" class="btn btn-primary">Save changes</button>--%>
</div>

Fiddle with Div

0
votes

So as Shehary pointed out the Bootstrap Validate plugin won't work on a modal dialog that is not a form. So to emulate the same effect, I added a span tag to each input to provide the feedback text and use javascript to to the validating and add the feedback styles.

Here is the modal:

<div class="modal fade" id="myModal" data-toggle="validator" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
    <div class="modal-dialog" role="document">
        <div class="modal-content">
            <div class="modal-header">
                <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
                <h4 class="modal-title" id="myModalLabel">Here is the modal dialog</h4>
            </div>
            <div id="loginForm" class="modal-body form-horizontal">
                <h5>Describe what to do here...</h5>
                <div class="form-inline form-group">
                    <label for="mdltxtId" class="col-sm-3 control-label">Description</label>
                    <div class="col-sm-9">
                        <input type="text" pattern="^.{1,}" title="Item name required" class="form-control" id="mdltxtId" name="mdltxtId" placeholder="item description"/>                            
                        <span id="mdlIdHelper" class="help-block h6"></span>
                    </div>
                </div>
                <div class="form-inline form-group">
                    <label for="mdltxtWgt" class="col-sm-3 control-label">Weight (LB)</label>
                    <div class="col-sm-9">
                        <input type="text" pattern="^[+]?([.]\d+|\d+[.]?\d*)" title="Positive number only" class="form-control" id="mdltxtWeight" name="mdltxtWeight" placeholder="weight in pounds" />
                        <span id="mdlWgtHelper" class="help-block h6"></span>
                    </div>
                </div>
                <div class="form-inline form-group">
                    <label for="mdltxtArm" class="col-sm-3 control-label">Arm (IN)</label>
                    <div class="col-sm-9">
                        <input type="text" pattern="^[0-9]{1,}" title="Positive number only" class="form-control" id="mdltxtArm" name="mdltxtArm" placeholder="arm length in inches" />
                        <span id="mdlArmHelper" class="help-block h6"></span>
                    </div>
                </div>
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>                    
                <button type="button" class="btn btn-primary" id="btnmdlSave2" onclick="CloseModal(); return false;">Save</button>
            </div>
        </div>
    </div>
</div>

And here is the javascript:

    function OpenModal() {
        //window.alert('opening modal');
        ResetModal();
        //window.alert('modal was reset');
        $('#myModal').modal('show');
        //window.alert('modal was opened');
    }

    function ResetModal() {
        //window.alert('beginning modal reset');
        var mdlId = $('#mdltxtId');
        var mdlWgt = $('#mdltxtWeight');
        var mdlArm = $('#mdltxtArm');

        // reset the item description input group
        mdlId.closest('.form-group').removeClass('has-error').removeClass('has-success');
        mdlId.val('');
        mdlIdHelper.innerHTML = "";

        // reset the item weight input group
        mdlWgt.closest('.form-group').removeClass('has-error').removeClass('has-success');
        mdlWgt.val('');
        mdlWgtHelper.innerHTML = "";

        // reset the arm length input group
        mdlArm.closest('.form-group').removeClass('has-error').removeClass('has-success');
        mdlArm.val('');
        mdlArmHelper.innerHTML = "";
        //window.alert('finished modal reset');
    }

    function ValidateModal() {
        var mdlId = $('#mdltxtId');
        var mdlWgt = $('#mdltxtWeight');
        var mdlArm = $('#mdltxtArm');
        var val = true

        // Check if the input is valid
        if (!mdlId.val()) {
            // Add errors highlight
            mdlId.closest('.form-group').removeClass('has-success').addClass('has-error');
            mdlIdHelper.innerHTML = "You must enter a description";
            val = false
        } else {
            // Add success highlight
            mdlId.closest('.form-group').removeClass('has-error').addClass('has-success');
            mdlIdHelper.innerHTML = "";
        }

        // Check if the input is valid
        if (!mdlWgt.val() || !$.isNumeric(mdlWgt.val()) || mdlWgt.val() <= 0) {
            // Add errors highlight
            mdlWgt.closest('.form-group').removeClass('has-success').addClass('has-error');
            mdlWgtHelper.innerHTML = "Item weight must be a positive numeric value";
            val = false;
        } else {
            // Add success highlight
            mdlWgt.closest('.form-group').removeClass('has-error').addClass('has-success');
            mdlWgtHelper.innerHTML = "";
        }

        // Check if the input is valid
        if (!mdlArm.val() || !$.isNumeric(mdlArm.val()) || mdlArm.val() <= 0) {
            // Add errors highlight
            mdlArm.closest('.form-group').removeClass('has-success').addClass('has-error');
            mdlArmHelper.innerHTML = "Arm length must be a positive numeric value";
            val = false;
        } else {
            // Add success highlight
            mdlArm.closest('.form-group').removeClass('has-error').addClass('has-success');
            mdlArmHelper.innerHTML = "";
        }

        // return false if there was an error in the modal dialog.  A FALSE return value will prevent a postback to the server.  Might be redundant since the button onclick also has 'return false'.
        return val;
    }
0
votes

I faced the same problem, probably it's too late but here is the code I did. It's useful if you don't care about use ajax and json...

<!-- Modal -->
<div class="modal fade" id="modalOption" tabindex="-1" role="dialog" aria-labelledby="modalOptionLabel">
    <div class="modal-dialog" role="document">
        <div class="modal-content">
            <div class="modal-header">
                <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
                <h4 class="modal-title" id="modalOptionLabel">Options</h4>
            </div>
            <div class="modal-body">
                @using (Html.BeginForm("Save", "Option", FormMethod.Post, new { id = "frmTest" }))
                {
                    Html.AntiForgeryToken();
                    <div class="row">
                        <div class="col-md-6">
                            <div class="col-md-6">
                                @Html.LabelFor(model => model.VALUE1)
                                @Html.TextBoxFor(model => model.VALUE1, String.Format("{0:#0.00}", Model.VALUE1), new { @class = "form-control input-decimal" })
                                @Html.ValidationMessageFor(model => model.VALUE1, "", new { @class = "label label-danger" })
                            </div>

                        </div>
                        <div class="col-md-6">
                            <div class="col-md-6">
                                @Html.LabelFor(model => model.VALUE2)
                                @Html.TextBoxFor(model => model.VALUE2, String.Format("{0:#0.00}", Model.VALUE2), new { @class = "form-control input-decimal" })
                                @Html.ValidationMessageFor(model => model.VALUE2, "", new { @class = "label label-danger" })
                            </div>
                        </div>
                    </div>
                }
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-default btn-sm" data-dismiss="modal">Close</button>
                <button type="button" class="btn btn-primary btn-sm crud-save" id="SaveOption" data-target="#modalOption" data-container="#divOptionGrid" form="frmTest">Save</button>
            </div>
        </div>
    </div>
</div>


<script>
$(document).off('click', '.crud-save').on('click', '.crud-save', function (event) {
    var formName = $(this).attr("form");
    var form = $("#" + formName)
    form.removeData('validator');
    form.removeData('unobtrusiveValidation');
    $.validator.unobtrusive.parse(form);

    var isValid = $(form).validate().form();

    if (!isValid) {
        event.preventDefault();
        return false;
    }
    var formInfo = form.serialize();
    //here goes your ajax implementation
   $.ajax({
        type: form.attr('method'),
        dataType: "json",//response by using json
        url: form.attr('action'),
        data: formInfo,
        success: function (customJson) //just return a JsonResult from the controller if the process is successful
        {
            if(customJson.isDone){
                alert("Done");
                $("#modalOption").modal('hide');
            }
            else{                   
                alert(customJson.errorMessage);
            }                
        },
        error: function (jqXHR, textStatus, errorThrown) {
            console.log(textStatus, errorThrown);
        }
    }
});
</script>