Ctrl_Alt_Defeat
Ctrl_Alt_Defeat

Reputation: 4009

Bootstrap validation with MVC?

I have been looking at the following site - http://bootstrapvalidator.com/getting-started/#release

and I like the validation. I am developing an MVC 5 application and I am using bootstrap. I am currently using Decorators on my models to fire Validation.

e.e

[Required]
public string FirstName { get; set; }

My Markup then is as below:

    <div class="col-xs-5">
        @Html.TextBoxFor(model => model.FirstName, new { @class = "form-control input-sm" })
        @Html.ValidationMessageFor(model => model.FirstName)
    </div>

Another field on model would be for e.g

Age

[Range(0,150), ErrorMessage="Age field cannot be greater than 150"]
public int Age { get; set; }

So I would want that Error Message for Age injected into the toottip rather than repeating Validations server side and client side for every page in my Application

So if user trys to submit the form without FirstName filled in the validation message will fire.

What I would really like to achieve is something like this - http://bootstrapvalidator.com/settings/#form-container - so the field fires validation and the error message is put in ToolTip - has anyone done anything like this with MVC and is there a way we could force the Server Side Validation message to be injected into the error that would appear in that ToolTip?

Update

Attempting to use @Tieson answer below - not working currently. I added the styles just on the page that contain a percentage field that can only contain 3 numeric digits. My bundle config after downloading the bootstrap unobtrusice extension is as:

        var thirdPartyScripts = new ScriptBundle("~/bundles/thirdpartyscripts").Include(
            "~/Scripts/modernizr-*",
            "~/Scripts/jquery-{version}.js",
            "~/Scripts/jquery.validate.js",
            "~/Scripts/jquery.validate.unobtrusive.js",
            "~/Scripts/jquery.form.js",
            "~/Scripts/jquery.blockUI.js",
            "~/Scripts/jquery.autosize.min.js",
            "~/Scripts/bootstrap.js",
            "~/Scripts/jquery.validate.unobtrusive.bootstrap.js",
            "~/Scripts/respond.js",
            "~/Scripts/kendo/kendo.web.min.js",
            "~/Scripts/kendo/kendo.aspnetmvc.min.js",
            "~/Content/ckeditor/ckeditor.js",
            "~/Content/ckeditor/adapters/jquery.js",
            "~/Scripts/knockout-3.1.0.js",
            "~/Scripts/knockout.mapping-latest.js");

        thirdPartyScripts.Orderer = new AsIsBundleOrderer();
        bundles.Add(thirdPartyScripts);

The percentage field in my view model is as:

 [RegularExpression(@"^(?:999|[1-9]?[0-9]?[0-9])$", ErrorMessage = "Does this work")]
        public int Percentage{ get; set; }

I added the update to the bootstrap.js file and then in the document.ready function I added the update to the setDefaults validation. What I am seeing is that if I enter valid numbers to the field eg 50 it does not highlight green. If I enter 4 digits and tab away from field it does highlight red but there is no X icon included in the field that will render the tooltip error message and then when I enter valid data again it does not turn back from red. I put an alert('here') in the showErrors function in the setDefaults on the validator in document.ready and I do not see it firing the alert?

The rendered html on screen when I enter invalid data and the field goes red is:

<input class="form-control input-validation-error text-danger" data-val="true" data-val-regex="Does this work" data-val-regex-pattern="^(?:999|[1-9]?[0-9]?[0-9])$" id="txtPercentage" name="Percentage" placeholder="Percentage" type="text" value="" aria-invalid="true">

My cshtml markup is as below:

            <div class="row form-group">
                @Html.LabelFor(m => m.Percentage, new { @class = "col-md-3 control-label" })
                <div class="col-md-9">
                    @Html.TextBoxFor(m => m.Percentage, new { @class = "form-control", @placeholder = "Percentage", id = "txtPercentage" })
                </div>
            </div>

Do I need to add tootip to my markup here as well?

Upvotes: 3

Views: 8699

Answers (3)

Tieson T.
Tieson T.

Reputation: 21211

I've found that combining a few answers from SO let me use Bootstrap's tooltips in place of the inline validation messages, while letting me also use the styling of the .alert classes.

First, I tweaked the CSS from this answer: Modifying Twitter Bootstrap's Tooltip Colors Based on Position

/* INFO */
.tooltip.tooltip-info .tooltip-inner
{
    color: #31708f;
    background-color: #d9edf7;
    border-color: #bce8f1;
    background-image: -webkit-linear-gradient(top, #d9edf7 0, #b9def0 100%);
    background-image: linear-gradient(to bottom, #d9edf7 0, #b9def0 100%);
    background-repeat: repeat-x;
    border-color: #9acfea;
    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);
}

.tooltip.tooltip-info.top .tooltip-arrow,
.tooltip.tooltip-info.top-left .tooltip-arrow,
.tooltip.tooltip-info.top-right .tooltip-arrow
{
    border-top-color: #bce8f1;
}

.tooltip.tooltip-info.bottom .tooltip-arrow,
.tooltip.tooltip-info.bottom-left .tooltip-arrow,
.tooltip.tooltip-info.bottom-right .tooltip-arrow
{
    border-bottom-color: #bce8f1;
}

.tooltip.tooltip-info.right .tooltip-arrow
{
    border-right-color: #bce8f1;
}

.tooltip.tooltip-info.left .tooltip-arrow
{
    border-left-color: #bce8f1;
}


/* DANGER (ERROR) */
.tooltip.tooltip-danger .tooltip-inner
{
    color: #a94442;
    background-color: #f2dede;
    border-color: #ebccd1;
    background-image: -webkit-linear-gradient(top, #f2dede 0, #e7c3c3 100%);
    background-image: linear-gradient(to bottom, #f2dede 0, #e7c3c3 100%);
    background-repeat: repeat-x;
    border-color: #dca7a7;
    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);
}

.tooltip.tooltip-danger.top .tooltip-arrow,
.tooltip.tooltip-danger.top-left .tooltip-arrow,
.tooltip.tooltip-danger.top-right .tooltip-arrow
{
    border-top-color: #ebccd1;
}

.tooltip.tooltip-danger.bottom .tooltip-arrow,
.tooltip.tooltip-danger.bottom-left .tooltip-arrow,
.tooltip.tooltip-danger.bottom-right .tooltip-arrow
{
    border-bottom-color: #ebccd1;
}

.tooltip.tooltip-danger.right .tooltip-arrow
{
    border-right-color: #ebccd1;
}

.tooltip.tooltip-danger.left .tooltip-arrow
{
    border-left-color: #ebccd1;
}


/* WARNING */
.tooltip.tooltip-warning .tooltip-inner
{
    color: #8a6d3b;
    background-color: #fcf8e3;
    border-color: #faebcc;
    background-image: -webkit-linear-gradient(top, #fcf8e3 0, #f8efc0 100%);
    background-image: linear-gradient(to bottom, #fcf8e3 0, #f8efc0 100%);
    background-repeat: repeat-x;
    border-color: #f5e79e;
    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);
}

.tooltip.tooltip-warning.top .tooltip-arrow,
.tooltip.tooltip-warning.top-left .tooltip-arrow,
.tooltip.tooltip-warning.top-right .tooltip-arrow
{
    border-top-color: #faebcc;
}

.tooltip.tooltip-warning.bottom .tooltip-arrow,
.tooltip.tooltip-warning.bottom-left .tooltip-arrow,
.tooltip.tooltip-warning.bottom-right .tooltip-arrow
{
    border-bottom-color: #faebcc;
}

.tooltip.tooltip-warning.right .tooltip-arrow
{
    border-right-color: #faebcc;
}

.tooltip.tooltip-warning.left .tooltip-arrow
{
    border-left-color: #faebcc;
}


/* SUCCESS */
.tooltip.tooltip-success .tooltip-inner
{
    color: #3c763d;
    background-color: #dff0d8;
    border-color: #d6e9c6;
    background-image: -webkit-linear-gradient(top, #dff0d8 0, #c8e5bc 100%);
    background-image: linear-gradient(to bottom, #dff0d8 0, #c8e5bc 100%);
    background-repeat: repeat-x;
    border-color: #b2dba1;
    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);
}

.tooltip.tooltip-success.top .tooltip-arrow,
.tooltip.tooltip-success.top-left .tooltip-arrow,
.tooltip.tooltip-success.top-right .tooltip-arrow
{
    border-top-color: #d6e9c6;
}

.tooltip.tooltip-success.bottom .tooltip-arrow,
.tooltip.tooltip-success.bottom-left .tooltip-arrow,
.tooltip.tooltip-success.bottom-right .tooltip-arrow
{
    border-bottom-color: #d6e9c6;
}

.tooltip.tooltip-success.right .tooltip-arrow
{
    border-right-color: #d6e9c6;
}

.tooltip.tooltip-success.left .tooltip-arrow
{
    border-left-color: #d6e9c6;
}

Then we add the Bootstrap Unobtrusive Validation plugin: https://www.nuget.org/packages/jquery.validate.unobtrusive.bootstrap/ - this extends the Unobtrusive Validation plugin such that it adds the .has-error, etc. classes to the nearest .form-group of the triggering input.

The one part that bugs me is this; I had to modify the bootstrap.js file a bit, from this:

  $tip
    .detach()
    .css({ top: 0, left: 0, display: 'block' })
    .addClass(placement)

to this:

  $tip
    .detach()
    .css({ top: 0, left: 0, display: 'block' })
    .addClass(placement)
    .addClass(this.$element.data('tooltip'))

which lets the tooltip plugin read the data- attribute which defines which tooltip style to apply.

Lastly, modify the validator settings a bit:

$.validator.setDefaults({
    showErrors: function (errorMap, errorList) {
        this.defaultShowErrors();

        // destroy tooltips on valid elements                              
        $("." + this.settings.validClass).tooltip("destroy");

        // add/update tooltips 
        for (var i = 0; i < errorList.length; i++) {
            var error = errorList[i];

            var id = '#' + error.element.id;
            var isInModal = $(id).parents('.modal').length > 0;

            $(id)
                .tooltip({ trigger: 'focus', placement: isInModal ? 'auto right' : 'right', container: isInModal ? false : '.row', html: true })
                .attr('data-tooltip', 'tooltip-danger')
                .attr('data-original-title', error.message);
        }
    }
});

The version I use is a little different from what @VahidN suggested; I sometimes use modals to display input forms, and applying a container to the tooltip doesn't render correctly in a modal. The container is necessary if your inputs might be used in an .input-group, though, so I just use the closest .row.

Upvotes: 2

VahidN
VahidN

Reputation: 19156

It's possible to rewrite Default settings of jQuery validator:

  <script type="text/javascript">
        $.validator.setDefaults({            
            showErrors: function (errorMap, errorList) {
                this.defaultShowErrors();

                // If an element is valid, it doesn't need a tooltip
                $("." + this.settings.validClass).tooltip("destroy");

                // Add tooltips
                for (var i = 0; i < errorList.length; i++) {
                    var error = errorList[i];
                    $(error.element).tooltip({ trigger: "focus" }) // Activate the tooltip on focus
                                    .attr("data-original-title", error.message);
                }
            }
     });
 </script>

Here, errorList gives us all of the not validated elements. By using its error.element property, we can attach a tooltip to this element.

Upvotes: 3

Gediminas Soliškis
Gediminas Soliškis

Reputation: 96

MVC framework uses JQuery validation. Razor view helpers generate HTML data annotations based on model property and it's attributes.

[Required(ErrorMessage="You must enter a username")]
public string Username {get; set;}

@Html.EditorFor(m => m.Username)
<input type="text" data-val="true" data-val-required="You must enter a username" value="">
</input>
<!--Generated placeholder for clientside validator message-->

You would have to overwrite editor helper(s) to make bootstrap validator specific data-* annotations. Here's an article to quickly introduce you to writing your own custom DisplayFor and EditorFor helpers:

http://buildstarted.com/2010/09/10/overriding-displayfor-and-editorfor-to-create-custom-outputs-for-mvc/

Upvotes: 1

Related Questions