I\'m looking for a way to submit only changed form fields to the server. So, let\'s say I have a form
Another approach would be to serialize
the form when the page loads, and then on submit, only submit the changes.
$(function() {
var $form = $('form');
var startItems = convertSerializedArrayToHash($form.serializeArray());
$('form').submit() {
var currentItems = convertSerializedArrayToHash($form.serializeArray());
var itemsToSubmit = hashDiff( startItems, currentItems);
$.post($form.attr('action'), itemsToSubmit, etc.
}
});
Then, all you have to write is the hashDiff
function, which is straightforward and generally useful.
This is nice because it can easily be packaged into a plugin, and it can work repeatedly on the same form if you're using Ajax.
function hashDiff(h1, h2) {
var d = {};
for (k in h2) {
if (h1[k] !== h2[k]) d[k] = h2[k];
}
return d;
}
function convertSerializedArrayToHash(a) {
var r = {};
for (var i = 0;i<a.length;i++) {
r[a[i].name] = a[i].value;
}
return r;
}
Here's a minimal test:
describe('hashDiff()', function() {
it('should return {} for empty hash',function() {
expect(hashDiff({},{})).toEqual({});
});
it('should return {} for equivalent hashes',function() {
expect(hashDiff({a:1,b:2,c:3},{a:1,b:2,c:3})).toEqual({});
});
it('should return {} for empty hash',function() {
expect(hashDiff({a:1,b:2,c:3},{a:1,b:3,c:3})).toEqual({b:3});
});
});
just compare betwen current value and default value like this:
var toBeSubmited = new FormData();
for(var i in formObj)
if('value' in formObj[i] && formObj[i].value!=formObj[i].defaultValue){ //is an input or select or textarea
toBeSubmited.add(i, formObj[i].value);
}
//now send "toBeSubmited" form object
$.ajax(...)
Another option would be to mark the fields as disabled
before they are submitted. By default disabled
fields will not be serialized or submitted with a default form post.
Simple example:
function MarkAsChanged(){
$(this).addClass("changed");
}
$(":input").blur(MarkAsChanged).change(MarkAsChanged);
$("input[type=button]").click(function(){
$(":input:not(.changed)").attr("disabled", "disabled");
$("h1").text($("#test").serialize());
});
on jsfiddle.
I may be missing something but I tried this and the hashDiff function returned an "undefined" error for the first form element it tried to process.
I implemented something a bit simpler which seems to work fine.
$('#submitChangesOnlyButton').click(function () {
var formAfterEdit = $('#myForm').serializeArray()
var itemsToSubmit = checkDiff(formBeforeEdit,formAfterEdit);
})
...
function checkDiff(before, after) {
var whatsChanged = [];
for (i = 0; i < before.length; i++) {
if (after[i].value !== before[i].value) {
whatsChanged.push(after[i]);
}
}
return whatsChanged;
}
My solution
$('form').each(function(){
var $form = $(this);
if (!$form.hasClass('send-all')){
var watchedFields = 'input:not([type="hidden"]):not([type="checkbox"]), select, textarea';
$form.find(watchedFields).addClass('watched').on('change', function() {
$(this).addClass('changed');
});
$form.submit(function(e){
var data = $form.serializeArray();
// Loop fields
for (i = 0; i < data.length; i++){
$field = $form.find('[name="' + data[i].name + '"]');
// Prevent send unchanged fields
if ($field.hasClass('watched') && !$field.hasClass('changed')) $field.prop('disabled', 'disabled');
// Send changed but set to readonly before
else $field.prop('readonly', 'readonly');
}
return true;
});
}
});
You could add an 'oldvalue' parameter to the input field. Populate this value at the time the page is generated either with JavaScript or on the server-side.
<input name="field1" value="10" oldvalue="10">
Then use the following function to serialize:
function serializeForm() {
data = "";
$("input,textarea").each(function (index, obj) {
if ($(obj).val() != $(obj).attr("oldvalue")) {
data += "&" + $(obj).serialize();
}
});
return data.substr(1);
}
After the data has been sent to the server, your script could update the 'oldvalue' parameters to prevent the data from being sent again unless a further change is made.