Serialize HTML Form with Embedded Objects - javascript

I have the following form ...
<form id="my-form">
<fieldset name="address">
<input name="streetAddress" type="text" placeholder="Street Address"><br>
<input name="city" type="text" placeholder="City"><p>,</p>
<input name="state" type="text" placeholder="State">
<input name="zipCode" type="text" placeholder="Zip Code">
</fieldset>
<fieldset name="dimensions">
<input name="length" type="text" placeholder="length">
<input name="width" type="text" placeholder="width">
<input name="height" type="text" placeholder="height">
</fieldset>
</form>
I need to serialize it into JSON with JS, but I need to have the address's fields warped in an address object, and the dimensions's fields warped in a dimensions object.
Something like this ...
{
'address':{'streetAddress':'111 Candy Ln', 'city':'Los Angeles', ...},
'dimensions':{...}
}
How do you do this cleanly, idealy without having to write my own function for doing this? I have of course seen scripts to serialize, but nothing that will do embedded objects.

Have you tried putting all the fields into arrays?
<fieldset name="address">
<input name="address[streetAddress]" type="text" placeholder="Street Address"><br>
<input name="address[city]" type="text" placeholder="City"><p>,</p>
<input name="address[state]" type="text" placeholder="State">
<input name="address[zipCode]" type="text" placeholder="Zip Code">
</fieldset>
Heres an example, using the serializeObject plugin
Just include that script and you can convert any form into a multi layered JSON object.
DEMO HERE
Using this plugin...more info here Convert form data to JavaScript object with jQuery
(function($){
$.fn.serializeObject = function(){
var self = this,
json = {},
push_counters = {},
patterns = {
"validate": /^[a-zA-Z][a-zA-Z0-9_]*(?:\[(?:\d*|[a-zA-Z0-9_]+)\])*$/,
"key": /[a-zA-Z0-9_]+|(?=\[\])/g,
"push": /^$/,
"fixed": /^\d+$/,
"named": /^[a-zA-Z0-9_]+$/
};
this.build = function(base, key, value){
base[key] = value;
return base;
};
this.push_counter = function(key){
if(push_counters[key] === undefined){
push_counters[key] = 0;
}
return push_counters[key]++;
};
$.each($(this).serializeArray(), function(){
// skip invalid keys
if(!patterns.validate.test(this.name)){
return;
}
var k,
keys = this.name.match(patterns.key),
merge = this.value,
reverse_key = this.name;
while((k = keys.pop()) !== undefined){
// adjust reverse_key
reverse_key = reverse_key.replace(new RegExp("\\[" + k + "\\]$"), '');
// push
if(k.match(patterns.push)){
merge = self.build([], self.push_counter(reverse_key), merge);
}
// fixed
else if(k.match(patterns.fixed)){
merge = self.build([], k, merge);
}
// named
else if(k.match(patterns.named)){
merge = self.build({}, k, merge);
}
}
json = $.extend(true, json, merge);
});
return json;
};
})(jQuery);

I have two solutions for this:
Name the fields after the fieldsets (like the proposal above): address[street], address[zipcode], etc.
Give the fieldsets some unique id's.
In both cases I suggest you using this library I made: https://github.com/serbanghita/formToObject
Call it like this:
Case 1: var myFormObj = new formToObject('myFormId');
Case 2: var myFormObj = new formToObject('myFieldsetId');

Related

Get data for form input array using specific key

So, let's say I have an HTML form like this:
<form id="myForm">
<input type="text" name="dummy">
<input type="text" name="people[0][first_name]" value="John">
<input type="text" name="people[0][last_name]" value="Doe">
<input type="text" name="people[1][first_name]" value="Jane">
<input type="text" name="people[1][last_name]" value="Smith">
</form>
And I want to get a JavaScript array that matches the values of real. For example:
// If there was a sweet function for this...
var people = getFormDataByInputName( 'people' );
// Value of `people` is...
// [
// {
// 'first_name' : 'John',
// 'last_name' : 'Doe'
// },
// {
// 'first_name' : 'Jane',
// 'last_name' : 'Smith'
// }
// ]
Is there any easy way of doing that for just a specific form item (in this case, people)? Or would I have to serialize the entire form an then just extract the element I want?
I also thought of potentially using the following approach:
var formData = new FormData( document.querySelector( '#myForm' ) );
var people = formData.get( 'people' );
But that doesn't appear to work; people is just null after that.
You could do this with plain js using reduce method and return each person is one object.
const form = document.querySelectorAll('#myForm input');
const data = [...form].reduce(function(r, e) {
const [i, prop] = e.name.split(/\[(.*?)\]/g).slice(1).filter(Boolean)
if (!r[i]) r[i] = {}
r[i][prop] = e.value
return r;
}, [])
console.log(data)
<form id="myForm">
<input type="text" name="dummy">
<input type="text" name="people[0][first_name]" value="John">
<input type="text" name="people[0][last_name]" value="Doe">
<input type="text" name="people[1][first_name]" value="Jane">
<input type="text" name="people[1][last_name]" value="Smith">
</form>
function getObject(name, key) {
if(key.includes(name)) {
var splitStr = key.split(/\[|\]/g);
return {
index: splitStr[1],
key: splitStr[3],
}
}
return null;
}
function getFormDataByInputName(name) {
var formData = new FormData( document.querySelector('#myForm'));
var results = [];
for (var key of formData.keys()) {
var obj = getObject(name, key);
if (obj) {
if (results[obj.index]) results[obj.index][obj.key] = formData.get(key);
else results[obj.index] = { [obj.key]: formData.get(key) };
}
}
return results;
}
var people = getFormDataByInputName('people');
console.log(people);
<form id="myForm">
<input type="text" name="dummy">
<input type="text" name="people[0][first_name]" value="John">
<input type="text" name="people[0][last_name]" value="Doe">
<input type="text" name="people[1][first_name]" value="Jane">
<input type="text" name="people[1][last_name]" value="Smith">
</form>
Your code won't work because to HTML/JS name is just a string that it sends to the server when the form is submitted (the name in the name/value pairs). You might think it is arrays, but HTML/JS doesn't.
So no one-liner to get the job done. Try this: In your HTML, add <div class="name"> ...
(UPDATE: thanks for the idea, #Nenad, I've never tried one of these snippets)
var people = [];
$('.name').each(function() {
people.push({
first_name: $('input:nth-child(1)', this).val(),
last_name: $('input:nth-child(2)', this).val()
});
});
console.log(people);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<form id="myForm">
<input type="text" name="dummy">
<div class="name">
<input type="text" value="John">
<input type="text" value="Doe">
</div>
<div class="name">
<input type="text" value="Jane">
<input type="text" value="Smith">
</div>
</form>
Use CSS attribute prefix selector, such as
form.querySelectorAll('[name^="people[]"]')
You can use a for-loop to get all peoples, as such
const MAX_PEOPLES = 2;
const list = [];
for (i = 0; i <= MAX_PEOPLES; i++) {
const eles = form.querySelectorAll(`[name^="people[${i}]`);
if (eles.length !== 2)
break;
list.push({
first_name: eles[0].value,
last_name: eles[1].value
});
}
that yields
[
{
"first_name":"John",
"last_name":"Doe"
},
{
"first_name":"Jane",
"last_name":"Smith"
}
]

How to convert the input name dot to json format in simple way?

I have used the struts json plugin and tried to convert the form data to json format to submit by ajax.
I have two cases in the HTML
<form>
<input type="text" name="user.name" value="Tom"></p>
<input type="text" name="user.location" value="China"></p>
<input type="text" name="user.data[0].id" value="993"></p>
<input type="text" name="user.data[0].accountId" value="123"></p>
<input type="text" name="user.data[1].id" value="222"></p>
<input type="text" name="user.data[1].accountId" value="333"></p>
</form>
What I expected is to convert it to the json structure:
{
user : {
name: "Tom",
location : "China",
data: [
{
id : 993,
accountId : 123
},
{
id : 222,
accountId : 333
}
]
}
}
I know how to declare the json data and declare the attributes one by one.
I would like to have the better way to make each form to be in json format using simple way rather than declaring the parameter one by one in json format.
Appreciate for any suggestion or advice. Thank you.
Provided your form is exactly like that
Using a plain JS approach
<form class="userform">
<input type="text" class="username" value="Tom"></p>
<input type="text" class="userlocation" value="China"></p>
<input type="text" class="userid" value="993"></p>
<input type="text" class="useraccountid" value="123"></p>
<input type="text" class="userid2" value="222"></p>
<input type="text" class="useraccountid2" value="333"></p>
</form>
Then assign the values to the object
var frm = document.getElementsByClassName('userform');
//initialize blank object and keys
var user = {},
user.name = "",
user.location = "",
user.data = [];
//get all child input elements
for(var i = 0; i < frm.length; i++){
var uname = frm[i].getElementsByClassName('username')[0];
var uloc = frm[i].getElementsByClassName('userlocation')[0];
var uid = frm[i].getElementsByClassName('userid')[0];
var uaccid = frm[i].getElementsByClassName('useraccountid')[0];
var uid = frm[i].getElementsByClassName('userid2')[0];
var uaccid = frm[i].getElementsByClassName('useraccountid2')[0];
//assign values to object here
user[name] = {}; //assigning a parent property here, the name for example.
user[name].name = uname.value;
user[name].location = uloc.value;
user[name].data.push({
'id': uid.value
'accountId': uaccid.value
});
user[name].data.push({
'id': uid2.value
'accountId': uaccid2.value
});
}
JSON.stringify(user); //convert to JSON (or ignore if you want a plain object)
Output would be this in JSON format
{
user :{
Tom: {
name: "Tom",
data: [
{
id : 993,
accountId : 123
},
{
id : 222,
accountId : 333
}
]
},
Jerry: {
//more data
},
Courage: {
//more data
}
}
}
Hope this helps
If your input fields are many, like id3, accountid3, 4, 5, 6. You have to loop through the classes that you assign to these two repetitive fields
Here you go with a solution using jQuery https://jsfiddle.net/pnz8zrLx/2/
var json = {};
$('button').click(function(){
$('form').each(function(i){
json["user" + i] = {};
json["user" + i].data = [];
var tempJSON = {};
$('form:nth-child(' + (i+1) + ') input[type="text"]').each(function(){
if($(this).attr('name') === 'name' || $(this).attr('name') === 'location'){
json["user" + i][$(this).attr('name')] = $(this).val();
} else {
tempJSON[$(this).attr('name')] = $(this).val();
if(tempJSON != {} && $(this).attr('name') === 'accountId'){
json["user" + i].data.push(tempJSON);
tempJSON = {};
}
}
});
});
console.log(json);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<form>
<input type="text" name="name" value="Tom">
<input type="text" name="location" value="China">
<input type="text" name="id" value="993">
<input type="text" name="accountId" value="123">
<input type="text" name="id" value="222">
<input type="text" name="accountId" value="333">
</form>
<form>
<input type="text" name="name" value="Test">
<input type="text" name="location" value="Test112">
<input type="text" name="id" value="22">
<input type="text" name="accountId" value="78">
<input type="text" name="id" value="00">
<input type="text" name="accountId" value="44">
</form>
<button>
Submit
</button>
Hope this will help you.

Javascript replace string with values from dom object

Im trying to make a formula dynamic and also the input fields. First problem is to change the (c1 + d2) into values like (1 + 2) depending on what is the value in input. consider that tdinput is dynamic so I can add as many as I can and formula can change anytime.
<input class="tdinput" type="text" data-key="c1">
<input class="tdinput" type="text" data-key="d2">
<input type="text" data-formula="(c1 + d2)">
<script>
$('.tdinput').on('change', function() {
var key = $(this).attr('data-key');
$('[data-formula*="+key+"]').each(function() {
//now here goes to change the data-formula by inputted values
//calculate using eval()
});
});
</script>
You need to make a more modular approach with this.
And make good use of the javascript jquery api you're using.
When a variable is defined by data-something-something you can access it by jQuerySelectedElement.data('something-something')
Now, eval is evil, but when you sanitise your input variables(in this case by parseInt) you should be relatively save from xss inputs etc..
What happens is, all the variables are inserted as an property in an object t.
then eval will call and and access all the objects in t and do the calculation.
Only requirement is that you define all the variables not as just c2, but as t.c2 in your key definition properties.
have a look below at the play with the data properties and the eval.
When using eval ALWAYS make sure you only eval 'safe' data! Don't eval strings if you plan to safe user input! you open your site for XSS attacks then.
$('[data-formula]').on('keyup', function() {
var $this = $(this);
var formulaName = $this.data('formula');
var $output = $('[data-formula-name="'+formulaName+'"]');
var formula = $output.data('formula-calc');
var t = {};
var keys = [];
$select = $('[data-formula="'+formulaName+'"]');
$select.each(function(index,elem) {
var $elem = $(elem);
var key = $elem.data('key');
t[key] = parseFloat($elem.val());
keys.push(key);
if(isNaN(t[key])) {
t[key]=0;
}
});
for(var c=0;c<keys.length;c++) {
formula = formula.replace(new RegExp(keys[c],'g'),'t.'+keys[c]);
}
var result = 0;
eval('result = '+formula)
$output.val(result)
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
sum:
<input class="tdinput" type="text" data-formula="sum" data-key="c1">
<input class="tdinput" type="text" data-formula="sum" data-key="d2">
<input type="text" data-formula-name="sum" data-formula-calc="(c1 + d2)" disabled>
<BR/>
xor
<input class="tdinput" type="text" data-formula="pow" data-key="c1">
<input class="tdinput" type="text" data-formula="pow" data-key="d2">
<input type="text" data-formula-name="pow" data-formula-calc="(c1 ^ d2)" disabled>
<BR/>
sub
<input class="tdinput" type="text" data-formula="sub" data-key="c1">
<input class="tdinput" type="text" data-formula="sub" data-key="d2">
<input type="text" data-formula-name="sub" data-formula-calc="(c1 - d2)" disabled>
<BR/>
silly
<input class="tdinput" type="text" data-formula="silly" data-key="c1">
<input class="tdinput" type="text" data-formula="silly" data-key="d2">
<input type="text" data-formula-name="silly" data-formula-calc="(c1 / d2 * 3.14567891546)" disabled>
You can use eval().
Logic
Fetch formula and save it in string
Get all valid keys
Replace keys with value in formula
Use eval() to process it
Sample
JSFiddle
$("#btnCalculate").on("click", function() {
var $f = $("input[data-formula]");
var formula = $f.data("formula");
formula.split(/[^a-z0-9]/gi)
.forEach(function(el) {
if (el) {
let v = $("[data-key='" + el + "']").val();
formula = formula.replace(el, v);
}
});
var result = eval(formula);
console.log(result)
$f.val(result)
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
<input class="tdinput" type="text" data-key="c1">
<input class="tdinput" type="text" data-key="d2">
<br/>
<input type="text" data-formula="(c1 + d2)">
<button id="btnCalculate">calculate</button>
Reference
Eval's alternate for string calculation.
Why using eval is not a good idea

Setting textfields value from a csv-like string

There is a csv-like string :
var ret = "21-01-2015|0|50|31-01-2015|0|0|5000000";
The separator is then "|".
There are HTML textfields :
<input type="text" name="P_FIN_DECLARATION" id="P_FIN_DECLARATION" class="input-small datepick validate[required]"/>
<input type="text" name="P_TAUX_PENALITE" id="P_TAUX_PENALITE" maxlength="5" class="input-mini validate[required]"/>
<input type="text" name="P_TAUX_MAJORATION" id="P_TAUX_MAJORATION" maxlength="5" class="input-mini validate[required]"/>
<input type="text" name="P_LIMITE_PAIEMENT" id="P_LIMITE_PAIEMENT" class="input-small datepick validate[required]"/>
<input type="text" name="P_TAUX_1" id="P_TAUX_1" maxlength="5" class="input-mini validate[required]"/>
<input type="text" name="P_TAUX_2" id="P_TAUX_2" maxlength="5" class="input-mini validate[required]"/>
<input type="text" name="P_PLAFOND" id="P_PLAFOND" maxlength="50" class="input-small validate[required]"/>
Each data from the string are associated exactly matching each textfield's sequence : for example 21-01-2015 is for P_FIN_DECLARATION , and so on.
How to set values of the textfields from the data parts of the string ?
Try this
var ret = "21-01-2015|0|50|31-01-2015|0|0|5000000".split('|');
// split function splits a string into an array
$('input').each(function (index) {
$(this).val(ret[index]);
});
Example
P.S. You do not need JQuery to do this simple task!!
You can do something like this:
http://jsfiddle.net/t33553x2/
var ret = "21-01-2015|0|50|31-01-2015|0|0|5000000";
var fieldArr = document.getElementsByTagName('input');
var retArr = ret.split("|");
for(var i=0; i<fieldArr.length; i++) {
var field = fieldArr[i];
field.value = retArr[i];
}
Try to use .split() to achieve the same effect as php's explode and use $.each to iterate over the array pretty easily,
var elems = $('input[type="text"]');
$.each("21-01-2015|0|50|31-01-2015|0|0|5000000".split('|'), function(i,val){
elems.eq(i).val(val)
});
Or you can do like,
var arr = "21-01-2015|0|50|31-01-2015|0|0|5000000".split('|');
$('input[type="text"]').val(function(i){
return arr[i];
});

using one function for multiple entries javascript

im trying to take multiple inputs from a user with different element id's and run them through one if else function. here is my code:
Enter the grade and number of credits for course 1:
<input type="text" id="lettergrade1" onchange="lettergrade[0]=numgrade(this.value)";>
<input type="text" id="credithour1" onchange="credhour[0]";>
etc... this extends down to lettergrade 5. I have to run them through the function numgrade (without using a for loop). how do I run these through the same if else construct without making 5 different ones (how can I make one statement that will work for 5 diffferent id's)?
Iterate through the elements and bind them to the onchange callback:
var $ = document;
var lettergrade = [];
[].slice.call($.querySelectorAll('.lettergrade')).forEach(function(el, index) {
el.onchange = function() {
numgrade(index, this.value);
}
});
function numgrade(index, value) {
lettergrade[index] = value;
$.getElementById('output').innerText = JSON.stringify(lettergrade);
}
<input type="text" class="lettergrade" id="lettergrade1" placeholder="1">
<input type="text" class="lettergrade" id="lettergrade2" placeholder="2">
<input type="text" class="lettergrade" id="lettergrade3" placeholder="3">
<input type="text" class="lettergrade" id="lettergrade4" placeholder="4">
<input type="text" class="lettergrade" id="lettergrade5" placeholder="5">
<output id="output"></output>

Categories

Resources