MVC Twitter Bootstrap unobtrusive error handling
Asked Answered
C

7

24

I've been trying to get MVC Jquery unobtrusive error handling working with twitter bootstrap for some time now. Its got to the point were i'm either going to edit jquery.validate or do some hack and slash on document.ready.

In order to get unobtrusive error handling to work with Bootstrap and MVC I need to make it so the 'error' class it appended to the 'control-group' class. As well as that, the 'error' class is appended to the input.

I was wondering if anyone in the community has already found a solution.

For example

Typical bootstrap markup would be like so...

<div class="control-group">
    <label for="Username">Username</label>
    <div class="controls">
        <input data-val="true" data-val-required="Username is required" id="Username" name="Username" type="text" value="" />
        <span class="field-validation-valid" data-valmsg-for="Username" data-valmsg-replace="true"></span>
    </div>
</div>

What should happen, on blur when jquery.validate unobtrusive fires... it would change to the following

<div class="control-group error">
    <label for="Username">Username</label>
    <div class="controls">
        <input data-val="true" data-val-required="Username is required" id="Username" name="Username" type="text" value="" />
        <span class="field-validation-valid help-inline" data-valmsg-for="Username" data-valmsg-replace="true"></span>
    </div>
</div>

To get this to work on postback/submit you can do the following...

//twitter bootstrap on post
$(function () {
    $('span.field-validation-valid, span.field-validation-error').each(function () {
        $(this).addClass('help-inline');
    });

    $('form').submit(function () {
        if ($(this).valid()) {
            $(this).find('div.control-group').each(function () {
                if ($(this).find('span.field-validation-error').length == 0) {
                    $(this).removeClass('error');
                }
            });
        }
        else {
            $(this).find('div.control-group').each(function () {
                if ($(this).find('span.field-validation-error').length > 0) {
                    $(this).addClass('error');
                }
            });
        }
    });


    $('form').each(function () {
        $(this).find('div.control-group').each(function () {
            if ($(this).find('span.field-validation-error').length > 0) {
                $(this).addClass('error');
            }
        });
    });

});

However, on blur it won't work as you'd expect. I don't want to edit the bootstrap CSS, or Jquery.validate files as they will likely roll out an update at some-point.

Would I create a delegate, or a bind to the jquery functions and work from there. This is deep JS code which I'm not familiar with but could with time firefight my way through it.

Does any one know where I'd start with this problem, or know where it is implemented/been discussed?

Counter answered 18/4, 2012 at 20:8 Comment(2)
Where is the question here? I see more of a request with no code, or specific issues! if you can provide a sample of where "MVC Jquery unobtrusive error handling" fails with twitter bootstrap this question might be answerable.Appear
Just updating my post. This is my first time here so I apologies for the noobificationisms :sCounter
C
28
var page = function () {
    //Update that validator
    $.validator.setDefaults({
        highlight: function (element) {
            $(element).closest(".control-group").addClass("error");
        },
        unhighlight: function (element) {
            $(element).closest(".control-group").removeClass("error");
        }
    });
} ();

Finally, this fixed it for me. I hope this helps other people too...

My final JS ended like so.

$(function () {
    $('span.field-validation-valid, span.field-validation-error').each(function () {
        $(this).addClass('help-inline');
    });

    $('form').submit(function () {
        if ($(this).valid()) {
            $(this).find('div.control-group').each(function () {
                if ($(this).find('span.field-validation-error').length == 0) {
                    $(this).removeClass('error');
                }
            });
        }
        else {
            $(this).find('div.control-group').each(function () {
                if ($(this).find('span.field-validation-error').length > 0) {
                    $(this).addClass('error');
                }
            });
        }
    });


    $('form').each(function () {
        $(this).find('div.control-group').each(function () {
            if ($(this).find('span.field-validation-error').length > 0) {
                $(this).addClass('error');
            }
        });
    });

});


var page = function () {
    //Update that validator
    $.validator.setDefaults({
        highlight: function (element) {
            $(element).closest(".control-group").addClass("error");
        },
        unhighlight: function (element) {
            $(element).closest(".control-group").removeClass("error");
        }
    });
} ();
Counter answered 19/4, 2012 at 16:20 Comment(2)
The code for the form.submit works well, except that it doesn't seem to remove the error class if something is corrected in the input (but something else still prevents the action from going through). Would you know why this is?Durban
If you move the loop which looks for "valid" fields outside of the if statement, then it will perform correctly to remove error class.Durban
S
10

Here's a nice solution...

Add this to your _Layout.cshtml file outside jQuery(document).ready():

<script type="text/javascript">

jQuery.validator.setDefaults({
    highlight: function (element, errorClass, validClass) {
        if (element.type === 'radio') {
            this.findByName(element.name).addClass(errorClass).removeClass(validClass);
        } else {
            $(element).addClass(errorClass).removeClass(validClass);
            $(element).closest('.control-group').removeClass('success').addClass('error');
        }
    },
    unhighlight: function (element, errorClass, validClass) {
        if (element.type === 'radio') {
            this.findByName(element.name).removeClass(errorClass).addClass(validClass);
        } else {
            $(element).removeClass(errorClass).addClass(validClass);
            $(element).closest('.control-group').removeClass('error').addClass('success');
        }
    }
});

</script>

Add this inside $(document).ready():

$("span.field-validation-valid, span.field-validation-error").addClass('help-inline');
$("div.control-group").has("span.field-validation-error").addClass('error');
$("div.validation-summary-errors").has("li:visible").addClass("alert alert-block alert-error");

You're good to go.


Code pieces taken from:

Twitter Bootstrap validation styles with ASP.NET MVC

Integrating Bootstrap Error styling with MVC’s Unobtrusive Error Validation

@daveb's answer

Simon answered 1/2, 2013 at 16:38 Comment(0)
N
6

In addition to the answer provided by @leniel-macaferi I use the following as my $(document).ready() function:

$(function () {
    $("span.field-validation-valid, span.field-validation-error").addClass('help-inline');
    $("div.control-group").has("span.field-validation-error").addClass('error');
    $("div.validation-summary-errors").has("li:visible").addClass("alert alert-block alert-error");
});

This also sets the "error" class on the control group if server side validation has failed on a form post and formats any validation summary nicely as a bootstrap error alert.

Naturopathy answered 8/8, 2013 at 20:10 Comment(1)
Man... I was looking for the server validation errors situation. I saw that the error class was not being added to the .control-group <div>. You saved me! :) StackOverflow is SO AWESOME!!! One answer building upon the other... This is how the world evolves.Simon
K
4

I know this is an oldy, but I thought I'd share my answer to update for Bootstrap 3. I scratched my head for quite sometime, before building on top of the solution given by Leniel Macaferi.

On top of changing the clases to reflect Bootstrap 3, I thought it would be a nice touch to present the user with a glyphicon to represent the state of the field.

(function ($) {
var defaultOptions = {
    errorClass: 'has-error has-feedback',
    validClass: 'has-success has-feedback',
    highlight: function (element, errorClass, validClass) {
        var _formGroup = $(element).closest(".form-group");
        _formGroup
            .addClass('has-error')
            .removeClass('has-success');

        if (!_formGroup.hasClass("has-feedback")) {
            _formGroup.addClass("has-feedback");
        }

        var _feedbackIcon = $(element).closest(".form-group").find(".glyphicon");
        if (_feedbackIcon.length) {
            $(_feedbackIcon)
                .removeClass("glyphicon-ok")
                .removeClass("glyphicon-remove")
                .addClass("glyphicon-remove");
        }
        else {
            $("<span class='glyphicon glyphicon-remove form-control-feedback' aria-hidden='true'></span>")
                .insertAfter(element);
        }
    },
    unhighlight: function (element, errorClass, validClass) {
        var _formGroup = $(element).closest(".form-group");
        _formGroup
            .removeClass('has-error')
            .addClass('has-success');

        if (!_formGroup.hasClass("has-feedback")) {
            _formGroup.addClass("has-feedback");
        }

        var _feedbackIcon = $(element).closest(".form-group").find(".glyphicon");
        if (_feedbackIcon.length) {
            $(_feedbackIcon)
                .removeClass("glyphicon-ok")
                .removeClass("glyphicon-remove")
                .addClass("glyphicon-ok");
        }
        else {
            $("<span class='glyphicon glyphicon-ok form-control-feedback' aria-hidden='true'></span>")
                .insertAfter(element);
        }
    }
};

$.validator.setDefaults(defaultOptions);

$.validator.unobtrusive.options = {
    errorClass: defaultOptions.errorClass,
    validClass: defaultOptions.validClass,
};

})(jQuery);
Kinney answered 13/2, 2015 at 23:24 Comment(1)
thanks for the code, but it doesn't work if you submitting using ajax and partial view, can you help on this?Giverin
E
2

Try use this plugin I've made https://github.com/sandrocaseiro/jquery.validate.unobtrusive.bootstrap

What I did differently from the others answers was to override the errorPlacement and success methods from validate.unobtrusive with my own implementations, but without removing the original implementation so nothing will break.

My implementation look like this:
erroPlacement:

function onError(formElement, error, inputElement) {
    var container = $(formElement).find("[data-valmsg-for='" + escapeAttributeValue(inputElement[0].name) + "']"),
        replaceAttrValue = container.attr("data-valmsg-replace"),
        replace = replaceAttrValue ? $.parseJSON(replaceAttrValue) !== false : null;

    //calling original validate.unobtrusive method
    errorPlacementBase(error, inputElement);

    if (replace) {
        var group = inputElement.parent();
        if (group.hasClass('form-group')) {
            group.addClass('has-error').removeClass('has-success');
        }
        group = group.parent();
        if (group.hasClass('form-group')) {
            group.addClass('has-error').removeClass('has-success');
        }
    }
}

success:

function onSuccess(error) {
    var container = error.data("unobtrusiveContainer");

    //calling original validate.unobtrusive method
    successBase(error);

    if (container) {
        var group = container.parent();
        if (group.hasClass('form-group')) {
            group.addClass('has-success').removeClass('has-error');
        }
        group = group.parent();
        if (group.hasClass('form-group')) {
            group.addClass('has-success').removeClass('has-error');
        }
    }
}
Express answered 10/2, 2014 at 13:14 Comment(0)
C
0

Out of the box I wanted on blur to raise my error validation. I found this wasn't the case with Jquery Unobtrusive. It seemed to work if you had a select input but not on a text type input. To get around this for me, perhaps its clumsy but I used the following.

$(function () {
    $("input[type='text']").blur(function () {
        $('form').validate().element(this);
    });
});

You can change it is just enabled on certain inputs that have a specific css class.

$(function () {
    $(".EnableOnblurUnobtrusiveValidation").blur(function () {
        $('form').validate().element(this);
    });
});

EnableOnblurUnobtrusiveValidation... is a bit of a long name but you get the jist.

Counter answered 21/4, 2012 at 11:51 Comment(0)
O
-1

Use TwitterBootstrapMvc.

It takes care of unobtrusive validation attributes automatically and all you have to write to get a full control group with label, input and validation is:

@Html.Bootstrap().ControlGroup().TextBoxFor(x => x.Field)

Good luck!

Overcasting answered 8/7, 2013 at 11:0 Comment(4)
The styling is different for the client-side validation vs. when the model is invalid on the server's side. I think you still need https://mcmap.net/q/428543/-mvc-twitter-bootstrap-unobtrusive-error-handlingAmplifier
the styling depends solely on css.Overcasting
Yes and MVC4's unobtrusive validation adds classes like field-validation-error whereas Bootstrap has classes like error. TwitterBootstrapMvc adds error when it's generating the markup (when the model is invalid) but that's only half the time validation occurs. braindonor.net/blog/…Amplifier
I see what you mean. The class error is only added on the server validation. Well yea, TwitterBootstrapMVC does not come together with javascript. Maybe it should...Overcasting

© 2022 - 2024 — McMap. All rights reserved.