Dynamically Generated AJAX/JS calculator form - javascript

I am trying to make a form that generates a value based on weighted inputs. For example, if we had the following in the database
**Item** _|_**Weight**
sink | 1.5
toilet | 2.5
shower | 3
And a form that looked like this, built from the database, using AJAX (Has to be built using AJAX, because the inputs' names and the number of inputs varies depending on a user selection in a previous section of the form)
<form id="calculator">
...There are several field sets here...
<fieldset id="myFields">
<input type="text" class="iteminput" data-weight="1.5" name="sink" id="sink">
<input type="text" class="iteminput" data-weight="2.5" name="toilet" id="toilet">
<input type="text" class="iteminput" data-weight="3" name="shower" id="shower">
</fieldset>
</form>
If the user puts in that they have X sinks, Y toilets, and Z showers, I want to automatically calculate the total "value" of their plumbing system, in this case, X*1.5+Y*2.5+Z*3.
Where I am hung up is on how to get the value of each input. What I want to be able to do is loop through all of the inputs in #myFields, get the value of the field, as well as the value of the data-weight attribute, multiply them together, then add them to a running total. I would like to do all of this in a function attached to the onKeyUp event for each input.
Any help is appreciated, and more information can be provided if necessary.
Javascript only solution would be best, but I am not against using jQuery if it drastically simplifies the answer.

Here is a jQuery example:
You should be able to get the values of the inputs on a blur function. And then update the values by running an each function on the inputs. Something like this...
<ul>
<li>
<label>Number of X</label>
<input type="text" value="" id="x"/>
</li>
<li>
<label>Number of Y</label>
<input type="text" value="" id="y"/>
</li>
<li>
<label>Number of Z</label>
<input type="text" value="" id="z"/>
</li>
</ul>
<p>Total is: <span class="total"></span>
jQuery:
$('input').blur(function () {
var total = 0;
$('input').each(function() {
total += Number($(this).val());
});
$('.total').text(total);
});
DEMO:
http://jsfiddle.net/DYzsR/1/

This is what I ended up doing.
function calcFixtures()
{
var elements = [];
var total = 0;
elements = document.getElementsByClassName('fixtureinput');
for (var i=0; i<elements.length; i++) {
total += elements[i].value * elements[i].getAttribute("data-weight");
}
}
Logic being, get all elements with a certain class, loop through them, and for each element, get the value of the data-weight attribute and multiply it by the current form value for that element. Thanks to #Kris for the idea of doing it as a running total rather than a single calculation. That was really the breakthrough point.

Related

A want to use the for loop in my code, but I don't know how is it possible

I am about to learn programming and I have created a practice form to test my knowledge. The checkbox allows the user to choose what extra topping they want for their hamburger. in javascript I can get extra topping value by using the getElementById command. The code works perfectly but I need the for loop to go through all the elements .I need the checkbox's index which were written in HTML code to be able to use in javascript.
This what I written in HTML:
<div class="extra">
<div>
<input value="250" type="checkbox" name="cheese" id="cheese">
<label for="cheese">Sajt (+250 Ft)</label>
</div>
<div>
<input value="600"type="checkbox" name="meat" id="doublemeat">
<label for="doublemeat">+Hús (+600 Ft)</label>
</div>
<div>
<input value="250"type="checkbox" name="onion" id="onion">
<label for="onion">Hagyma (+250 Ft)</label>
</div>
<div>
<input value="450"type="checkbox" name="bacon" id="bacon">
<label for="bacon">Bacon (+450 Ft)</label>
</div>
<div>
<input value="600"type="checkbox" name="coleslaw" id="coleslaw">
<label for="coleslaw">Coleslaw saláta (+600)</label>
</div>
</div>
And this in javascript:
var extra=0;
if (document.getElementById("cheese").checked){
extra=extra+parseInt(cheese.value)}
if (document.getElementById("doublemeat").checked){
extra=extra+parseInt(doublemeat.value)}
if (document.getElementById("onion").checked){
extra=extra+parseInt(onion.value)}
if (document.getElementById("bacon").checked){
extra=extra+parseInt(bacon.value)}
if (document.getElementById("coleslaw").checked){
extra=extra+parseInt(coleslaw.value)
}
You'll need to make an array of all your elements and then you can use JavaScript's reduce method.
Here's an example:
const elements = [
document.getElementById("cheese"),
document.getElementById("doublemeat"),
document.getElementById("onion"),
document.getElementById("bacon"),
document.getElementById("coleslaw")
];
const extra = elements.reduce((total, element) => {
if (element.checked) {
return total + parseInt(element.value);
}
return total;
}, 0);
The .reduce method reduces an array into a new value. In this case we start with 0 and return the sum + value if the element is checked.
You can read more about reduce on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce
In your JavaScript, you might be better off doing:
const elements = Array.from(document.querySelectorAll('.extra > div > input'))
This will save you from having to manually fetch all the elements.

Sort by or re-order, instead of hiding/showing

I'm trying to refactor my Javascript/Jquery to use a select option and hidden input value in order to sort the divs on the page.
Currently, my JS works but it's hiding all divs whose hidden input has a value of 0. I want to keep the logic, but instead of hiding, just reorder the divs.
So if the user selects Recently_ordered from the select box, then any div whose hidden input has a value of 1 would show first and all with 0 would show after. Basically, all items remain on page but reorder slightly.
here's the working script that currently hides:
<script type="text/javascript">
$(function(){
$('#filterText').on('change', function() {
var currentVal = $(this).val();
$(".group-container").show();
if (currentVal == 'recently_ordered') {
$('.group-container input[name="reorder"]').each(function (index, value){
if($(this).val() == "0"){
$(this).parent('.group-container').hide();
}
});
}
});
});
</script>
And the basic structure of the HTML:
#foreach ($orderFormData->pgroups as $pgroup)
<div class="group-container">
<input type="hidden" name="topseller" value="{{$pgroup->topseller}}" />
<input type="hidden" name="reorder" value="{{$pgroup->reorder}}"/>
Grab the .group-container elements as array, sort it according to their reorder value and append them to the DOM in the order of the array.
The snippet is really verbose but should give enough informations to follow the code.
The functions used in this snippet should all have a link to the official documentation or the description in the Mozilla Development Network in the comments.
The "groups" in the DOM have the mentioned hidden input fields and an additional <p> element which shows the value of the topseller and reorder fields which should make it easier to follow the changes from the script.
$(function() {
$("#filterText").on("change", function() {
var container = $(".container"), // get the surrounding container, used for appending the sorted groups
groups = $(".group-container").get(), // grab the "groups" and make them a regular array
// .get() -> https://api.jquery.com/get/
currentVal = this.value; // the value of the selected option
groups
// first we have to sort them in the "correct" order
// Array.prototype.sort() -> https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
.sort(function(left, right) {
var topsellerValueLeft = parseInt(left.querySelector('input[name="topseller"]').value, 10), // get the value of the topseller field and make it a number
topsellerValueRight = parseInt(right.querySelector('input[name="topseller"]').value, 10), // get the value of the topseller field and make it a number
// Element.prototype.querySelector -> https://developer.mozilla.org/en-US/docs/Web/API/Element/querySelector
// parseInt() -> https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt
// parseInt($(left).find('input[name="topseller"]').val(), 10)
// parseInt($(right).find('input[name="topseller"]').val(), 10)
// would yield the same result, but we don't need two full-blown jQuery objects just to get the value
reorderValueLeft,
reorderValueRight;
// in case of "recently ordered" we sort the groups on their reorder value
if (currentVal === "recently_ordered") {
reorderValueLeft = parseInt(left.querySelector('input[name="reorder"]').value, 10); // get the value of the reorder field and make it a number
reorderValueRight = parseInt(right.querySelector('input[name="reorder"]').value, 10); // get the value of the reorder field and make it a number
// we have to check the reorder value only when the values are different
if (reorderValueLeft !== reorderValueRight) {
return reorderValueRight - reorderValueLeft; // sort descending -> 1 ... 0
}
}
// either we are not supposed to sort the items by their reordered value
// or they have the same reordered value
// hence we will then sort them on their topseller value
// this time in ascending order
return topsellerValueLeft - topsellerValueRight;
})
// now we append the elements to the DOM in the same order as we find them in the array
// this will "remove" the groups one by one from the DOM and append it at their correct new spot
// Array.prototype.forEach() -> https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
.forEach(function(group) {
container.append(group); // .append() -> https://api.jquery.com/append/
});
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<select id="filterText">
<option value="default">default</option>
<option value="recently_ordered">recently ordered</option>
</select>
<br />
<div class="container">
<div class="group-container">
<p>1 | 1</p>
<input type="hidden" name="topseller" value="1" />
<input type="hidden" name="reorder" value="1" />
</div>
<div class="group-container">
<p>2 | 0</p>
<input type="hidden" name="topseller" value="2" />
<input type="hidden" name="reorder" value="0" />
</div>
<div class="group-container">
<p>3 | 1</p>
<input type="hidden" name="topseller" value="3" />
<input type="hidden" name="reorder" value="1" />
</div>
<div class="group-container">
<p>4 | 0</p>
<input type="hidden" name="topseller" value="4" />
<input type="hidden" name="reorder" value="0" />
</div>
</div>
(or on jsfiddle.net)

how to get ng-repeat checkbox values on submit function in angularjs

I have a form which has 10 checkboxes. By default angular js triggers on individual checkbox. I want to grab all selected check box values on submit action only. Here is my code...
<form name="thisform" novalidate data-ng-submit="booking()">
<div ng-repeat="item in items" class="standard" flex="50">
<label>
<input type="checkbox" ng-model="typeValues[item._id]" value="{{item._id}}"/>
{{ item.Service_Categories}}
</label>
</div>
<input type="submit" name="submit" value="submit"/>
</form>
$scope.check= function() {
//console.log("a");
$http.get('XYZ.com').success(function(data, status,response) {
$scope.items=data;
});
$scope.booking=function(){
$scope.typeValues = [];
console.log($scope.typeValues);
}
I am getting empty array.
Can somebody tell how to grab all selected checkbox values only on submit event.
<div ng-repeat="item in items">
<input type="checkbox" ng-model="item.SELECTED" ng-true-value="Y" ng-false-value="N"/>
</div>
<input type="submit" name="submit" value="submit" ng-click="check(items)"/>
$scope.check= function(data) {
var arr = [];
for(var i in data){
if(data[i].SELECTED=='Y'){
arr.push(data[i].id);
}
}
console.log(arr);
// Do more stuffs here
}
Can I suggest reading the answer I posted yesterday to a similar StackOverflow question..
AngularJS with checkboxes
This displayed a few checkboxes, then bound them to an array, so we would always know which of the boxes were currently checked.
And yes, you could ignore the contents of this bound variable until the submit button was pressed, if you wanted to.
As per your code all the checkboxes values will be available in the typeValues array. You can use something like this in your submit function:
$scope.typeValues
If you want to access the value of 3rd checkbox then you need to do this:
var third = $scope.typeValues[2];
Declare your ng-model as array in controller, like
$scope.typeValues = [];
And in your template, please use
ng-model="typeValues[item._id]"
And in your controller, you will get this model array values in terms of 0 and 1. You can iterate over there.

Why is a HTML form field showing the calculations in the output field

I've created a basic profit calculator which is pretty much working fine. My issue is that every time I enter a number into each of the relevant fields, you can see the workings out in the "Total profit" field. It first tells me my entry is NaN, then -infinity, and then shows the workings. Once I click on my calculate button, I am then finally displayed with the correct number.
For this post I am not concerned with why it is producing the NaN (not a number), but why am I seeing it populated in the field in the first place. I want this field to remain blank until I click Calculate, and then see the resulting number. I suspect it's to do with my JavaScript code but I am a complete newbie - and very stuck.
Your thoughts are most appreciative.
<form id="profitCalculator" action="" class="dark-matter">
<h1>Profit Calculator</h1>
<fieldset>
<p><label>Case Cost:<br />£
<input name="casecost" type="text" value="" size="14" maxlength="8" /></label></p>
<p><label>Units per case:<br /> <input name="packs" type="text" value="1" size="14" maxlength="8" /></label></p>
<p><label>Sell price:<br /> £ <input name="sell_price" type="text" value="" size="14" maxlength="8" /></label></p>
<p><input type="button" class="button" OnClick="Circle_calc(this.form);" value="Calculate"></p>
<p>Total Profit:<br /> £ <input name="profit" type="text" value="0" autocomplete="off" SIZE=14></p>
<p><input type="reset" class="button" value="Reset"></p>
</fieldset>
</form>
And the JavaScript
<script type="text/javascript">
document.getElementById('profitCalculator').onclick = function () {
var casecost = this.elements['casecost'].value || 0;
var packs = this.elements['packs'].value || 0;
var sell_price = this.elements['sell_price'].value || 0;
var profit = (sell_price - casecost) / packs;
this.elements['profit'].value = profit.toFixed(2);
}
</script>
Thank you
You've set a click handler on the form element. Since click events bubble, clicks on the elements inside the form bubble to the form as well. So that's triggering an update to your profit element on anything that any element considers a click.
This diagram from the DOM3 events spec (which has since been folded into the DOM4 spec's events section) may help clarify how bubbling works:
You have a Onclick event on your button that calls Circle_calc function.
Change your function passing the form element as a parameter, and it will work.
Circle_calc = function (form) {
var casecost = form.elements['casecost'].value || 0;
var packs = form.elements['packs'].value || 0;
var sell_price = form.elements['sell_price'].value || 0;
var profit = (sell_price - casecost) / packs;
form.elements['profit'].value = profit.toFixed(2);
}
JsFiddle here

JQuery/JavaScript: Iterate through form fields, perform calculation, then submit

Right now my code is very "hard-coded" and repetitive. I'd like to know if there is a cleaner way to do the following. Ideally, I want to iterate through my forms fields with a loop and calculate the results with one statement, but I'm struggling to figure out how best to do so.
Summary: I have ten form fields, each with a distinct decimal value that a user may or may not supply. When the user hits submit, it should add the value in the input field with a value being displayed on the current HTML page, then insert into the DB.
First, I grab that value from the form input field and convert it into a number with two decimal places. I then grab the current total from the HTML and add the two numbers together. After that I inject that total back into the form input field so that it can be stored in $_POST and inserted into a database.
How can I make my code more DRY (ie, Don't Repeat Yourself)? Below are just two examples but they are exactly the same except for the element calls:
var subtotal = Number($("#housing").val());
subtotal = (subtotal).toFixed(2);
var currentTotal = $('#output-housing').html().replace("$", "");
var total = Number(subtotal) + Number(currentTotal);
$('#housing').val(total);
var subtotal = Number($("#utilities").val());
subtotal = (subtotal).toFixed(2);
var currentTotal = $('#output-utilities').html().replace("$", "");
var total = Number(subtotal) + Number(currentTotal);
$('#utilities').val(total);
I would like to iterate through my input fields like so, but I'm trying to figure out how I could display the logic inside:
var input = $('.form-expenses :input');
input.each(function() {
// Insert switch statement here?? Some other construct??
});
HTML: (Uses Bootstrap 3 classes)
FORM:
<form class="form-expenses form-horizontal" role="form" method="post" action="/profile/update">
<div class="form-group">
<label for="housing" class="control-label col-sm-3">Housing</label>
<div class="input-group input-group-lg col-sm-9">
<span class="input-group-addon">$</span>
<input type="text" class="form-control" name="housing" id="housing" />
</div>
</div>
<div class="form-group">
<label for="utilities" class="control-label col-sm-3">Utilities</label>
<div class="input-group input-group-lg col-sm-9">
<span class="input-group-addon">$</span>
<input type="text" class="form-control" name="utilities" id="utilities" />
</div>
</div>
...
<button class="btn btn-lg btn-primary btn-block" id="update-expenses" type="submit"> Update</button>
</form>
OUTPUT:
<tr>
<td>Housing</td>
<td id="output-housing">$<?php echo $total['housing']?></td>
</tr>
<tr>
<td>Utilities</td>
<td id="output-utilities">$<?php echo $total['utilities']?></td>
</tr>
Something like this should work. Assumes the same prefixing relationship of output/input ID's
$(function() {
$('form.form-expenses').submit(function() {
updateValues();
return false/* prevent submit for demo only*/
})
})
function updateValues(){
$('.form-expenses :input').not('#update-expenses').each(function(){
var $input=$(this), inputId=this.id;
var curr=$('#output-'+inputId).text().replace("$", "");
$input.val(function(i,val){
return (1*(val ||0) + 1*curr).toFixed(2);
})
});
}
DEMO
From a UI perspective, this seems very counter intuitive to change values that user just input.
To create ajax data object instead of updating the display values:
function getAjaxData(){
var ajaxData={}
$('.form-expenses :input').not('#update-expenses').each(function(){
var $input=$(this), inputId=this.id;
var curr=$('#output-'+inputId).text().replace("$", "");
ajaxData[this.name] =(1*(val ||0) + 1*curr).toFixed(2);
});
return ajaxData
}
/* in submit handler*/
$.post('path/to/server', getAjaxData(), function(response){/*do something with reponse*/})
"if I allow a user to add/remove fields, then this could get a bit sticky"
In that case, give your fields a class name. As long as that exists on added fields, they will all be calculated.
<input type="text" class="form-control calculate-me" name="housing" id="housing" />
And iterate though all, using their ids as a reference
$(".calculate-me").each(function(){
var ref=this.id;
var subtotal = Number($("#" + ref).val());
subtotal = (subtotal).toFixed(2);
var currentTotal = $('#output-' + ref).html().replace("$", "");
var total = Number(subtotal) + Number(currentTotal);
$('#' + ref).val(total);
});

Categories

Resources