I have a task which required me to selectively validate an input using parsley, then exclude it from parsley once a valid value is entered (make it read only). BUT if the user clears it out, I need to remove the excluded data attribute, and re-bind parsley to the form. I'm doing all of this, but it's as if either a. excluded isn't actually removed, or b. parsley isn't re-bound. Here's some code:
var handleValidation = function($form, type){
//this works for the initial validation. Meaning it will return
//false the first time (before I add/remove the excluded data attr)
var formId = $form.attr('id');
var valid = $('#' + formId).parsley().validate({group: ""+type + "-validation", force: true});
return valid;
};
var reBindParsley = function(parsleyDef,formId){
$('#' + formId).parsley().destroy();
parsleyDef();
};
$clearIcon.click(function(){
//find input traversing the dom
$input.prop("readonly", false); //this works fine
$input.removeData('parsley-excluded'); //not sure if this is needed
$input.removeAttr('data-parsley-excluded'); //this gets removed from the visual representation of the DOM,
//but when i call validation on it, it's as if it's still there
reBindParsley();
})
//this will handle validating the input
$myInput.click(function(){
var valid = handleValidation($form, type);
if(valid){
makeReadOnly() //adds $myInput.attr('data-parsley-excluded', true);
reBindParsley(parsleyFunction, form);
}
});
So after calling the code in the clearIcon click event, the nexst time i click the input, it should call handleValidation(), and return false if the value isn't valid (like it does the FIRST time), but doesn't. I checked the visual representation of the DOM in firefox debugger, and the excluded data isn't there.
Any help is greatly appreciated!
Not 100% what's going on, but there is a simpler way.
$input.removeData('parsley-excluded') is not needed, nor is calling destroy.
var formId = $form.attr('id'); var valid = $('#' + formId).parsley... is simply $form.parsley...
Simply add to the form's excluded list [readonly] and you should be pretty much good to go. Otherwise, please post a working fiddle.
Related
I have two select boxes and i dont want that the user choose the same value in both.
I've tried some solution proposed on stack, but the materialized select is different from "normal select" as contains the options in list item elements.
However, i came up with a solution, which is all but elegant, i know..im a novice with these things.
But its not working as i intended.
I want to create an additional method for jquery validation plugin, in the example on fiddle i've inserted an additional field to show the error placement.
I think is pretty simple, but i just can't figure out how to do it...
$.validator.addMethod("checksameval", function(value, element) {
return $('#pref1').val() == $('#pref2').val()
}, "Pref1 and Pref2 cant have same value!");
https://jsfiddle.net/L24otmaa/5/
edited with custom method (still not working..)
The problem with your solution is that the form will still be valid and therefore it will be possible to send it anyway.
You have to add a custom validation. The plug-in offers a callback where you can check whatever you want before you finally submit it.
This can be done by adding your validation to a custom submit handler
var isSameValue = function() {
var val1 = $('#pref1').val();
var val2 = $('#pref2').val();
if (val1 == val2) {
$customErrorDiv.text('error you cant select same value twice!!');
return true;
}
$customErrorDiv.text('');
return false;
}
// check also on runtime
$('.course').change( function() {
isSameValue();
});
$("#application").validate({
// check before submitting
submitHandler: function(form) {
if (isSameValue()) {
return;
}
// submit the form manually
form.submit();
}
});
Fiddle: https://jsfiddle.net/7uhkddrx/
Documentation: https://jqueryvalidation.org/validate/
Of course you would have to style this message according to your needs.
EDIT: By the way: currently your select boxes are not set to be required.
EDIT2: added checking on runtime
I am using a backend where it is ideal that I send an ajax post request rather than using the default action on forms.
With this in mind, I need to extract the final fields that are selected in my form.
I have various text fields, radio buttons, checkboxes, etc.
I've always struggled gaining a good understanding of event delegation and event propagation. I'm not entirely sure if this is the topic I should be worried about with what I am trying to achieve.
I know I can write code that grabs all of the information in my form by placing an ID on each field and a have a function extract each value on the ID such as:
function example(){
var field0 = $('#field0').val();
var field1 = $('#field1').parent().hasClass('active')
// ... and more
}
I've used this pattern for a while and I don't feel like it is efficient.
I have two pattern idea, but I am still not sure if this is a "common practice"
Since I am not concerned about the data in each field until the form is submitted, I could run a loop on all of my input based fields on my form and extract the contents, instead of assigning an ID to each individual input field.
I can listen to changes on the form (I am not exactly sure how to do this, this is where event delegation/propagation will come into play). Instead of waiting for the submit button to gather all the info in the form, I will have some type of listener that detects a change on the form (not sure if that is possible).
I've been using my current pattern for several months and would like to improve myself, If anyone has any suggestions, links, or criticism about my thoughts on a new approach I'd appreciate it.
So, you basically propose 3 ways to get all form fields with a value on submit (or a similar event):
hard-code IDs and retrieve their values, e.g.
var field_a = document.getElementById('a')
, field_b = document.getElementById('b')
, form = document.getElementById('my_form');
form.addEventListener('submit', function() {
fetch('//your/api/endpoint', {
method: 'POST',
body: JSON.stringify({a: field_a.value, b: field_b.value})
});
});
loop all and retrieve their values, e.g.
var form = document.getElementById('my_form');
form.addEventListener('submit', function() {
var values = [].reduce.call(
form.querySelectorAll('input, textarea, select'),
function(values, element) {
values[element.name] = element.value;
return values;
},
{}
);
fetch('//your/api/endpoint', {
method: 'POST',
body: JSON.stringify(values)
});
});
watch for changes inside the form, accumulate them
var form = document.getElementById('my_form')
, state = {};
form.addEventListener('change', function(e) {
state[e.srcElement.name] = e.value;
});
form.addEventListener('submit', function() {
fetch('//your/api/endpoint', {
method: 'POST',
body: JSON.stringify(state)
});
});
From a performance perspective, option 1. will be the fastest, followed by 2 followed by 3 (with the last 2 I'm not 100% certain, querySelectorAll can be expensive, but listening for tons of change events might be as well -- depends on how often change events are triggered I'd say).
From development perspective (how long does it take to set up a form), 2 and 3 should not be that different as they are both generic (and you can use my code sample as a start).
"Real" data-binding (like Angular) or "pure state" (like React) pretty much come down to options 2/3 as well (just that the framework will perform the heavy lifting for you).
Regarding option 3 (listening for a change on the whole form): https://stackoverflow.com/a/4616720/1168892 explains quite well how event bubbling in JavaScript happens. To use that you have to make sure that no element inside the form cancels the change event (otherwise it would not bubble to the form itself). To not cancel events is the default behavior, so you would have to explicitly make this wrong (and with that you can just have an eye on it in your implementation).
I didn't use jQuery in my examples as that can all be done by browsers directly now. What I used are Element.querySelectorAll, Array.reduce and window.fetch.
Pattern #1 (use serializeArray)
$('#formId').on('submit', function(e){
var allData;
e.preventDefault();
allData = $(this).serializeArray();
// use the allData variable when sending the ajax request
});
Pattern #2 (use the delegated form of $container.on('event', 'selector', ..) and the change event)
$('#formId').on('change', 'input,textarea,select', function(){
var element = $(this), // element that changed
value = element.val(); // its new value
// do what you want ..
});
Without jquery I once wrote a function that return in an object all input value tie with its name.
I think it's better than plain id link, because you don't have to worry about what's inside your form, as long as your giving a name attribute to your inputs.
function getFormData(form) {
var data = {};
for (var i = 0; i < form.elements.length; i++) {
var input = form.elements[i];
if (input.value && input.type !== 'submit' && input.type !== 'button') {
data[input.name] = input.value;
}
}
return data;
}
All you need to do is passing your form like this:
var form = document.querySelector('.monFormulaire');
// your form data
var data = getFormData(form);
I have code like below to perform some conditional validation on fields in my form. The basic idea being that if something is entered in one field, then all the fields in this 'group' should be required.
jQuery.validator.addMethod('readingRequired', function (val, el) {
//Readings validation - if a reading or a date is entered, then they should all be ntered.
var $module = $(el).closest('tr');
return $module.find('.readingRequired:filled').length == 3;
});
//This allows us to apply the above rule using a CSS class.
jQuery.validator.addClassRules('readingRequired', {
'readingRequired': true
});
//This gets called on change of any of the textboxes within the group, passing in the
//parent tr and whether or not this is required.
function SetReadingValidation(parent) {
var inputs = parent.find('input');
var required = false;
if (parent.find('input:filled').length > 0) {
required = true;
}
if (required) {
inputs.addClass("readingRequired");
}
else {
inputs.removeClass("readingRequired");
}
}
//This is in the document.ready event:
$("input.reading").change(function () {
SetReadingValidation($(this).closest("tr"));
});
This works fine, and I've used pretty much the same code on other pages with success. The slight problem here is that when i enter a value into the first textbox and tab out of it, the validation fires and an error message is displayed. This doesn't happen on other pages with similar code, rather the validation waits until the form is first submitted. Does anybody have any idea why this might be happening?
Hmm. You know how it goes, post a question and then find a solution yourself. Not sure why this works exactly, but changing my binding from:
$("input.reading").change(function () {
SetReadingValidation($(this).closest("tr"));
});
to
$("input.reading").blur(function () {
SetReadingValidation($(this).closest("tr"));
});
Seems to have solved this issue. Would still appreciate being enlightened as to why that might be...
I ahave some ajax that is fired when a checkbox is clicked, it essentially sends a query string to a PHP script and then returns the relevant HTML, however, if I select a select it works fine if I then slect another checkbox as well as the previous I get no activity what so ever, not even any errors in firebug, it is very curious, does anyone have any ideas?
//Location AJAX
//var dataObject = new Object();
var selected = new Array();
//alert(selected);
$('#areas input.radio').change(function(){ // will trigger when the checked status changes
var checked = $(this).attr("checked"); // will return "checked" or false I think.
// Do whatever request you like with the checked status
if(checked == true) {
//selected.join('&');
selected = $('input:checked').map(function() {
return $(this).attr('name')+"="+$(this).val();
}).get();
getQuery = selected.join('&')+"&location_submit=Next";
alert(getQuery);
$.ajax({
type:"POST",
url:"/search/location",
data: getQuery,
success:function(data){
//alert(getQuery);
//console.log(data);
$('body.secEmp').html(data);
}
});
} else {
//do something to remove the content here
alert($(this).attr('name'));
}
});
I see you are using the variable checked = $(this).attr("checked"); I think this might be a problem because checked is a standard JS attribute native to JS. You can compare checked normally on an element and see if it is true or false. I would start by changing the name of your variable and move on from there.
The other thing that could be happening is you might be losing your listener which might be caused by your variable selected. You do not need to declare selected outside your listener. Just declare it inside when you set it.
And if THAT doesn't help, providing some markup would help debug this issue because it seems like there is a lot going on here.
Good luck.
I turned out that because my ajax loads in a new page on success the actions were not being put on the elements as they were only being loaded once on DOM ready, I moved the all the script into a function and call that on DOM Ready now and it works great.
All I want to do is to check if the textfield has been changed. If not, I want to highlight the boxes when submit is pressed. How do I do that? Seems very simple, but not sure why every other solution is so complicated.
Building upon Pim's answer, you can associate the changed flag for each text field using jQuery's data API.
// initially, assign changed to false for all text fields
$("input:text").data("changed", false);
// if any field changes, set its changed flag to true
$("input:text").change(function() {
$(this).data("changed", true);
}
// finally on submission, get all text fields that did not
// change by checking their "changed" property
var unchangedItems = $("input:text").filter(function() {
return $(this).data("changed") === false;
});
You can use the jQuery Validate plugin.
Bassicly, you just say it has never been changed, and when it cahnges, you set a flag saying is has been changed:
var changed = false;
$('#textfield').change(function(){
changed = true;
});
if(changed){
$('.textbox').each(function(){
$(this).addClass('.highlighted');
//or something like this, whatever you want to do to highlight them
});
}