Выберите/отправьте только измененные поля формы с помощью jQuery

Я ищу способ отправить только измененные поля формы на сервер. Итак, скажем, у меня есть форма

<form>
    <input type="text" name="a"/>
    <select name="b">...</select>
    <input type="checkbox" name="c"/>
</form>

который уже заполнен определенными данными. Пользователь редактирует форму и нажимает кнопку submit. Если пользователь только изменил вход b, то я хочу отправить только вход b. Если изменились только а и с, я хочу представить только а и с. И так далее.

Я мог бы написать кое-что для себя, но мне интересно, может быть, есть что-то там, что я мог бы использовать? В идеале я бы хотел, чтобы код был коротким. Что-то вроде этого было бы идеально:

$('form').serialize('select-only-changed');

Кроме того, я столкнулся с этим http://code.google.com/p/jquery-form-observe/, но я вижу, что с ним возникают проблемы. Этот плагин работает прочно? Спасибо!

EDIT: Спасибо всем, кто дал ответ. Я выбрал ndp ответ, потому что он лучше всего подходит к тому, что у меня уже есть.

Ответы

Ответ 1

Другим подходом было бы serialize форму при загрузке страницы, а затем отправить ее только с отправкой изменений.

$(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.
  }
});

Тогда все, что вам нужно написать, это функция hashDiff, которая проста и в целом полезна.

Это хорошо, потому что его можно легко упаковать в плагин, и он может многократно работать в той же форме, если вы используете 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;
}

Здесь минимальный тест:

  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});
    });
  });

Ответ 2

Другим вариантом было бы пометить поля как disabled до их отправки. По умолчанию поля disabled не будут сериализованы или отправлены с сообщением формы по умолчанию.

Простой пример:

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());
});

на jsfiddle.

Ответ 3

Вы можете добавить параметр "oldvalue" в поле ввода. Заполните это значение во время создания страницы с помощью JavaScript или на стороне сервера.

<input name="field1" value="10" oldvalue="10">

Затем для сериализации используйте следующую функцию:

function serializeForm() {
    data = "";
    $("input,textarea").each(function (index, obj) {
        if ($(obj).val() != $(obj).attr("oldvalue")) {
            data += "&" + $(obj).serialize();
        }
    });
    return data.substr(1);
}

После того, как данные были отправлены на сервер, ваш script может обновить параметры "oldvalue", чтобы предотвратить повторное отправку данных, если не будет сделано дальнейшее изменение.

Ответ 4

Простейшим решением было бы добавить что-то вроде:

$(function() {

    $("input, select").change(function() {
        $(this).addClass("changed");
    });

});

Затем просто выберите в классе .changed, чтобы получить элементы, которые были изменены.

Дополнительная информация о событии jQuery change: http://api.jquery.com/change/

Как указывает @Martin ниже, событие change запускается только для текстовых входов после нажатия на вход. Если это просто для сохранения некоторой пропускной способности, я бы рекомендовал привязать к событию click вместо этого. Вы можете отправить несколько полей, которые на самом деле не изменились, но, вероятно, лучше выпустить их на сторону слишком много назад, чем слишком мало.

Ответ 5

Вы можете попробовать добавить класс в каждое поле, которое было изменено, и удалить остальные до вызова $('form').serialize().

$(function() {
    $(':input').change(function() {
        $(this).addClass('changed');
    });
    $('form').submit(function () {
        $('form').find(':input:not(.changed)').remove();
        return true;
    });
});

Хотя это решение является разрушительным и работает только в том случае, если вы не используете AJAX (решение существует даже для AJAX, но оно становится еще более сложным).

Ответ 6

просто сравните betwen текущее значение и значение по умолчанию, например:

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(...)

Ответ 7

Мне может быть что-то не хватает, но я попробовал это, и функция hashDiff вернула ошибку "undefined" для первого элемента формы, который пыталась обработать.

Я реализовал что-то более простое, что, кажется, работает нормально.

$('#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;
}