Using jQuery Validation plugin, I defined the following for my Bootstrap form:
$(".form-with-validation").validate({
errorClass: "help-block",
errorElement: "span",
highlight: function(element) {
$(element).closest('.form-group').addClass('has-error');
},
unhighlight: function(element) {
$(element).closest('.form-group').removeClass('has-error');
}
});
It works well for a simple form. However, the part with highlight and unhighlight doesn't work when a .form-group contains multiple inputs (inline) that need to be validated:
<div class="form-group">
<label class="col-md-4 control-label">State & Zip</label>
<div class="col-md-3">
<select class="form-control required" name="state">
...
</select>
</div>
<div class="col-md-3">
<input type="text" class="form-control required" name="zip">
</div>
</div>
The problem is that once you select a state for example, the input becomes valid, and its .form-group parent loses .has-error class, even though the sibling input (i.e. zip) is still invalid (i.e. has a .help-block span below it):
So, I changed the unhighlight part to the following:
unhighlight: function(element) {
var formGroup = $(element).closest('.form-group');
var formGroupIsValid = true;
formGroup.find('input').each(function(){
if (! $(this).valid())
formGroupIsValid = false;
});
if (formGroupIsValid)
formGroup.removeClass('has-error');
}
Yet I get the following error:
Uncaught RangeError: Maximum call stack size exceeded
Any ideas why? I tried many approaches, but each time I get the same error.
EDIT
I'd prefer to stick with div.form-group having .has-error class if possible (because of styling).
EDIT 2
Jsfiddle to demonstrate the issue.
Here is the solution I ended up with. It was simpler than I thought. As people have indicated before, any form-group should contain only one form-control at a time. The easiest solution is thus to put a second form-group inside the first one and then place the second form-control in there:
<div class="form-group">
<label class="col-md-4 control-label">State & Zip</label>
<div class="col-md-6">
<div class="row">
<div class="col-sm-6">
<select class="form-control required" name="state">
...
</select>
</div>
<div class="col-sm-6 form-group" style="margin-bottom:0;padding-right:0">
<input type="text" class="form-control required" name="zip">
</div>
</div>
</div>
</div>
With just a few CSS styles, this works perfectly and looks just fine. Here's a jsfiddle.
You are calling a function which calls another function and so on, until you hit the call stack limit. I am going to assume the problem is in your .each loop when you call .valid().
You shouldn't have to do any of that though. Instead of targeting form-group you should target something around the input specifically so you don't have to change that unhightlight function. For example something like:
<div class="form-group">
<label class="col-md-4 control-label">State & Zip</label>
<div class="col-md-3 inputToValidate">
<select class="form-control required" name="state">
...
</select>
</div>
<div class="col-md-3 inputToValidate">
<input type="text" class="form-control required" name="zip">
</div>
</div>
And then update the JavaScript code to something like:
$(".form-with-validation").validate({
errorClass: "help-block",
errorElement: "span",
highlight: function(element) {
$(element).closest('.inputToValidate').addClass('has-error');
},
unhighlight: function(element) {
$(element).closest('.inputToValidate').removeClass('has-error');
}
});
I'd prefer to stick with div.form-group having .has-error class if possible
It's not possible with the options provided by the plugin. The valid/invalid classes are toggled on the element being validated. The highlight and unhighlight functions can be modified to toggle classes on other elements using jQuery DOM traversal, etc.
However, you want logic that makes a parent container "invalid" when any of its children are invalid... the plugin is not equipped for that. As soon as an invalid child element triggers the error class on the parent, any valid child element will apply the valid class to the same parent.
A workaround would be an external keyup and change handler that looks at the classes on all sibling input elements and toggles its parent class accordingly. Based on your own code and untested...
$('input, select').on('keyup change', function() {
var formGroup = $(this).closest('.form-group');
var formGroupIsValid = true;
formGroup.find('input, select').each(function(){
if (! $(this).valid()) {
formGroupIsValid = false;
}
});
if (formGroupIsValid) {
formGroup.removeClass('has-error');
} else {
formGroup.addClass('has-error');
}
});
I get the following error: Uncaught RangeError: Maximum call stack size exceeded .... Any ideas why?
Yes, you are calling the .valid() method from within the .validate() method (via unhighlight). So calling $(this).valid() from within this method only causes unhighlight to be called again... and so on indefinitely.
You don't have to use the form-with-validation class you can target all the form field that you are wanting to validate by selecting the inputToValidate class. It also much simpler. Also you probably want to use the toggleClass.
$(".inputToValidaten").validate({
errorClass: "help-block",
errorElement: "span",
highlight: function(element) {
$(this).toggleClass('has-error');
},
unhighlight: function(element) {
$(this).toggleClass('has-error');
}
});
Updates and Edites
OK. So here is code that will work on any nested form field with the form-control class. It will go up and grab the closest element with the form-group class and then add you validation code to it.
$(".form-control").validate({
errorClass: "help-block",
errorElement: "span",
highlight: function(e) {
$(e.target).closest('.form-group').toggleClass('has-error');
},
unhighlight: function(e) {
$(e.target).closest('.form-group').toggleClass('has-error');
}
});
Related
I'm trying to add a class to my element based on whether it's required and empty field. Not sure what I'm doing wrong. My console.log is getting printed but the class is not assigned.
This is my html
<div ng-repeat="supplier in vm.exportSuppliers" class="row">
<div class="col-xs-5">
<label ng-show="vm.exportSuppliers[$index].exportSupplier" class="control-label" for="es{{$index}}+Ref">Agreement Reference *</label>
<label ng-hide="vm.exportSuppliers[$index].exportSupplier" class="control-label" for="es{{$index}}+Ref">Agreement Reference</label>
<input class="form-control" id="es{{$index}}+Ref" type="text"
ng-model="supplier.agreementReference" ng-change="vm.addExportSupplier()"
ng-blur="vm.requiredField('es'+$index+'+Ref')"
ng-required="vm.exportSuppliers[$index].exportSupplier">
</div>
</div>
and my function to make sure field is required and not filled in
vm.requiredField = requiredField;
function requiredField (id) {
if($scope.vm.exportSupplier.form.$error.required) {
for (var i = 0; i < $scope.vm.exportSupplier.form.$error.required.length; i++) {
if ($scope.vm.exportSupplier.form.$error.required[i].$$attr.id == id) {
console.log('invalid required field');
var myEl = angular.element( document.querySelector( id ) );
myEl.addClass('top40');
}
}
}
}
What's wrong with my addClass?
Have a look at the below question. I think you could improve your approach by making use of the ternary operators in ng-class.
angular ng-class if-else expression
You can add conditional classes by using ng-class and form properties provided by angularjs
You need to add "name" attribute on form and specify novalidate on it
<input class="form-control" id="es{{$index}}+Ref" type="text"
name="inputName"
ng-class="{'error-class': formName.inputName.$invalid && formName.inputName.$touched}"
ng-model="supplier.agreementReference" ng-change="vm.addExportSupplier()"
ng-required="vm.exportSuppliers[$index].exportSupplier">
I think this will solve your purpose. No need to write blur function in controller.
There are two ways to handle this situation.
Option 1:
<div ng-class="{'class1': obj.success, 'classB': obj.error}"></div>
Option 2:
<div class="{{obj.classes}}"></div>
The only problem with this approach is that updating the template could become an issue. Sometimes the controller may not update the template. If that is the case, use $scope.$apply(); after the change has been made.
I'm about lose my mind with this problem. No form of jQuery selector seems to work in dynamically finding any elements above the link. I'm trying to access an element above the link and hide it. Using things like parent(), prev(), before(), closest(), ect. will show a non-null object but it won't respond to the hide() method.
<div class="row">
<div class="col-xs-5">
<div id="test_fields">
<li id="test_input" class="string input optional stringish">
<label class="label" for="test_input">Ingredient name</label>
<input type="text" name="test_input" value="afsfasf" id="test_input">
</li>
</div>
<input type="hidden" id="recipe_recipe_ingredients_attributes_0__destroy" name="recipe[recipe_ingredients_attributes][0][_destroy]">
Remove Ingredient
</div>
</div>
function remove_fields(link)
{
$(link).prev("input[type=hidden]").val('1'); // this doesn't work
var divToHide = $(link).prev('div');
$(divToHide).hide() // this doesn't work
//$('#test_fields').hide(); //this works
}
Try replacing the link as below:
Remove Ingredient
I'm not sure. But maybe this is the problem. Because I remember that I have had problem with 'this'previously and when I replaced that, it performed the job.
you can try .closest() and .find()
function remove_fields(link) {
$(link).closest('div[class^="col-xs"]').find("input[type=hidden]").val('1');
var div_to_hide = $(link).closest('div[class^="col-xs"]').find('#test_fields');
$(div_to_hide).hide();
//$('#test_fields').hide(); //this works
}
You can't change hidden input's "value" attribute by using .val(). You need to use:
$(link).prev("input[type=hidden]").attr('value', '1');
As I'm not really sure what do you want to do with this input, I'll just let it go like this.
.prev() fn goes only one previous element in the structure. As input is a <a>'s previous element, you can't select div like that. You can use .siblings() for instance.
$(link).siblings('div').hide();
If you break the code in pieces, it gets easier.
First I took the 'Link', from it I grabbed the nearest div above it, then I picked up the input.
I did not make many changes to your code.
function remove_fields(link)
{
var $link =$(link);
var $divToHide = $link.closest('div');
$divToHide.find("input[type='hidden']").val('1');
$divToHide.hide()
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<div class="row">
<div class="col-xs-5">
<div id="test_fields">
<li id="test_input" class="string input optional stringish">
<label class="label" for="test_input">Ingredient name</label>
<input type="text" name="test_input" value="afsfasf" id="test_input">
</li>
</div>
<input type="hidden" id="recipe_recipe_ingredients_attributes_0__destroy" name="recipe[recipe_ingredients_attributes][0][_destroy]">
Remove Ingredient
</div>
</div>
I'm trying to check whether a check box is checked, and if it is checked, then I want to add the "required" attribute to an adjacent text field. I've tried it two different ways with no success. Here are the form elements and my two JQuery attempts.
neither of those will actually trigger the event. My browser either does nothing at all or triggers an "Empty string passed to getElementById()." event
Form elements:
<div class="col-sm-5">
<label id="checkboxNumber-label" class="toplabel" for="checkboxNumber">Checkbox</label>
<g:textField name="checkboxNumber" value="${...checkboxNumber}" class="form-control" required="" aria-labelledby="checkboxNumber-label"/>
<label class="checkbox-inline">
<g:checkBox name="checkboxYesNo" id="checkboxYesNo" value="${...checkboxYesNo}" onclick="chkboxYesChecked()"/>
</label>
</div>
<div class="col-sm-5">
<label id="someTextField-label" class="toplabel" for="someTextField">Some Text Field Here</label>
<g:textField name="someTextField" id="someTextField" value="${...someTextField}" class="form-control" aria-labelledby="someTextField-label"/>
</div>
JQuery:
function chkboxYesChecked(){
if($('#checkboxYesNo').prop('checked')){
$('#someTextField').prop('required',true);
$('#someTextField').append('<span class="required-indicator">*</span>');
}else{
$('#someTextField').removeAttr('required');
}
}
$(document).ready(function() {
$('#checkboxYesNo').click(function() {
if($(this).is(":checked"))
{
$('#someTextField').prop('required',true);
$('#someTextField').append('<span class="required-indicator">*</span>');
} else {
$('#someTextField').removeAttr('required');
}
});
});
With your markup this becomes more convoluted than it needs to be.
$(document).on("click", ".checkbox-inline :checkbox", function () {
var $nextTextbox = $(this).closest("div").next("div").find(":text").first();
if (this.checked) {
$nextTextbox.prop("required", true).after('<span class="required-indicator">*</span>');
} else {
$nextTextbox.prop("required", false).next('.required-indicator').remove();
}
});
Notes
This approach uses event delegation.
There are no IDs involved, because I suppose you need the same thing more than once on your page. Tying it to a specific element ID is counter-productive.
This approach relies on the specific document structure from your sample Grails template. If you want something more flexible and easier-to-read, change your HTML.
This applies to all checkboxes that have a text field in the immediately following <div>. Use CSS classes on your elements to filter it/make it apply to specific ones only.
If there is no immediately following <div> with a text box, the function does nothing.
$(this).is(":checked") is superfluous. You don't need jQuery to find out if the current DOM element is checked. this.checked is a lot simpler and has the same effect.
Don't use inline event handlers (onclick="..."). Ever.
See it in action:
$(document).on("click", ".checkbox-inline :checkbox", function () {
var $nextTextbox = $(this).closest("div").next("div").find(":text").first();
if (this.checked) {
$nextTextbox.prop("required", true).after('<span class="required-indicator">*</span>');
} else {
$nextTextbox.prop("required", false).next('.required-indicator').remove();
}
});
input[required] {
background-color: #FFD1D1;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="col-sm-5">
<label id="checkboxNumber-label" class="toplabel" for="checkboxNumber">Checkbox</label>
<input type="text" name="checkboxNumber" value="${...checkboxNumber}" class="form-control" required="" aria-labelledby="checkboxNumber-label" />
<label class="checkbox-inline">
<input type="checkbox" name="checkboxYesNo" id="checkboxYesNo" value="${...checkboxYesNo}" />
</label>
</div>
<div class="col-sm-5">
<label id="someTextField-label" class="toplabel" for="someTextField">Some Text Field Here</label>
<input type="text" name="someTextField" id="someTextField" value="${...someTextField}" class="form-control" aria-labelledby="someTextField-label" />
</div>
i am trying to use parsely.js on my html page to validate input box. currently this html page contains one input box and one submit button. the structure is created using bootstrap 3 and this page does not contain Form tag.
<div role='form'>
<div class="row form-group">
<div class="col-xs-3">
<label title="fullname">Full Name</label>
</div>
<div class="col-xs-4">
<input type="text" class='form-control' id="name" name="fullName" data-parsley-required="true" data-parsley-required-message="Please insert your name"/>
</div>
</div>
<input type="submit" class= "btn btn-danger"/> </div>
i am calling parsley.js like
function validateInput()
{
var handle = $("input[name='fullName']").parsley({
successClass: "has-success",
errorClass: "has-error",
classHandler: function (el) {
return $(el).closest('.form-group');//not working
},
errorsWrapper: "<span class='help-block'></span>",
errorTemplate: "<span></span>",
});
return handle.isValid();
}
on click of Submit button. it returns true/false correctly and create span tag also. but error classes are not applied. even data-parsley-required-message'Please insert your name' is not working.
when i put alert($(el)) or alert(el) it gives [object Object]. i think el should be the input object on which i am calling parsley function. but i am not able to get el.attr('id') or any other attribute. it returns undefined. i have also tried
//return el.closest('.form-group');//not working
//return el.$element.closest('.form-group)//not working
//return $(el).$element.closest('.form-group')//not working
I can not use Form tag as i am using this html structure in sharepoint content edtior web part.
A few things first:
Parsley allows you to bind it to a field, so you won't have a problem without the form element (see docs);
The classHandler function recieves an object of the type ParsleyField. With this object, you can access the input element with el.$element (for example: alert(el.$element.attr('id'));
I have made the following changes to your validateInput function:
<script type="text/javascript">
function validateInput() {
$("input[name='fullName']").parsley({
successClass: "has-success",
errorClass: "has-error",
classHandler: function (el) {
return el.$element.closest('.form-group'); //working
},
errorsWrapper: "<span class='help-block'></span>",
errorTemplate: "<span></span>",
});
// Returns true / false if the field has been validated. Does not affect UI.
//$("input[name='fullName']").parsley().isValid());
// validate field and affects UI
$("input[name='fullName']").parsley().validate();
}
</script>
With this code, the message is presented correctly, and the successClass and errorClass are appended to the div form-group.
See the following working jsfiddle
Hello guys i have the below html for a number of products on my website,
it displays a line with product title, price, qty wanted and a checkbox called buy.
qty input is disabled at the moment.
So what i want to do is,
if the checkbox is clicked i want the input qty to set to 1 and i want it to become enabled.
I seem to be having some trouble doing this. Could any one help
Now i can have multiple product i.e there will be multiple table-products divs within my html page.
i have tried using jQuery to change the details but i dont seem to be able to get access to certain elements.
so basically for each table-product i would like to put a click listener on the check box that will set the value of the input-text i.e qty text field.
so of the below there could be 20 on a page.
<div class="table-products">
<div class="table-top-title">
My Spelling Workbook F
</div>
<div class="table-top-price">
<div class="price-box">
<span class="regular-price" id="product-price-1"><span class="price">€6.95</span></span>
</div>
</div>
<div class="table-top-qty">
<fieldset class="add-to-cart-box">
<input type="hidden" name="products[]" value="1"> <legend>Add Items to Cart</legend> <span class="qty-box"><label for="qty1">Qty:</label> <input name="qty1" disabled="disabled" value="0" type="text" class="input-text qty" id="qty1" maxlength="12"></span>
</fieldset>
</div>
<div class="table-top-details">
<input type="checkbox" name="buyMe" value="buy" class="add-checkbox">
</div>
<div style="clear:both;"></div>
</div>
here is the javascript i have tried
jQuery(document).ready(function() {
console.log('hello');
var thischeck;
jQuery(".table-products").ready(function(e) {
//var catTable = jQuery(this);
var qtyInput = jQuery(this).children('.input-text');
jQuery('.add-checkbox').click(function() {
console.log(jQuery(this).html());
thischeck = jQuery(this);
if (thischeck.is(':checked'))
{
jQuery(qtyInput).first().val('1');
jQuery(qtyInput).first().prop('disabled', false);
} else {
}
});
});
// Handler for .ready() called.
});
Not the most direct method, but this should work.
jQuery(document).ready(function() {
jQuery('.add-checkbox').on('click', function() {
jQuery(this)
.parents('.table-products')
.find('input.input-text')
.val('1')
.removeAttr('disabled');
});
});
use
jQuery('.add-checkbox').change(function() {
the problem is one the one hand that you observe click and not change, so use change rather as it really triggers after the state change
var qtyInput = jQuery(this).children('.input-text');
another thing is that the input is no direct child of .table-products
see this fiddle
jQuery('input:checkbox.add-checkbox').on('change', function() {
jQuery(this)
.parent()
.prev('div.table-top-qty')
.find('fieldset input:disabled.qty')
.val(this.checked | 0)
.attr('disabled', !this.checked);
});
This should get you started in the right direction. Based on jQuery 1.7.2 (I saw your prop call and am guessing that's what you're using).
$(document).ready(function() {
var thischeck;
$('.table-products').on('click', '.add-checkbox', function() {
var qtyInput = $(this).parents('.table-products').find('.input-text');
thischeck = $(this);
if (thischeck.prop('checked')) {
$(qtyInput).val('1').prop('disabled', false);
} else {
$(qtyInput).val('0').prop('disabled', true);
}
});
});
Removing the property for some reason tends to prevent it from being re-added. This works with multiple tables. For your conflict, just replace the $'s with jQuery.
Here's the fiddle: http://jsfiddle.net/KqtS7/5/