calculate total outside ng-repeat - javascript

I am trying to calculate total outise ng-repeat which is dependent on ng-change inside ng-repeat.
My table looks like this
<table>
<tr>
<td>Total - total is {{tot}}</td>
</tr>
<tr ng-repeat="obj in Array">
<td>
<input ng-change="getValue(obj.Cost);" ng-model="obj.Quantity" type="number">
</td>
<td>
<span ng-model="obj.ListPrice">{{obj.ListPrice}}</span>
</td>
<td>
<input type="number" ng-value="obj.Cost = obj.Quantity * obj.ListPrice">
</td>
</tr>
</table>
Now i am calculating total
var total = 0;
$scope.getValue = function(Cost){
total += parseInt(Cost);
$scope.tot = total
}
which is not happening. What is the right way to do it?

There are a few issues with your code that I can see at a glace.
First
<span ng-model="obj.ListPrice">{{obj.ListPrice}}</span>
You're using ng-model on a span which is a no go. You can read about it from the official documentation.
Second
<input type="number" ng-value="obj.Cost = obj.Quantity * obj.ListPrice">
You're assigning the operation of assigning a value to obj.Costto ng-value. I can only hope you understood that, I'm not sure if a better way to describe this.
I believe what you're trying to achieve is this
<input type="number"
ng-init="obj.Cost = obj.Quantity * obj.ListPrice"
ng-value="obj.Cost" />

Your code isn't working because before it could calculate obj.Cost, ng-change gets triggered and you get the stale/old value of Cost. So, instead, you could do something like this:
$scope.getValue = function() {
$scope.total = 0
$scope.myArray.forEach(function(arr) {
$scope.total += arr.Quantity * arr.ListPrice
})
}
And your input would be:
<td>
<input ng-change="getValue()" ng-model="obj.Quantity" type="number">
</td>
Here's the working code snippet:
var app = angular.module('myApp', []);
app.controller("myCtrl", function($scope) {
$scope.myArray = [{
Quantity: "",
ListPrice: 100
}, {
Quantity: "",
ListPrice: 200
}]
var total = 0;
$scope.getValue = function(obj) {
$scope.total = 0
$scope.myArray.forEach(function(arr) {
$scope.total += arr.Quantity * arr.ListPrice
})
}
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.3/angular.js"></script>
<body ng-app="myApp">
<div ng-controller="myCtrl">
<table>
<tr>
<td>Total - total is {{total}}</td>
</tr>
<tr ng-repeat="obj in myArray">
<td>
<input ng-change="getValue(obj);" ng-model="obj.Quantity" type="number">
</td>
<td>
<span ng-model="obj.ListPrice">{{obj.ListPrice}}</span>
</td>
<td>
<input type="number" ng-value="obj.Cost = obj.Quantity * obj.ListPrice">
</td>
</tr>
</table>
</div>
</body>

You can access the total outside ng-repeat like this
<span>Total is - {{total}}</span>
<table>
<tbody ng-init="total = 0">
<tr ng-repeat="obj in Array">
<td>
<input ng-change="getValue(obj.Cost);" ng-model="obj.Quantity" type="number">
</td>
<td>
<span ng-model="obj.ListPrice">{{obj.ListPrice}}</span>
</td>
<td ng-init="$parent.total = $parent.total + (obj.Quantity * obj.ListPrice)">
<input type="number" ng-value="obj.Cost = obj.Quantity * obj.ListPrice">
</td>
</tr>
</table>
Basically you are doing sum inside ng-repeat and doing ng-init in tbody, so that u can access the total outside ng-repeat

Related

Dynamically update cart totals using JS and HTML?

I'm making a simple cart page for a website and wanted to have cart totals update dynamically. For some reason nothing adjusts though. I also don't receive an error or any activity in the console which makes me wonder if I'm using class names improperly. It's been a long time since I've tried this so apologies for forgetting how haha. Here is my html:
<tr>
<td>
<div class="cart-info">
<img src="images/watercolor2.jpg">
<div>
<p>Watercolor Set</p>
<div class="price" data-amount="25.00">Price: $25.00</div><br>
Remove
</div>
</div>
</td>
<td><input class="quantity" type="number" value="0"></td>
<td class="total">$0.00</td>
</tr>
and js:
var quantity = document.getElementsByClassName("quantity");
Array.prototype.forEach.call(quantity, update);
function update(val, i){
val.addEventListener('input', function(){
var x = val.value;
document.getElementsByClassName('total')[i].innerHTML = "$" +
(x*document.getElementsByClassName('price')[i].getAttribute("data-amount")).toFixed(2);
});
};
I've double checked the script src is spelled properly and is posted above the tag in the html file...what am I overlooking? Is there a better approach?
This is how I'd approach it. Use querySelectorAll in concert with closest(), applying the function through an input event listener. Also, you can access the data-amount via the dataset property
const doGrandTotal = () => {
let gtotal = 0;
document.querySelectorAll('.total').forEach(t => {
gtotal += +t.innerText.replaceAll("$", "")
})
document.querySelector('#gtotal').innerText = `$${gtotal.toFixed(2)}`;
}
document.addEventListener('DOMContentLoaded', () => {
document.querySelectorAll('.quantity').forEach(q => {
q.addEventListener('input', e => {
let p = +e.target.closest('tr').querySelector('[data-amount]').dataset.amount * +e.target.value;
e.target.closest('tr').querySelector('.total').innerText = `$${p.toFixed(2)}`;
doGrandTotal()
})
})
doGrandTotal()
})
<table>
<tr>
<td>
<div class="cart-info">
<img src="images/watercolor2.jpg">
<div>
<p>Watercolor Set</p>
<div class="price" data-amount="25.00">Price: $25.00</div><br>
Remove
</div>
</div>
</td>
<td><input class="quantity" type="number" value="0"></td>
<td class="total">$0.00</td>
</tr>
<tr>
<td>
<div class="cart-info">
<img src="images/watercolor2.jpg">
<div>
<p>Watercolor Set</p>
<div class="price" data-amount="25.00">Price: $25.00</div><br>
Remove
</div>
</div>
</td>
<td><input class="quantity" type="number" value="0"></td>
<td class="total">$0.00</td>
</tr>
</table>
<hr> Grand total: <span id='gtotal'></span>

html and javascript on a simiple table

I am doing a basic course for web development. I used html for creating a very simple table with 2 columns: Apples and fruits. Fruits should be the number of apples + 5 (basic approach for a better comprehension). I tried to add an addeventlistener as well
var apples = document.getElementById('apples');
var fruits = document.getElementById('fruits');
function calculate() {
fruits = apples + 5;
fruits.innerHTML = fruits;
}
// Get all the input columns
var inputElement = document.getElementById('apples');
// Add Event Listeners to all the input columns
inputElement.addEventListener('change', calculate);
<table class="egt">
<tr>
<th>apples</th>
<th>fruits</th>
</tr>
<tr>
<td>
<input type="number" id="apples">
</td>
<td>
<input type="number" id="fruits">
</td>
</tr>
</table>
I don't get the "8" on the column "fruits". Am I missing something in the code? Thanks in advance
var fruits = document.getElementById('fruits');
function calculate(event) {
//use event here to get the apples element value on every change event.target.value so you dont have to fetch the element every time
fruits.value = (parseInt(event.target.value) + 5);
//parse it as nummber and + 5
}
var inputElement = document.getElementById('apples');
inputElement.addEventListener('change', calculate);
<table class="egt">
<tr>
<th>apples</th>
<th>fruits</th>
</tr>
<tr>
<td>
<input type="number" id="apples">
</td>
<td>
<input type="number" id="fruits">
</td>
</tr>
</table>
Read here how to target Event Property
And also read about JavaScript parseInt() Function
Input values are updated on value attribute on input tag. So it is needed to get value like fruits.value and apple.value.
And apples.value is string so for sum operation, it is needed to convert it to number.
var apples = document.getElementById('apples');
var fruits = document.getElementById('fruits');
function calculate() {
fruits.value = Number(apples.value) + 5;
}
// Get all the input columns
var inputElement = document.getElementById('apples');
// Add Event Listeners to all the input columns
inputElement.addEventListener('change', calculate);
<table class="egt">
<tr>
<th>apples</th>
<th>fruits</th>
</tr>
<tr>
<td>
<input type="number" id="apples">
</td>
<td>
<input type="number" id="fruits">
</td>
</tr>
</table>
Get the string value from apples.value and use parseInt to convert to an integer then update the fruits.
var apples = document.getElementById('apples');
var fruits = document.getElementById('fruits');
function calculate() {
fruits.value = parseInt(apples.value) + 5;
}
// Get all the input columns
var inputElement = document.getElementById('apples');
// Add Event Listeners to all the input columns
inputElement.addEventListener('change', calculate);
<table class="egt">
<tr>
<th>apples</th>
<th>fruits</th>
</tr>
<tr>
<td>
<input type="number" id="apples">
</td>
<td>
<input type="number" id="fruits">
</td>
</tr>
</table>

How to change the inputs' onchange event by name or id?

I have a snippet of HTML which is the simple version of my site. The inputs name is related to the row index. So, I am wondering how to change the inputs' onchange event using the jQuery?
For each row, it has its own function related to the row index. For example, for tr index = 0, it has the function Test1(), for tr index = 1, it has the function Test2()...
Cannot simply using the input selector, because there are some other inputs in the page. Only the id and name for the input are unique.
<tr stampId = '1001' index = '0'>
<td>
<span>
<input type="text" name="QuickOrderQty#6#0#1" id="QuickOrderQty#6#0#1" ct="Field" onchange=" SaveControlState(event,'Text');">
</span>
</td>
</tr>
<tr stampId = '1001' index = '1'>
<td>
<span>
<input type="text" name="QuickOrderQty#6#1#1" id="QuickOrderQty#6#1#1" ct="Field" onchange=" SaveControlState(event,'Text');">
</span>
</td>
</tr>
<tr stampId = '1001' index = '2'>
<td>
<span>
<input type="text" name="QuickOrderQty#6#2#1" id="QuickOrderQty#6#2#1" ct="Field" onchange=" SaveControlState(event,'Text');">
</span>
</td>
</tr>
<tr stampId = '1001' index = '3'>
<td>
<span>
<input type="text" name="QuickOrderQty#6#3#1" id="QuickOrderQty#6#3#1" ct="Field" onchange=" SaveControlState(event,'Text');">
</span>
</td>
</tr>
Well, maybe I should put my desired result here.
<script>Do something here, then change the onchange events to below.</script>
<tr stampId = '1001' index = '0'>
<td>
<span>
<input type="text" name="QuickOrderQty#6#0#1" id="QuickOrderQty#6#0#1" ct="Field" onchange="Test1();">
</span>
</td>
</tr>
<tr stampId = '1001' index = '1'>
<td>
<span>
<input type="text" name="QuickOrderQty#6#1#1" id="QuickOrderQty#6#1#1" ct="Field" onchange="Test2();">
</span>
</td>
</tr>
<tr stampId = '1001' index = '2'>
<td>
<span>
<input type="text" name="QuickOrderQty#6#2#1" id="QuickOrderQty#6#2#1" ct="Field" onchange="Test3();">
</span>
</td>
</tr>
<tr stampId = '1001' index = '3'>
<td>
<span>
<input type="text" name="QuickOrderQty#6#3#1" id="QuickOrderQty#6#3#1" ct="Field" onchange="Test4();">
</span>
</td>
</tr>
Sorry if any mistakes occurred. This is my first time answering questions. I am not even that experienced.
if I understood you correctly, you want to know the element that caused the event to trigger. You can use the word this
$("input").change(function(event) {
var x = $(this).val();
// x = the value of the input element that triggered the event.
});
I hope this helps.
If you're using jQuery, you shouldnt be using inline onchange event handlers. Also, if you wish to target multiple elements with the same behaviour use a shared class. If you do both those things this becomes trivial using jQuery's index() method:
var functions = {
Test1: function(){ console.log("TextBox1 changed") },
Test2: function(){ console.log("TextBox2 changed") },
Test3: function(){ console.log("TextBox3 changed") },
Test4: function(){ console.log("TextBox4 changed") }
}
$(document).on('change','.myClass',function(){
var index = $(this).index();
functions["Test" + index]();
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="text" class="myClass">
<input type="text" class="myClass">
<input type="text" class="myClass">
<input type="text" class="myClass">

Want to add users to an array and display it dynamically in a table using angular and javascript

I'm a beginner at javascript and angularjs and was recently coding to display all the users in my array in a table and have them update dynamically as I add more users through a form... however when I run my code all I get is "Fill out the entire form!". I was hoping you guys could tell me what I am doing wrong (most importantly) and how I could fix it.
Thanks!
My HTML:
<table>
<tr>
<td colspan="5" class="align-center"><input type="text" placeholder="Search Users" class="search-users" ng-click="userSearch"/></td>
</tr>
<tr>
<td><input type="text" placeholder="First Name" class="add-user" id="formFirstName" /></td>
<td><input type="text" placeholder="Last Name" class="add-user" id="formLastName" /></td>
<td><input type="text" placeholder="Race" class="add-user" id="formRace" /> </td>
<td><input type="text" placeholder="Class" class="add-user" id="formClass" /></td>
<td><input type="text" placeholder="Faction" class="add-user" id="formFaction" /></td>
</tr>
<tr>
<td colspan="4" class="align-right error-field" id="errorField"></td>
<td colspan="1" class="align-right"><button type="button" class="add-user" ng-click="addUser()"/> Add </button></td>
</tr>
</table>
My Javascript/Angular:
$scope.jsFirstName = document.getElementById('formFirstName').value;
$scope.jsLastName = document.getElementById('formLastName').value;
$scope.jsRace = document.getElementById('formRace').value;
$scope.jsClass = document.getElementById('formClass').value;
$scope.jsFaction = document.getElementById('formFaction').value;
$scope.jsID = users.length;
$scope.addUser = function () {
$scope.character = {};
$scope.character.id = $scope.jsID+1;
$scope.character.firstName = $scope.jsFirstName;
$scope.character.lastName = $scope.jsLastName;
$scope.character.faction = $scope.jsFaction;
$scope.character.class = $scope.jsClass;
$scope.character.race = $scope.jsRace;
if ($scope.jsFirstName.length === 0 || $scope.jsLastName.length === 0 || $scope.jsFaction.length === 0 || $scope.jsClass.length === 0 || $scope.jsRace.length === 0) {
document.getElementById('errorField').innerHTML = "Fill out the entire form!";
} else {
users.push(character);
}
};
});
As you are using AngularJS, you should use ng-model directive to pass the data from the view to controller instead of doing it manually using Javascript. So, you should never use document.getElementById within a controller.
These changes you have to made in your code :
add ng-model directive in each input type.
pass all the form data inside an object using ng-model.
Ex :
<td><input type="text" placeholder="First Name" class="add-user" ng-model="user.formFirstName" id="formFirstName" /></td>
Here, I created user object and then we can pass this whole object using ng-click="addUser(user)".
Working demo :
var app = angular.module('myApp',[]);
app.controller('userController',function($scope) {
$scope.usersData = [];
$scope.addUser = function(user) {
$scope.usersData.push(user);
$scope.user = {};
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="userController">
<table>
<tr>
<td><input type="text" placeholder="First Name" class="add-user" ng-model="user.formFirstName" id="formFirstName" /></td>
<td><input type="text" placeholder="Last Name" class="add-user" ng-model="user.formLastName" id="formLastName" /></td>
<td><input type="text" placeholder="Race" class="add-user" ng-model="user.formRace" id="formRace" /> </td>
<td><input type="text" placeholder="Class" class="add-user" ng-model="user.formClass" id="formClass" /></td>
<td><input type="text" placeholder="Faction" class="add-user" ng-model="user.formFaction" id="formFaction" /></td>
</tr>
<tr>
<td colspan="1" class="align-right">
<input type="button" class="add-user" ng-click="addUser(user)" value="Add User"/>
</td>
</tr>
</table>
<table>
<tr>
<td>First Name</td>
<td>Last Name</td>
<td>Race</td>
<td>Class</td>
<td>Faction</td>
</tr>
<tr ng-repeat="users in usersData">
<td>{{users.formFirstName}}</td>
<td>{{users.formLastName}}</td>
<td>{{users.formRace}}</td>
<td>{{users.formClass}}</td>
<td>{{users.formFaction}}</td>
</tr>
</table>
</div>
Since you use angular, you should not do what you do.
Let's take an exemple out of your code : FirstName :
<td><input type="text" id="formFirstName" ng-model="firstName" /></td>
<!-- Others like this -->
<td ng-bind="errorField"></td>
<!-- OR INSTEAD -->
<td>{{errorField}}</td>
Now you should have a controller, right ? So, in your controller, you can do this :
$scope.addUser = function() {
$scope.character = {};
$scope.character.firstName = $scope.firstName;
// Others like this
if($scope.firstName === "") { // More conditions
$scope.errorField="Please fill out the form";
}
}
This is why Angular has been made.
If you are using angular use 2 way data binding. Add ng-modal="fromFirstName" .... Etc to your inputs . In your controller set up local variable like var firstname = $scope.formFirstName . As it's 2 way data binding in real time you views and models are changed. When the user clicks the button you can check for emptiness. Hope this helps
Try using the bellow
<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<body>
<script>
var app = angular.module("myusersList", []);
app.controller("myCtrl", function($scope) {
$scope.users = [{fname:"sj",lname:"rao",class:"10"},{fname:"fvsdf",lname:"sdf",class:"1120"}];
$scope.addUser = function () {
var newUser = {};
newUser.fname=$scope.fname;
newUser.lname=$scope.lname;
newUser.class=$scope.class;
$scope.users.push(newUser);
}
});
</script>
<div ng-app="myusersList" ng-controller="myCtrl">
<ul>
<li ng-repeat="data in users">First Name : {{data.fname}} last Name : {{data.lname}} class : {{data.class}}</li>
</ul>
<input ng-model="fname">
<input ng-model="lname">
<input ng-model="class">
<button ng-click="addUser()">Add</button>
</div>
<p>Write in the input field to add users.</p>
</body>
</html>

jquery / javascript to sum field only if checkbox on same row is checked

I have a jquery / javascript function that totals the number of cubes in my order. this works 100% and is below.
function calculateTotalVolume() {
var grandTotalCubes = 0;
$("table.authors-list").find('input[name^="cubicvolume"]').each(function () {
grandTotalCubes += +$(this).val();
});
$("#grandtotalcubes").text(grandTotalCubes.toFixed(2));
}
as mentioned the above works great. I need a second function to total the same field but only if an checkbox named treated is checked. each row has the checkbox named treated but as the table is dynamically generated, a counter is appended to the name each time hence my use of name^="treated"
I am after something like below but this doesn't work:
function calculateTotalTreatedVolume() {
var grandTotaltreatedCubes = 0;
$("table.authors-list").find('input[name^="cubicvolume"]').each(function () {
if($("table.authors-list").find('checkbox[name^="treated"]').checked){
alert('10');
grandTotaltreatedCubes += +$(this).val();
}
});
$("#grandtotaltreatedcubes").text(grandTotaltreatedCubes.toFixed(2));
}
help appreciated as always.
UPDATE
Rendered HTML output [1 dynamic row added]: (Still in development so very rough, please excuse it)
<table class="authors-list" border=1>
<thead>
<tr>
<td></td><td>Product</td><td>Price/Cube</td><td>Qty</td><td>line total cost</td><td>Discount</td><td>Cubes per bundle</td><td>pcs per bundle</td><td>cubic vol</td><td>Bundles</td><td><input type="checkbox" class="checkall"> Treated</td>
</tr>
</thead>
<tbody>
<tr>
<td><a class="deleteRow"> <img src="http://devryan.tekwani.co.za/application/assets/images/delete2.png" /></a></td>
<td><input type="text" id="product" name="product" />
<input type="hidden" id="price" name="price" readonly="readonly"/></td>
<td><input type="text" id="adjustedprice" name="adjustedprice" /></td>
<td><input type="text" id="qty" name="qty" /></td>
<td><input type="text" id="linetotal" name="linetotal" readonly="readonly"/></td>
<td><input type="text" id="discount" name="discount" /></td>
<td>
<input type="text" id="cubesperbundle" name="cubesperbundle" >
</td>
<td>
<input type="text" id="pcsperbundle" name="pcsperbundle" >
</td>
<td>
<input type="text" id="cubicvolume" name="cubicvolume" size='5' disabled>
</td>
<td><input type="text" id="totalbundles" name="totalbundles" size='5' disabled ></td>
<td valign="top" ><input type="checkbox" id="treated" name="treated" ></td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="15"><input type="button" id="addrow" value="Add Product" /></td>
</tr>
<tr>
<td colspan="3">Grand Total: R<span id="grandtotal"></span></td>
<td colspan="2">Ave Discount: <span id="avediscount"></span>%</td>
<td colspan="1">Total Cubes: <span id="grandtotalcubes"></span></td>
<td colspan="15">Treated Cubes: <span id="grandtotaltreatedcubes"></span></td>
</tr>
<tr>
<td colspan="15"><textarea rows="1" cols="50" placeholder="Specific Comments"></textarea><textarea rows="1" cols="20" placeholder="Customer Reference"></textarea>
</td>
</tr>
</tfoot>
</table>
First go the parent tr and then using find to find the checkbox in current row and also use checked with DOM object not jQuery object, you can use indexer to convert jQuery object to DOM object.
Change
if($("table.authors-list").find('checkbox[name^="treated"]').checked){
To
if($(this).closest('tr').find('checkbox[name^="treated"]')[0].checked){
checked is a property of the actual DOM element, and what you have is a jQuery element. You need to change this:
$("table.authors-list").find('checkbox[name^="treated"]').checked
To this:
$("table.authors-list").find('checkbox[name^="treated"]')[0].checked
-^- // get DOM element
Or more jQuery-ish:
$("table.authors-list").find('checkbox[name^="treated"]').is(':checked')
You can iterate through the "checked" checkboxes using $("table.authors-list").find('checkbox[name^="treated"]:checked') and use the value of the input nearest to it (assumed to be in the same row).
Assuming your table has many rows each having a checkbox and an input, you can use:
function calculateTotalTreatedVolume() {
var grandTotaltreatedCubes = 0;
// iterate through the "checked" checkboxes
$("table.authors-list").find('input[type="checkbox"][name^="treated"]:checked').each(function () {
// use the value of the input in the same row
grandTotaltreatedCubes += +$(this).closest('tr').find('input[name^="cubicvolume"]').val();
});
$("#grandtotaltreatedcubes").text(grandTotaltreatedCubes.toFixed(2));
}
Try this:
var grandTotaltreatedCubes = 0;
// Cache the table object here for faster processing of your code..
var $table = $("table.authors-list");
$table.find('input[name^="cubicvolume"]').each(function () {
// Check if checkbox is checked or not here using is(':checked')
if ($table.find('checkbox[name^="treated"]').is(':checked')) {
grandTotaltreatedCubes += $(this).val();
}
});
$("#grandtotaltreatedcubes").text(grandTotaltreatedCubes.toFixed(2));
Change the following line
if($("table.authors-list").find('input[name^="treated"]').checked){
To this
if($("table.authors-list").find('input[name^="treated"]').is(':checked')){

Categories