HTML Form - similar input groups as an array of objects - javascript

So I have a form with two identical group of inputs that represent education info. There could be more than two as I want to include a button to create a new group so the user can put all his education background like in LinkedIn.
<form id="formCV" action="">
<div id="educationContainer">
<!-- First Group -->
<div class="education">
<div>
<input type="text" name="institutionName">
</div>
<div>
<input type="text" name="courseName">
</div>
<div>
<input type="month" name="startDate">
</div>
<div>
<input type="month" name="endDate">
</div>
</div>
<!-- Second Group -->
<div class="education">
<div>
<input type="text" name="institutionName">
</div>
<div>
<input type="text" name="courseName">
</div>
<div>
<input type="month" name="startDate">
</div>
<div>
<input type="month" name="endDate">
</div>
</div>
</div>
</form>
Now, if I use the FormData API to get the form data like this:
for(let entry of formData.entries()){
console.log(entry);
}
I get the following output:
(2) ["institutionName", "Harvard"]
(2) ["courseName", "Web Development"]
(2) ["startDate", "2000-11"]
(2) ["endDate", "2008-11"]
(2) ["institutionName", "Oxford"]
(2) ["courseName", "Business Management"]
(2) ["startDate", "2009-10"]
(2) ["endDate", "2010-05"]
What I want to achieve is to get the output in an organized way, like this:
education:[
{
institutionName:"Harvard",
courseName:"Web Development",
startDate:"2000-11",
endDate:"2008-11"
},
{
...
}
]
So I'm interested in knowing the best approach to achieve this. Thanks in advance for any help!

It does not make sense to have two equal forms, with one being sufficient.
In addition to the form you should have a list that shows each item added.
It's what I recommend.

Not sure whether this is the best approach, but you can achieve the desired structure like this:
const formCV = document.querySelector('#formCV');
const formData = new FormData(formCV);
function groupEducationData(inputGroupSize = 4) {
const result = [];
let educationObj = null;
let counter = 0;
for (const entry of formData.entries()) {
// Since the counter is divisible by the number of inputs in a group
// only if one form group finishes. And when one form group finishes,
// we need to add the object into the result array
if (counter % inputGroupSize === 0) {
// if this is the first iteration, the educationObj is null and
// we don't want to add it to the result array yet
// we only add the educationObj to the result array if it is
// an object containing the education info
if (educationObj) result.push(educationObj);
// initialize the educationObj at the start
// and after one form finishes
educationObj = {};
}
// add entry[0] as key to the object (e.g. 'institutionName')
// with the value of entry[1] (e.g. 'Harvard')
educationObj[entry[0]] = entry[1];
counter++;
}
return result.concat(educationObj);
}
console.log(groupEducationData());
<form id="formCV" action="">
<div id="educationContainer">
<!-- First Group -->
<div class="education">
<div>
<input type="text" name="institutionName" value="Harvard">
</div>
<div>
<input type="text" name="courseName" value="Web Development">
</div>
<div>
<input type="month" name="startDate" value="2000-11">
</div>
<div>
<input type="month" name="endDate" value="2008-11">
</div>
</div>
<!-- Second Group -->
<div class="education">
<div>
<input type="text" name="institutionName" value="Oxford">
</div>
<div>
<input type="text" name="courseName" value="Business Management">
</div>
<div>
<input type="month" name="startDate" value="2009-10">
</div>
<div>
<input type="month" name="endDate" value="2010-05">
</div>
</div>
</div>
</form>

You can try FormData.getAll() and iterate over each group entry.
const institutionNames = formData.getAll('institutionName');
const courseNames = formData.getAll('courseName');
...
const educations = [];
for (let i = 0; i < institutionNames.length; i++) {
educations.push({
institutionName: institutionNames[i],
courseName: courseNames[i],
...
});
}

This is also a way to populate your desired format data.
$(document).ready(function(){
$(":button").click(function(){
var educations=$("#formCV .education");
var data=[];
educations.each(function(i,education){
var set={}
$(education).find(":input").each(function(i,value){
set[$(value).attr("name")] = $(value).val();
});
data.push(set);
})
console.log("data",data)
});
});
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
<form id="formCV" action="">
<div id="educationContainer">
<!-- First Group -->
<div class="education">
<div>
<input type="text" name="institutionName">
</div>
<div>
<input type="text" name="courseName">
</div>
<div>
<input type="month" name="startDate">
</div>
<div>
<input type="month" name="endDate">
</div>
</div>
<!-- Second Group -->
<div class="education">
<div>
<input type="text" name="institutionName">
</div>
<div>
<input type="text" name="courseName">
</div>
<div>
<input type="month" name="startDate">
</div>
<div>
<input type="month" name="endDate">
</div>
</div>
</div>
<input type="button" value="click me"/>
</form>
</body>
</html>

Related

Adding elements dynamically

I'm trying to add elements dynamically through javascript but whenever I try opening up the page they appear for a split second then disappear
I take a number of process from the input tag and run a loop to create each element individually
I tried removing everything from the event and only call a function which I placed the code in but didn't work
const numberOfProcesses = document.getElementById("numberOfProcesses").value;
const timeQuantum = document.getElementById("timeQuantum").value;
const start = document.getElementById("start");
const processDiv = document.getElementById("processDiv");
const burstDiv = document.getElementById("burstDiv");
start.addEventListener("click", (event) => {
for (let i = 0; i < numberOfProcesses; i++) {
let pLabel = document.createElement("label");
pLabel.setAttribute("id", `process ${i}`);
pLabel.innerText = `Process ${i}`;
let pInput = document.createElement("input");
pInput.setAttribute("type", "number");
pInput.setAttribute("id", `process ${i}`);
let bLabel = document.createElement("label");
bLabel.setAttribute("id", `burstTime ${i}`);
bLabel.innerText = `Burst Time ${i}`;
let bInput = document.createElement("input");
bInput.setAttribute("type", "number");
bInput.setAttribute("id", `burstTime ${i}`);
processDiv.appendChild(pLabel);
processDiv.appendChild(pInput);
burstDiv.appendChild(bLabel);
burstDiv.appendChild(bInput);
console.log(pLabel, pInput, bLabel, bInput);
}
});
<form action="">
<div>
<label for="numberOfProcesses">Enter Number Of Processes</label>
<input type="number" name="Number Of Processes" id="numberOfProcesses" value="5" />
</div>
<br />
<div>
<label for="timeQuantum">Enter Time Quantum</label>
<input type="number" name="time quantum" value="5" id="timeQuantum" />
</div>
<button id="start">Start</button>
</form>
</section>
<br /><br />
<section>
<form action="">
<div id="processDiv">
<label for="process0">P0</label>
<input type="number" name="process" id="process0" />
</div>
<div id="burstDiv">
<label for="burstTime0">Burst Time</label>
<input type="number" name="burst time" id="burstTime0" />
</div>
<button id="excute">Execute</button>
</form>
Remove action="" and set type attribute to button if nothing is submitted. The behaviour you describe is due to the form being submitted.
Do like this and you can see you console log for other errors:
<form>
<div>
<label for="numberOfProcesses">Enter Number Of Processes</label>
<input type="number" name="Number Of Processes" id="numberOfProcesses" value="5" />
</div>
<br />
<div>
<label for="timeQuantum">Enter Time Quantum</label>
<input type="number" name="time quantum" value="5" id="timeQuantum" />
</div>
<button type="button" id="start">Start</button>
</form>

how to fix hook to set state in React JS?

I am trying to have a user set a function variable with an input number. I have a form they enter a number into, which needs to set the col const up top.
So far, i am using a simple hook, and col is being set (i console logged it), but its not producing the desired array. Im thinking its something getting messed up in the toggling at the bottom of the code.
function DataFrame(){
const [toggle, setToggle] = React.useState(false);
const [col, setCol] = useState(0)
var element = <li class="element"/>
var row = 3
var arr = []
var i
for (i = 0; i<row; i++){
arr.push(element)
}
const Element = () => <li className="element" />;
console.log(col)
return (
<div>
<div >
<div style={{fontFamily:'PressStart2P',opacity:'45%', width:'360px',
position:'absolute',left:'36px', top: '160px',color:'rgb(143, 0, 145)'}}>
</div>
<div >
<h1 class="dfHeader" style={{left:'30px'}}>
DataFrames :<br></br></h1>
<h1 class='dfHeader2'style={{top:'150px',left:'30px'}}>
constructor</h1>
<div class="codeBorder" style={{scrollbarColor:'#6a00ff'}}>
<div class="textbox" style={{width:'180px'}}>
<p class="instructions"></p>
<p class="instructions2"></p>
<p class="instructions3">
<form class="codeForm">
<label>
enter dimensions:
<input type="number" name="dimension" onKeyUp=
{e => setCol(e.target.value)} />
</label>
<input class='goButton' type="submit" value="run" />
</form>
<br/><br/></p>
<p class="instructions3">
<form class="codeForm">
<label>
input code:
<input type="number" name="dimension" />
</label>
<input class='goButton' type="submit" value="run" />
</form></p>
<p class="instructions3">
<form class="codeForm">
<label>
input code:
<input type="number" name="dimension" />
</label>
<input class='goButton' type="submit" value="run" />
</form></p>
<p class="instructions3">
<form class="codeForm">
<label>
input code:
<input type="number" name="dimension" />
</label>
<input class='goButton' type="submit" value="run" />
</form> </p>
</div>
</div>
<div class="btnConsole">
<button class="dfButton" onClick={()=>setToggle( (prev) => (!prev) )}>
</button>
</div>
</div>
<div class="monitor"style={{}}>
<div class="superScreen">
<div class="screenDiv" >
<div id="subScreen" class="subScreen">
{[...Array(col).keys()].map(ul => (
<ul key={ul}>
{toggle &&
[...Array(row).keys()].map(
li => <Element key={li} />)}
</ul>
))}
</div>
</div>
</div>
</div>
<br/>
</div>
</div>
)
}
export default DataFrame;
ReactDOM.render(<DataFrame />, document.getElementById('root'));
Any help is appreciated as always!
onKeyUp={e => setCol(e.target.value)}
this is the cause of your problem. e.target.value is a string, you are setting col equal to a string. Consequently, [...Array(col).keys()] gives you an array of length 1.
const col = '5';
console.log([...Array(col).keys()]);
Change
onKeyUp={e => setCol(e.target.value)}
to
onKeyUp={e => setCol(Number(e.target.value))}

How can I validate multiple grouped inputs?

I've this structure here:
<div>
<div class="row">
<input id="1a">
<input id="1b">
</div>
<div class="row">
<input id="2a">
<input id="2b">
</div>
<div class="row">
<input id="3a">
<input id="3b">
</div>
<div class="row">
<input id="4a">
<input id="4b">
</div>
</div>
If the user leaves everything empty, there is no problem. But when he enters for example something into 1a and leaves 1b empty, this should cause an error. So how can I find out if a & b is filled for each row? It's a bit tricky and I have no idea how to deal with this.
You can achieve it in this simple way
$('button').on("click", () => {
$('body').find(".row").each(function(index, row){
var count = 0;
$(row).find("input").each(function(i, input) {
if($(input).val() !== "")
count++;
})
if(count === 1)
alert("Row " + (index + 1) + " is invalid");
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
<div class="row">
<input id="1a">
<input id="1b">
</div>
<div class="row">
<input id="2a">
<input id="2b">
</div>
<div class="row">
<input id="3a">
<input id="3b">
</div>
<div class="row">
<input id="4a">
<input id="4b">
</div>
</div>
<button>Check</button>
something like that ?
const inRow = document.querySelectorAll('.row')
, valida = document.querySelector('button')
, respon = document.querySelector('output')
;
valida.onclick =_=>
{
let OK = true
inRow.forEach(eR=>
{
let vals = 0
eR.querySelectorAll('input').forEach(eI=>{ vals+=eI.value.length ? 1:0 })
if (vals===1) OK=false
})
respon.value = OK ? 'OK' : 'bad'
}
<div>
<div class="row"> <input id="1a"> <input id="1b"> </div>
<div class="row"> <input id="2a"> <input id="2b"> </div>
<div class="row"> <input id="3a"> <input id="3b"> </div>
<div class="row"> <input id="4a"> <input id="4b"> </div>
</div>
<button>validate</button> <output></output>
Normally multiple form controls (ex <input>, <textarea>, <select>, etc) should be inside a <form> tag. Moreover, the behavior described is called form validation which requires said <form> tag to be triggered by a "submit" event.
The following demo features proper HTML
<form> and <fieldset> instead of <div>
added <output> for each <fieldset>
Also the JavaScript is designed to show a message when any pair of <input> has a .value and the other doesn't.
The message: <output>Complete data input</output>
This pseudo-form validation is triggered whenever a user enters data within an <input> and then clicks (aka "blur" event). This entire chain of actions combined is the "change" event.
The <form> tag is registered to the "change" event so if any of the <input> within the <form> is the event origin (aka event.target - the <input> that the user triggers a "change" event on).
const form = document.forms[0];
form.onchange = reqData;
function reqData(event) {
let origin = event.target;
if (origin.tagName === 'INPUT') {
const parent = origin.parentElement;
const output = parent.querySelector('output');
const inputs = [...parent.querySelectorAll('input')];
let total = inputs.length;
let count = 0;
for (let input of inputs) {
if (input.value) {
count++;
}
}
if (count < total && count > 0) {
output.style.opacity = '1';
} else {
output.style.opacity = '0';
}
}
return false;
}
output {
opacity: 0;
color: tomato
}
<form>
<fieldset>
<input><br>
<input> <output>Complete data input</output>
</fieldset>
<fieldset>
<input><br>
<input> <output>Complete data input</output>
</fieldset>
<fieldset>
<input><br>
<input> <output>Complete data input</output>
</fieldset>
<fieldset>
<input><br>
<input> <output>Complete data input</output>
</fieldset>
</form>

How do I dynamically sum input values inside a repeatable fields?

How can i get total of dynamically created each fields value
<div class="rf-row count-container">
<input class="total">
<div class="row">
<input class="add-monthly">
</div>
<div class="row">
<input class="add-monthly">
</div>
<div class="row">
<input class="add-monthly">
</div>
</div>
Please check this code this may help
<!DOCTYPE html>
<html>
<head>
Demo
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
<div class="rf-row count-container">
<input class="total">
<div class="row">
<input class="add-monthly" value="5">
</div>
<div class="row">
<input class="add-monthly" value="5">
</div>
<div class="row">
<input class="add-monthly" value="5">
</div>
</div>
<script>
$(document).ready(function(){
var value = 0;
$('.count-container .row').each(function(){
value += parseInt($(this).find('.add-monthly').val()) ? parseInt($(this).find('.add-monthly').val()) : 0
})
$('.total').val(value);
});
</script>
</body>
</html>
check below link
https://codepen.io/anon/pen/gdpZpR?editors=1010
Since you have jquery tagged, the following is the way you'd iterate over all your inputs of class add-monthly and sum their values together, finally returning the sum value:
function sumFields() {
var totalSum = 0;
$("input.add-monthly").each(function () {
totalSum += $(this).val();
});
return totalSum;
}
If you are dynamically adding input fields, I would suggest delegating the change functionality to the parent .count-container. Once the event is triggered, I am iterating over the .add-monthly fields to sum up their values. To help JavaScript understand that these values are floats (since I assume we are dealing with a currency), I am using parseFloat() to insure the value can be added to the sum. Lastly, I am setting the value of .sum-monthly to use toFixed() to format a number using a fixed-point notation (again I am assuming we are dealing with a currency).
$(document).ready(function() {
$('.count-container').on('change', '.add-monthly', function() {
let total = 0;
$('.add-monthly').each(function() {
total += parseFloat($(this).val()) || 0;
});
$('.sum-monthly').val(total.toFixed(2));
}).find('.add-monthly').change();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="rf-row count-container">
<input class="sum-monthly" readonly>
<div class="sub-rf-row">
<input class="add-monthly">
</div>
<div class="sub-rf-row">
<input class="add-monthly">
</div>
<div class="sub-rf-row template">
<input class="add-monthly">
</div>
</div>
var classes = document.getElementsByClassName('add-monthly')
classes = [...classes]
var result = classes
.map(item => +item.value)
.reduce((a, b) => a + b, 0);
document.getElementsByClassName('sum-monthly')[0].value = result
console.log('summ', result)
<div class="rf-row count-container">
SUM: <input class="sum-monthly"/><br/>
<div class="sub-rf-row">
<input class="add-monthly" type="number" value="1">
</div>
<div class="sub-rf-row">
<input class="add-monthly" type="number" value="4">
</div>
<div class="sub-rf-row template">
<input class="add-monthly" type="number" value="0">
</div>
</div>

how to use variables in jQuery select event in other javascript functions?

I am really new to jquery and javascript... I want to use the four variables in myairFunction to calculate something and those four variables store values retrieved from database.
How can make use of these values in my function ?
The function does not work. even if i test as follows, it printed nothing. where's wrong ?
$("#airradio1").click(function(){
$("#aircalc").click(function(){
$("#airanswer").html(lat1);
});
});
html :
<div id="calculator">
<h3>Air Travel</h3>
<div id="air">
<input type="radio" name="frm" id="airradio1" checked="check" onclick="setairform1()">Enter Individual flights(more accurate) <br>
<input type="radio" name="frm" id="airradio2" onclick="setairform2()">Enter numbers of flight(faster)<br>
<form id="airform1">
<div>
From
<div class="textinput">
<input type="text" id="dept" name="departure" placeholder="City name or aiport code" >
</div>
</div>
<div>
To
<div>
<input type="text" id="dest" placeholder="City name or airport code" >
</div>
</div>
<div>
Via
<div>
<input type="text" id="via" placeholder="City name or airport code" >
</div>
</div>
<div>
<div>
<input type="radio" name="trip" value="roundtrip">Round-trip <br>
<input type="radio" name="trip" value="oneway">One-way
</div>
</div>
<div>
Number of flights/passengers
<input type="text">
</div>
<div>
Class
<select style="width: 82px">
<option selected="selected">Economy</option>
<option>Business</option>
<option>First</option>
</select>
</div>
<div><button type="button" id="aircalc" >calculate</button></div>
<div id="result">
<div id="totalresult"> Total Emission:
<p id="airanswer">0 </p> lbs CO<sub>2</sub> </div>
</div>
here is the php :
$strSQL = "SELECT display, lat, longi FROM airport WHERE display LIKE '$term%'";
$rs = mysql_query($strSQL);
$json=array();
while($row = mysql_fetch_array($rs)){
$json[]=array('value'=>$row['display'],
'label'=>$row['display'],
'lat'=>$row['lat'],
'longi'=>$row['longi']
);
}
echo json_encode($json);
javascript :
$(document).ready(function(){
var lat1;
var long1;
var lat2;
var long2;
var a;
$('#dept').autocomplete({
source:'source.php',
minLength:1,
select:function(evt,ui){
lat1=ui.item.lat;
long1=ui.item.longi;
}
});
$('#dest').autocomplete({
source:'source.php',
minLength:1,
select:function(evt,ui){
lat2=ui.item.lat;
long2=ui.item.longi;
}
});
$("#airradio1").click(function(){
$("#aircalc").click(function(){
myairFunction1(lat1,lat2,long1,long2);
});
});
$('#via').autocomplete({
source:'source.php',
minLength:1
});
});
function myairFunction1(lat1,lat2,long1,long2){
co2 = lat1+lat2+long1+long2;
document.getElementById("airanswer").innerHTML = co2;
}
without seeing the information requested in the comments its nearly impossible to say what's going wrong. but the nested click function looks suspect. if you can target the element directly then why the nested listener?
$("#airradio1").click(function(){
$("#aircalc").click(function(){
myairFunction1(lat1,lat2,long1,long2);
});
});
instead just add the event listener directly to the #aircalc element:
$("#aircalc").click(function(){
myairFunction1(lat1,lat2,long1,long2);
});

Categories

Resources