Prototype: Form Serialization and Deserialization
In the web community Prototype is a "must" for who wants to implement unobstrusive javascript and web2.0 applications. Why? 'Cause prototype makes things simplest and more Object Oriented.
The fact is that Javascript is often used as a "functional language" and not Object Oriented. Maybe because Javascript is not a clean OO language or maybe because, for historical reasons, people used it to handle (most of the time) the "onclick" event...
With the revolution of these days, javascript is becoming a way to enhance the user experience in web applications (take Ajax for example...).
So here's it: Prototype is coming to help us develop web2.0 application, using Objects in a more simple way, introducing classes for Ajax integration and much more. Maybe one of these days I'll write a good Prototype in Action post, but for the moment use this link as reference:
http://www.sergiopereira.com/articles/prototype.js.html
Update: now Prototype has its own official documentation. Read it here: http://www.prototypejs.org/learn.
Ok...now that you have "the tool" let's see an interesting feature: with prototype you can serialize a form given its id with a simple call:
This function allow you transform a form in a encoded string, containing all values from the passed form.
Ok, now you can save your form (for example via AJAX on your server...), but what you can do if you want to deserialize back it to your client? No way with the current version of Prototype (1.4)!
So I wrote a simple extension to allow you deserialize forms given a serialized string.
Here is a simple example:
The call to the function is simple:
You can find the .js file here
A complete example can be downloaded here
The fact is that Javascript is often used as a "functional language" and not Object Oriented. Maybe because Javascript is not a clean OO language or maybe because, for historical reasons, people used it to handle (most of the time) the "onclick" event...
With the revolution of these days, javascript is becoming a way to enhance the user experience in web applications (take Ajax for example...).
So here's it: Prototype is coming to help us develop web2.0 application, using Objects in a more simple way, introducing classes for Ajax integration and much more. Maybe one of these days I'll write a good Prototype in Action post, but for the moment use this link as reference:
http://www.sergiopereira.com/articles/prototype.js.html
Update: now Prototype has its own official documentation. Read it here: http://www.prototypejs.org/learn.
Ok...now that you have "the tool" let's see an interesting feature: with prototype you can serialize a form given its id with a simple call:
var serializedForm = Form.serialize('myform');
This function allow you transform a form in a encoded string, containing all values from the passed form.
Ok, now you can save your form (for example via AJAX on your server...), but what you can do if you want to deserialize back it to your client? No way with the current version of Prototype (1.4)!
So I wrote a simple extension to allow you deserialize forms given a serialized string.
Here is a simple example:
The call to the function is simple:
Form.deserialize(formElement, serializedString)
You can find the .js file here
A complete example can be downloaded here


22 Comments:
modified to ensure that multi-select textboxes get selected properly.
@@ -82,6 +82,7 @@
selectMany: function(element, data) {
for(i = 0; i < element.options.length; i++) {
op = element.options[i];
+ op.selected = false;
if(op.value == data)
op.selected = true;
}
Thank you very much, I modified the source code for the download.
When using your script, I found it difficult, in my situation to produce the same form that you had as your example, ie with the appropriate IDs on every input. Instead, I ended up modifying the script to work with names rather than IDs. In case you are interested, it is available here.
Thank you very much mattj. Nice work...I think we could modify this source code to allow both solutions to live togheter, maybe using a boolean parameter in the deserialize method and then switching to the name or to the id. What do you think?
What about cross browser...it shows errors on IE and does not work. Anyone?
Hi...the script IS cross browser, so you can download and use it without problem. There is an error here in this page, I will fix it as soon as possible.
An excellent post...just what I was looking for. I just wanted to let you know that there is indeed one error in the js file:
Object.extend(Form, {
deserialize: function(form, data) {
data = decodeURIComponent(data);
var tokens = data.split('&');
tokens.each(
function(input, index) {
var data = input.split('=');
var id = data[0];
var value = data[1];
if(id != form.id && value != 'undefined' && value != null)
Form.Element.deserialize(id, value);
}
);
}, <-- no comma here
});
It's an easy fix. This is a great addition to the prototype library, and did exactly what I needed. Thanks for sharing! Nice site.
Thank you very much Wesley. I fixed the problem and uploaded the new js file on the server. I think this was the error showing on IE...
I have a question. Suppose someone has a street address like 5th & Main. The user has decided to use the & character in the address which is a common problem and I have to let them use it, but Form.Deserialize splits on the & character.
var tokens = data.split('&');
any suggestions on how to deal with this?
thanks
Nick
Thank you Nick. This was a bug and was not so complicated to be solved. I updated this post and I also made a new one to announce the new release.
I have been trying to use this and I can not seem to get it to work with multi-select boxes. I dont know if it has to do with the values having spaces in them or not. I tried removing the spaces and it would select the last one that was selected but it would only select one
I cant determine how to edit a post so I am following up.
I found out why it is only selecting one and not others.
It is going though the entire select box again and is setting the selected = false to ones that have possibly already been set to true.
So what I did was before deserialization occurs I clear the form and do not reset the selected = false
so undo the change recommended earlier to have op.selected = false
another bug: if inputs have different value for the name and id attribute, it doesn't work.
Ok guys, it should be working (again). Sorry for these bugs, I think the new version of prototype changed the way to manage form inputs...
Thanks a lot for this.
I think I found a bug. I wasn't able to get [a href=""]Whatever[/a] (excuse my brackets) to deserialize (textarea). It was getting cutoff at the =. I did this to fix it.
tokens.each(
function(data, index) {
data = data.split('=');
var id = data[0];
var value = decodeURIComponent(data[1]);
if(id != form.id && value != 'undefined' && value != null)
Form.Element.deserialize(form, id, value);
}
);
Thanks for the awesome code - exactly what I needed (I can't believe more people haven't needed this..)
Anyway, I found one improvement. I use checkbox arrays in some of my forms (checkboxes with the same id/name - just different values). The current js won't handle these elements.
I played around and found that if you use the same code as the radio button code instead of 1-1 compare, everything works fine (plus the array method also works fine for regular one-off checkboxes.)
So, just change the inputSelector: code to the following
****
inputSelector: function(element, data) {
var name = element.name;
var checkboxs = Form.getInputs(element.form, 'checkbox', element.name);
for(var i = 0, len = checkboxs.length; i < len; i++) {
var checkbox = checkboxs[i];
if(checkbox.value == data)
checkbox.checked = true;
}
},
***
Thank you very much Art Harrison, I just updated the .js file and the .tgz example archive.
Hi, I thank you for your fantastic code, but I'm having some problems.
Right now, I'm serializing to object hash since that is the preferred way since prototype 1.5. Then I convert it to JSON string using toJSON and save that string to the database so I can retrieve it again when I need it.
The problem is, my form selects something on my multi-select elements and checks something on checkboxes by default when you load the page. Since your script does the form.reset, the form will get resetted to whatever it was by default, so when you call deserialize (in my case, I use deserializeJSON) it will apply whatever you serialized before with the default ones, rather than selecting/checking only the ones that you saved. This problem also has something to do with how serialize works - it does not include the checkbox element that you have not checked, so it won't process those elements when you deserialize. Also, it won't deselect the option on the multi-select element that you don't want it selected.
I am using prototype 1.6.0.2.
I also found out that checkboxes don't work properly in latest version of Safari on Windows. It won't check on the boxes that is supposed to. Might be a Safari bug.
I got it fixed (at least from what I see) and tested on the example page from your blog, modified so it has something selected/checked by default.
Here is the modified code on object.extend Form:
Object.extend(Form, {
deselectCheckBoxes: function(form) {
var elements = Form.getElements(form);
for (var index = 0, len = elements.length; index < len; index++) {
var item = elements[index];
if(item.type.toLowerCase() == 'checkbox') {
var checkboxs = Form.getInputs(item.form, 'checkbox', item.name);
for(var i = 0, len = checkboxs.length; i < len; i++) {
checkboxs[i].checked = false;
}
}
}
},
deserialize: function(form, data) {
form = $(form);
form.reset();
Form.deselectCheckBoxes(form);
(rest of the code)
},
deserializeJSON: function(form, data) {
form = $(form);
form.reset();
Form.deselectCheckBoxes(form);
(rest of the code)
}
});
and add this code into selectMany:
selectMany: function(element, data) {
for(var index = 0, len = element.options.length; index < len; index++) {
element.options[index].selected = false;
}
(rest of the code)
This is working for me as far as I know.
Also, another problem is the input text box will not blank out the default value even though the serialized string points out that the text box should be blanked. Try testing on the test page and serialize it while the text box is empty.
Sorry for this constant bugging. I hope this is the last.
Upon reading the inputSelector, I don't see why you need to check the value of the checkbox. Since the only checkbox elements that will be on the serialized string are the ones that are actually checked, there's no need to check on their values. So I removed that line and I confirmed that it works on IE6/7, Firefox 2/3, and Safari in Windows. I haven't tested on opera, but I wasn't going to install another browser on it XD
Here is the complete code:
inputSelector: function(element, data) {
var name = element.name;
var checkboxs = Form.getInputs(element.form, 'checkbox', element.name);
for(var i = 0, len = checkboxs.length; i < len; i++) {
checkboxs[i].checked = true;
}
},
I have confirmed this Form.serialize behavior in prototype 1.5.0.0 and up.
Thanks solgae, I updated the script, merging and modifying a little your code. I updated the release, here the new post
Post a Comment
Links to this post:
Create a Link
<< Home