How to create sum of numbers from ng-repeat directive - angularjs

I'm trying to sum a column of numbers but when I use code below, the "total" field is empty. Any thoughts on what I might be doing wrong?
HTML:
<tr ng-repeat="project in projectList.projects>
<td>{{project.description}}</td>
<td>{{project.type.cost | currency}}</td>
</tr>
<h2>Total: {{ total() | currency }}</h2>
Javascript:
myApp.controller('ProjectListCtrl', function ProjectListCtrl(Projects) {
var projectList = this;
projectList.total = function(){
var total = 0;
angular.forEach(projectList.projects, function(project) {
total += project.type.cost;
});
return total;
};
})

It appears that you're using the controllerAs syntax, such that ng-controller="ProjectListCtrl as projectList"
With that, you need to call projectList.total() from the view instead of just total()

Related

Angularjs: How to multiply total got from total filter in angularjs for showing total in different currency value?

I am using angularjs for a reporting app and i need to show total value in multiple currency. I obtained total making my own filter and applying it as such:
My filter file
app.filter('sumByColumn', function () {
return function (collection, column) {
var total = 0;
var ic = 0;
collection.forEach(function (item) {
total += parseInt(item[column]);
});
return total;
}; })
I implemented total in my table and shows value as such:
<strong>NC Rs. {{ vm.sameNameInNgrepeatFromTableList | sumByColumn: 'slValue' }}</strong>
Now i want to show total in Indian currency multiplying total by 1.6.How can i Implement this?
You probably could use something like
<strong>NC Rs. {{ vm.sameNameInNgrepeatFromTableList * 1.6 | sumByColumn: 'slValue' }}</strong>
Please take reference from below code snippet:
function TodoCtrl($scope) {
$scope.quantity = 1;
$scope.price = 10;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.0.3/angular.min.js"></script>
<div ng-app>
<h2>Todo</h2>
<div ng-controller="TodoCtrl">
Quantity: <input type="number" ng-model="quantity">
Price: <input type="number" ng-model="price">
<p> Toal Default = {{ (quantity * price) | currency }}</p>
<p>Total in INR = {{ (quantity * price) | currency :"INR " }}</p>
</div>
</div>
I made another filter using same filter above:
app.filter('ic', function () {
return function (collection, column) {
var total = 0;
var ic=0;
collection.forEach(function (item) {
total += parseInt(item[column]);
ic= total *1.6;
});
return ic;
};
})
And used that filter for currency conversion as follows
<strong>IC INR. {{ vm.sameNameInNgrepeatFromTableList | ic : 'slValue' }}</strong>

Angular JS multiple filter query

I'm a newcomer to Angular/JS so please be kind ...
I'm having a problem (probably with myself trying to wrap my brain around Angular) with some code where I want to use two different filters (I think) on a dataset with ng-repeat. Firstly here is a screen shot of the html form items and data output I am working with;
Here is the basic (stripped down) HTML code for the setup above;
<div ng-controller="customersCrtl">
Records Per Page:
<select ng-model="entryLimit" class="form-control">
<option>50</option>
<option>100</option>
<option>250</option>
<option>500</option>
<option>1000</option>
</select>
Record Type:
<select ng-model="entryType" class="form-control">
<option>All</option>
<option>Baptism</option>
<option>Marriage</option>
<option>Burial</option>
</select>
Filter Text:
<input type="text" ng-model="search" ng-change="filter()" placeholder="Filter" class="form-control" />
Filtered {{ filtered.length }} of {{ totalItems }} Total Records
<div ng-show="filteredItems > 0">
<table>
<thead>
<th>Event Date <a ng-click="sort_by('eventDate');">sort</a></th>
<th>Event Type <a ng-click="sort_by('type');">sort</a></th>
<th>Indivdual(s) <a ng-click="sort_by('name');">sort</a></th>
<th>Location <a ng-click="sort_by('location');">sort</a></th>
<th></th>
</thead>
<tbody>
<tr ng-repeat="data in filtered = (list | filter:search | orderBy : predicate :reverse) | startFrom:(currentPage-1)*entryLimit | limitTo:entryLimit">
<td>{{data.eventDate | date:'mediumDate'}}</td>
<td>{{data.type}}</td>
<td>{{data.name}}</td>
<td>{{data.location}}</td>
<td>View Record</td>
</tr>
</tbody>
</table>
</div>
... and here is the associated Angular JS code I currently have;
var app = angular.module('myApp', ['ui.bootstrap']);
app.filter('startFrom', function() {
return function(input, start) {
if(input) {
start = +start; //parse to int
return input.slice(start);
}
return [];
}
});
app.controller('customersCrtl', function ($scope, $http, $timeout) {
$http.get('ajax/getMarkers.php').success(function(data){
$scope.list = data;
$scope.currentPage = 1; //current page
$scope.entryLimit = 50; //max no of items to display in a page
$scope.entryType = 'All'; //Record type to display in a page
$scope.filteredItems = $scope.list.length; //Initially for no filter
$scope.totalItems = $scope.list.length;
});
$scope.setPage = function(pageNo) {
$scope.currentPage = pageNo;
};
$scope.filter = function() {
$timeout(function() {
$scope.filteredItems = $scope.filtered.length;
}, 10);
};
$scope.sort_by = function(predicate) {
$scope.predicate = predicate;
$scope.reverse = !$scope.reverse;
};
});
getMarkers.php in the above Angular code snippet returns JSON results formatted like so;
{"id":"646","eventDate":"1576-05-13","name":"John Phillip","type":"Baptism","churchName":"St. Marys","locationid":"563","location":"Swainswick, Somerset, UK","lat":"51.414211","lng":"-2.351418"},
{"id":"647","eventDate":"1577-07-01","name":"Jane Volantyne","type":"Baptism","churchName":"St. Mary the Virgin","locationid":"564","location":"Horsham, Sussex, UK","lat":"51.059750","lng":"-0.330879"},
{"id":"132","eventDate":"1634-05-09","name":"Katherine Stanley","type":"Burial","churchName":"St. James","locationid":"567","location":"Titsey, Surrey, UK","lat":"51.276505","lng":"0.018909"}
... etc. etc.
This currently works in so-far-as if a user types something in the "Filter Text" box, the table updates to only show entries that match the user text.
This is fine, but what I also want to be able to do is use the drop down "Record Type" box (with the defined entries All, Baptism, Burial, Marriage) to only show/filter the records with that particular "Event Type" in the table.
Is this even possible? Can you apply two concurrent filters to an ng-repeat in Angular, because I've tried and failed several times and am on the verge of giving up!!!
Am I asking something stupidly difficult or stupidly easy?
Yes, its very possible to filter the table based on multiple conditions, You could add a filter function to the table as:
<tr ng-repeat="data in filtered = (list | filter:filterEvents | orderBy : predicate :reverse) | startFrom:(currentPage-1)*entryLimit | limitTo:entryLimit">
<td>{{data.eventDate | date:'mediumDate'}}</td>
<td>{{data.type}}</td>
<td>{{data.name}}</td>
<td>{{data.location}}</td>
<td>View Record</td>
</tr>
Basically, $scope.filterEvents is called every time the table is populated and you could filter out the required items using this in the controller:
$scope.filterEvents = function(item){
//Here item is the array element from the table
which is automatically passed in the filter function.
return item.type.toLowerCase().indexOf($scope.entryType.toLowerCase()) != -1 ||
item.name.toLowerCase().indexOf($scope.search.toLowerCase()) != -1
}
Where , $scope.entryType is the option selected in the dropdown and $scope.search is the keyword for searching.
Ps: You would need to remove the ng-change=filter() from search textbox and handle the return if $scope.entryType = "All"

Custom order using orderBy in ng-repeat

I have objects like this:
students = {name: 'Aa_Student', class: 'A_Class'},
{name: 'Ab_Student', class: 'A_Class'},
{name: 'Ac_Student', class: 'B_Class'},
{name: 'Ba_Student', class: 'B_Class'},
{name: 'Bb_Student', class: 'C_Class'},
{name: 'Bc_Student', class: 'C_Class'}
Let's say the students object is shuffled. I use ng-repeat to show the data. I want to sort the objects in the custom order.
For example, I want to show the data like this:
Name Class
-----------------------------
Ac_Student B_Class
Ba_Student B_Class
Aa_Student A_Class
Ab_Student A_Class
Bb_Student C_Class
Bc_Student C_Class
So basically, I want to order by student's class, but it B_Class comes first, then A_Class, then C_Class. Also, I want to order by students name in alphabetic order. How can I do this?
HTML:
<table>
<tr ng-repeat="student in students | orderBy:customOrder">
...
</tr>
</table>
Controller:
$scope.customOrder = function(student) {
$scope.students = $filter('orderBy')(student, function() {
});
};
Hi you can create custom sort filter please see here http://jsbin.com/lizesuli/1/edit
html:
<p ng-repeat="s in students |customSorter:'class'">{{s.name}} - {{s.class}} </p>
</div>
angularjs filter:
app.filter('customSorter', function() {
function CustomOrder(item) {
switch(item) {
case 'A_Class':
return 2;
case 'B_Class':
return 1;
case 'C_Class':
return 3;
}
}
return function(items, field) {
var filtered = [];
angular.forEach(items, function(item) {
filtered.push(item);
});
filtered.sort(function (a, b) {
return (CustomOrder(a.class) > CustomOrder(b.class) ? 1 : -1);
});
return filtered;
};
});
Know this is old but may come in handy for others...
You could also create a simple custom sort function. "Not quite a filter":
$scope.customOrder = function (item) {
switch (item) {
case 'A_Class':
return 2;
case 'B_Class':
return 1;
case 'C_Class':
return 3;
}
};
And then use like you wanted to:
<table>
<tr ng-repeat="student in students | orderBy:customOrder">
...
</tr>
to set the orderBy as a property of the objects just quote that property name within the markup:
ng-repeat="student in students |orderBy:'name' | orderBy:'class'"
DEMO

Calculate in ng-repeat from the entry before

I have a filtered list (which is filtered by time - so in a specific timeframe) and over these item I am iterating with ng-repeat. These items have a name and a price. So if I am iterating over them I want to achieve that I always show the "sub"-total like this:
DATE NAME PRICE SUBTOTAL
2014-05 T-Shirt 20.00 20.00
2014-05 Jeans 45.00 65.00
2014-05 Cap 15.00 80.00
These Items are sorted by date but might have a different ID (ids dont match the index!).
I am really not able to find out how I could always calculate the subtotal (the table can be filtered by date ranges, means I could also include the items from 2014-04 and it should recalculate dynamically.
I tried it with a function like this in the controller:
var curBalanceCounter2 = 0;
$scope.currentBalanceCalc = function(finance) {
curBalanceCounter2 = curBalanceCounter2 + finance.amount;
return curBalanceCounter2;
}
But this i being executed 10 times so I get wrong numbers. Any better solution?
Thank you.
Create a custom filter
myApp.filter('subtotal', function(){
return function(items, index){
var subtotal = 0;
for (var i = 0; i <= index; i++) {
subtotal += items[i].price
}
return subtotal || items[index].price;
}
});
and call it like so
<li ng-repeat="item in items">{{item.name}} - {{item.price}} -
{{ items | subtotal : $index}}</li>
Demo
Since you have access to the original list (e.g. items in the code above) inside of an ng-repeat, you can pass it, along with the index of the current item, into a custom filter. This filter can then loop through each item up to and including the index passed in, and then return a summed subtotal. If the subtotal is 0 (as it would be for a first item), instead return the price of that item.
Docs: Custom filters in Angular
This is similar to Marc's answer. Define a subtotal function in the controller:
$scope.subtotal = function(index){
var total = 0;
angular.forEach($scope.data, function(value, key){
if(key <= index)
total += value.Price;
});
return total;
}
Then use it like this in the view:
<tr ng-repeat="d in data">
<td>{{d.Date}}</td>
<td>{{d.Name}}</td>
<td>{{d.Price}}</td>
<td>{{subtotal($index)}}</td>
</tr>
Demo
Update
If the issue is that the data isn't already sorted on the client, but is being sorted by a filter on the ng-repeat, then here's the fix:
Pass in the orderBy parameter to the subtotal function, and execute the filter on the data before computing the subtotals:
$scope.orderBy = 'Date';
$scope.subtotal = function(index, orderBy){
var total = 0;
angular.forEach($filter('orderBy')($scope.data,orderBy), function(value, key){
if(key <= index)
total += value.Price;
});
return total;
}
I've updated my demo with this code. You can change the sort order by changing 'Date' to 'Name' or 'Price' on this line
$scope.orderBy = 'Date';
and see that the subtotals automatically recalculate.
I don't know of a way to do this in pure angular, perhaps someone will chime in.
What you need looks like a cumulative sum:
function cSum(arr) {
var cumsum = [];
for(var i=0;i<arr.length;i++) {
if(i==0) cumsum[i] = arr[0];
else cumsum[i] = cumsum[i-1] + arr[i];
}
return cumsum
}
Then just add that field into the array of objects that you are repeating over and you can display it in the table.
Not too hard to do http://jsfiddle.net/VAJ5S/3/
HTML
<div ng-app="myApp">
<table ng-controller="myController">
<thead>
<tr>
<th>DATE</th>
<th>NAME</th>
<th>PRICE</th>
<th>SUBTOTAL</th>
</tr>
</thead>
<tr ng-repeat="item in items">
<td>{{item.date}}</td>
<td>{{item.name}}</td>
<td>{{item.price}}</td>
<td>{{subtotal($index)}}</td>
</tr>
</table>
</div>
JS
var app = angular.module("myApp", []);
app.controller("myController", ["$scope", function($scope){
$scope.items = [
{
date: "2014-05",
name: "T-Shirt",
price: 20.00
},
{
date: "2014-05",
name: "Jeans",
price: 65.00
},
{
date: "2014-05",
name: "Cap",
price: 80.00
}
];
$scope.subtotal = function(ind){
var subtotal = 0;
for (var i = 0; i<=ind; i++){
subtotal += $scope.items[i].price;
}
return subtotal;
};
}]);

AngularJs ng-repeat change loop increment

I'm newbie to AngularJs. I want to use ng-repeat as for(i=0; i < ele.length; i+=2)
I have a table with 4 columns, where I'm going to use ng-repeat
<table>
<tr ng-repeat="i in elements">
<th>{{i.T}}</th>
<td>{{i.V}}</td>
<th>{{elements[($index+1)].T}}</th> <!-- This should be next element of the elements array -->
<td>{{elements[($index+1)].V}}</td> <!-- This should be next element of the elements array -->
</tr>
</table>
I need to access 2 elements in a single iteration and iteration increment should be 2
I hope this make sense. Please help me.
Please check this html view Plunker
You can create a filter that creates an even copy of the array:
.filter("myFilter", function(){
return function(input, test){
var newArray = [];
for(var x = 0; x < input.length; x+=2){
newArray.push(input[x]);
}
return newArray;
}
});
JsFiddle: http://jsfiddle.net/gwfPh/15/
So if I understand correctly you want to walk your list and alternate th and td's while iterating.
If so you could use a ng-switch:
<table>
<tr ng-repeat="i in elements" ng-switch on="$index % 2">
<th ng-switch-when="0">{{i.T}}</th>
<td ng-switch-when="1">{{i.V}}</td>
</tr>
</table>
See Plunker here
One solution I can think of involves a change in the data model
Template
<table ng-app="test-app" ng-controller="TestCtrl">
<tr ng-repeat="i in items">
<th>{{i.T1}}</th>
<td>{{i.V1}}</td>
<th>{{i.T2}}</th>
<td>{{i.V2}}</td>
</tr>
</table>
Controller
testApp.controller('TestCtrl', ['$scope', function($scope) {
var elements=[]; //This is the dynamic values loaded from server
for (var i = 0; i < 5; i++) {
elements.push({
T : i,
V : 'v' + i
});
}
//A converter which convert the data elements to a format we can use
var items = [];
var x = Math.ceil(elements.length / 2);
for (var i = 0; i < x; i++) {
var index = i * 2;
var obj = {
T1 : elements[index].T,
V1 : elements[index].V
}
if (elements[index + 1]) {
obj.T2 = elements[index + 1].T;
obj.V2 = elements[index + 1].V
}
items.push(obj)
}
$scope.items = items;
}]);
Demo: Fiddle
Another slightly different approach can be found here.

Resources