I want to sum the product count which are added dynamically using JSON array.
<tr ng-repeat="(key,val) in form.products">
<td>{{ProductName(key)}}</td>
<td >
<input type="text" name="products" ng-model="form.products[key]" class="form-control">
</td>
</tr>
How can I get the sum of products in the above example?
use lodash
presuming the value you want to sum is in a property called 'price':
{{_(form.products).mapValues('price').sum()}}
you may need to get lodash into your scope first. In the controller, something like:
scope._ = _;
or this approach
you are using an 'object with properties' instead of 'an array of objects', that is why you cant use your example above , $scope.products.length;....
Your product object with its properties :
$scope.products ={
"1":"20",//property 1 with value 20
"2":"35",//property 2 with value 35
"3":"150"//property 3 with value 150
}
The data Object (object with properties as you have it):
$scope.myData = {"1":120,"2":250,"3":500};
Function that iterates into object's properties and sum the price:
//read object properties and sum price
$scope.calculateSum = function(data){
var sum=0;
var counter=0;
for (var property in data) {
if (data.hasOwnProperty(property)) {
sum += data[property];
counter++;
}
}
return sum;
};
Function that iterates into object properties and count the products
//read object properties and count the products
$scope.countProducts = function(data){
var counter=0;
for (var property in data) {
if (data.hasOwnProperty(property)) {
counter++;
}
}
return counter;
};
Into your HTML template:
<table>
<tr ng-repeat="(key,value) in myData track by $index">
<td>
<input type="text" name="id" ng-model="key" class="form-control">
</td>
<td>
<input type="text" name="price" ng-model="value" class="form-control">
</td>
</tr>
<tr>
<td>Products: {{countProducts(myData)}}</td>
<td>Sum: {{calculateSum(myData)}}</td>
</tr>
</table>
I have done a live example that counts the products and also sum the products price: http://plnkr.co/edit/FmJhvV?p=preview
Hope helps, good luck.
Related
I have a list of persons that I need to display, and for each person there is an input for age.
I need to sum the ages when im writing; I mean if I have 5 persons when I'm on the 5th record, i need the total of previous 4 ages like this one : Calculate sum from number type input fields by javascript
this is my code :
<tr th:each="person : ${persons}">
<td th:text="${person.first_name}"></td>
<td th:text="${person.last_name}"></td>
<td th:text="${person.phone}"></td>
<td><input id="age" class="form-control"
type="text" name="age" th:field="*{ages}">
</td>
</tr>
Total : <input type="text" name="total" id="total"/>
I did this js script, but in total it gives me only the value of first line !!!! Any help ?
<script type="text/javascript">
$(document).ready(function(){
$("#age").each(function() {
$(this).on('input', function(){
calculateSum();
});
log.console("sum :"+sum);
});
});
function calculateSum() {
var sum = 0;
$("#age").each(function() {
if(!isNaN(this.value) && this.value.length!=0) {
sum += parseFloat(this.value);
}
});
$("#sum").html(sum.toFixed(2));
}
</script>
put all of the ages in array
On every place you want to show the sum of the previous ages:
2.1. slice the array from 0 to the place of the current value (beacuse slice(0,x) actually slicing the array from 0 to x-1).
2.2 execute the reduce function on the sliced array, you can see an example here: https://www.w3schools.com/jsref/jsref_reduce.asp
AngularJS code
This is my code in AngularJS that I want to correct. Currently in the last rows of the table I print out the name and marks of the student with the highest marks. I want the name and score to change if a user edits the preprogrammed marks.
Example: The 2nd highest person becomes the highest if the highest is changed to 0
<script>
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope)
{
{
var x=[];
for (var i = 0; i<20; i++)
{
var R = Math.round((Math.random() * 10) * 10);
x[i]=R;
}
};
$scope.names = [
{name:'Priya',age:'19',gender:'Female',English:x[0], Hindi:x[1]},
....
{name:'Jiah', age:'18', gender:'Female',English:x[18],Hindi:x[19]}
];
$scope.sortColumn ="name";
$scope.avg = function(e, h){
if(!h) h=0;
if(!e) e=0;
return ( parseInt(h)+parseInt(e))/2;
}
$scope.delete = function (name)
{
$scope.names.splice( $scope.names.indexOf(name), 1 );
}
});
</script>
HTML Code
This is the code where I want the changes to occur. At the bottom of the HTML code you can see where I want the highest marks for Hindi and English.
<table>
<tr ng-repeat ="x in names | orderBy:sortColumn">
<td>{{x.name}}</td>
<td>{{x.age}}</td>
<td>{{x.gender}}</td>
<td><input type="text" ng-model="x.English"></td>
<td><input type="text" ng-model="x.Hindi"></td>
<td>
<button>Delete</button>
</td>
</tr>
</table>
<table ng-model="total">
<tr><td>The total is: {{total}}</td></tr>
Below is where I want the highest marks in English then Hindi to appear.
<tr><td ng-repeat="x in names |orderBy:'-English' | limitTo:1"> {{x.name}} has the highest marks in English: {{x.English}}</td></tr>
<tr><td ng-repeat="x in names |orderBy:'-Hindi' | limitTo:1"> {{x.name}} has the highest marks in Hindi: {{x.Hindi}}</td></tr>
</table>
Rather than using ngRepeat, you can create another function which will return the student with the highest value in a certain subject:
$scope.highestStudent = function(key) {
return $scope.names.reduce(function(prev, curr) {
return prev[key] > curr[key] ? prev : curr;
});
}
Using Reduce we are reducing the Array into a single result: which of these Students, has the highest value in a certain subject.
With this, we can use this instead to display the result:
<tr><td> {{highestStudent('English').name}} has the highest marks in English: {{highestStudent('English').English}}</td></tr>
<tr><td> {{highestStudent('Hindi').name}} has the highest marks in Hindi: {{highestStudent('Hindi').Hindi}}</td></tr>
I think the problem is that when you update the inputs, the model is set to "string" instead of "number".
You should change the type from "text" to "number".
<td><input type="number" ng-model="x.English"></td>
<td><input type="number" ng-model="x.Hindi"></td>
Also to properly ordering empty values:
<tr><td ng-repeat="x in names | orderBy:['!-English', '-English'] | limitTo:1"> {{x.name}} has the highest marks in English: {{x.English}}</td></tr>
<tr><td ng-repeat="x in names | orderBy:['!-Hindi', '-Hindi'] | limitTo:1"> {{x.name}} has the highest marks in Hindi: {{x.Hindi}}</td></tr>
Working jsfiddle:
http://jsfiddle.net/fk5zjuq8/
This app allows user to enter the quantity of questions for specific topic inside a workbook. I’m trying to sum up all the questions and give an output of totalQuestions.
I created a function (findTotalQuestions().) that will run when Quantity input box is changed. I’m getting an error in Console: Cannot read property 'quantity' of undefined. I’m thinking its because $scope.skill.quantity is not being passed into function. How can I fix this?
<div class="input-field col m3" ng-model="totalQuestions">
<medium>Total Questions in Workbook: </medium>{{totalQuestions}}
</div>
HTML
<table>
<tr>
<th> Skill </th>
<th> Quantity </th>
</tr>
<tr ng-repeat="skill in newWorkbook.skills">
<td>
{ skill.topicText }}
</td>
<td>
<p>
<input id="quantity{{ skill.TopicId }}" type="number" class="validate" ng-model="skill.quantity" required="true" autocomplete="off" ng-change="findTotalQuestions()">
</p>
</td>
</tr>
</table>
Controller:
$scope.getProposedSkills = function() {
return $http.get("/api/topics?SubjectId=" + $scope.newWorkbook.SubjectId).then(function(skills) {
return $scope.newWorkbookskills = _.map(skills.data, function(skill) {
var obj;
obj = {
TopicId: skill.id,
quantity: 0,
level: null,
topicText: skill.topicText,
startLevel: skill.startLevel,
endLevel: skill.endLevel
};
return obj;
});
});
};
$scope.findTotalQuestions = function() {
$scope.totalQuestions = 0;
$scope.totalQuestions = $scope.totalQuestions + $scope.skill.quantity;
return console.log($scope.totalQuestions);
};
Move your getProposedSkills() code in a service then in the controller set the $scope.skill like below
app.service('service',function($http){
return {getProposedSkills:$http.get('your api url')};
})
Your controller should look like below
app.controller('ctrl',function($scope,$http,service){
$scope.getProposedSkills = service.getProposedSkills().then(function(res){
$scope.skill=res;
})
$scope.findTotalQuestions = function() {
$scope.totalQuestions = 0;
$scope.totalQuestions = $scope.totalQuestions + $scope.skill.quantity;
return console.log($scope.totalQuestions);
}
})
And other question i can't find the place where you set $scope.skill in your controller,
if it's a paremeter to be used in your funcion, you should receive it as a paremter of your function and pass it form the view
if it's a variable on your controller it should be initialized or at least check if it exists.
By the way, In the html you have a div with ng-model, this is not correct ng-model should be place inside form inputs (select, input, textarea)
i am trying to add a object in ng-repeat, as shown :
<div ng-controller="filtersController as datas">
<table>
<tr>
<th>Names</th>
<th>nos</th>
</tr>
<tr ng-repeat="data in datas.dataset">
<td>{{data.name}}</td>
<td>{{data.no}}</td>
</tr>
</table>
<input type="text" ng-model="models.name" placeholder="enter name" />
<input type="number" ng-model="models.no" placeholder="enter number" />
<input type="button" ng-click="datas.add(models)" value="click me!!!!" />
</div>
relevant javascript is :
app.controller('filtersController', ['$scope', function ($scope) {
this.dataset = [{ name: "Vishesh", no: 1 },
{ name: "pqrst", no: 2 },
{ name: "uvwxyz", no: 3 }]
this.add = function (model) {
this.dataset.push(model);
$scope.$apply();
}}]);
Problem :
when i try to add new entries using textboxes, it will add first entry but after that i cannot add anymore entries in it, also the latest added entry changes when the values in the textboxes changes. Why is that?
Also if i change javascript like this :
app.controller('filtersController', ['$scope', function ($scope) {
this.dataset = [{ name: "Vishesh", no: 1 },
{ name: "pqrst", no: 2 },
{ name: "uvwxyz", no: 3 }]
this.add = function (names, nos) {
this.dataset.push({ name: names, no: nos });
$scope.$apply();
}}]);
and HTML :
<input type="text" ng-model="name1" placeholder="enter name" />
<input type="number" ng-model="no1" placeholder="enter number" />
<input type="button" ng-click="datas.add(name1,no1)" value="click me!!!!" />
it adds as many entries i want and works as expected.
What is wrong with the first approach?
ng-model="models.name" creates an object on the scope named "models". Changes to models.name and models.no will change values within that object, but it's still a single object -- so datas.add(model) winds up pushing a reference to that single object onto this.dataset. Further changes to model will change that original object, which is already in this.dataset, because it's all references to the same object.
Your other approach works correctly because you're using separate primitives for ng-model="name1" and ng-model="no1". Here, each time datas.add(name1,no1) runs, it's pushing a newly constructed object onto this.dataset. Further changes to name1 and no1 now won't modify old data, because they're primitives, not object references, and each time you push to this.dataset you construct a new object out of those primitives.
When you push the model object that you pass to your add() method onto the array you are actually pushing the reference to that object and now Angular is going to use two-way data binding on it. What you need to do is first copy the model so it disassociates it from the ng-model directives in your HTML.
this.add = function(model) {
var newModel = angular.copy(model);
this.dataset.push(newModel);
}
Here's a Plunker: http://plnkr.co/edit/SdtSesYxb828yJyDFwws?p=preview
I have a table that looks like this:
<table>
<tr>
<td class="packing-vol">0.19</td>
<td class="qty-cell"><input type="text" name="qty[]" class="qty" value="2" /></td>
</tr>
<tr>
<td class="packing_total">0.70</td>
<td class="qty-cell"><input type="text" name="qty[]" class="qty" value="1" /></td>
</tr>
</table>
I'm looping through each occurence of .packing-vol getting it's text, then I want to get the qty from the same row so I go up to it's parent then drill into the .qty class. But when I alert(qty) i get 'undefined' message.
var total = 0;
jQuery('.packing-vol').each(function(i) {
var qty = jQuery(this).parent("td.qty-cell .qty").val();
alert(qty);
var cur = parseFloat(jQuery(this).text());
if(!isNaN(cur)){
total = total + cur;
}
});
I think you should do this:
var qty = jQuery(this).parent().find("td.qty-cell .qty").val();
You need to go 1 level up (using .parent) and then find your field inside with .find
parent is just one level upwards. You can't use it to go inside trees again.
parents is multiple levels upwards. You can't use it to go inside trees again.
You can use parent/parents and then use find to do what you want, or even better:
var total = 0;
jQuery('.packing-vol').each(function(i) {
var qty = jQuery(this).parent().children('.qty-cell').children('.qty').val();
alert(qty);
var cur = parseFloat(jQuery(this).text());
if (!isNaN(cur)){
total = total + cur;
}
});
You could also use find, but it is slower than going directly inside, because it has to search the DOM object.
But you could also do:
var qty = jQuery(this).parent().find("td.qty-cell .qty").val();
Instead of
var qty = jQuery(this).parent("td.qty-cell .qty").val();
Try:
var qty = jQuery(this).parent().find(".qty").val();