I'm trying to create a prototype that populates a list of unpaid invoices using ng-repeat. Within this, I am creating an input for each invoice. I want to be able to have a user input $ amounts into these inputs and then display a total of all inputs. Going off of this example: http://jsfiddle.net/d5ug98ke/9/ I cannot get it to work as it does in the fiddle. Here is my code:
<table class="table table-striped header-fixed" id="invoiceTable">
<thead>
<tr>
<th class="first-cell">Select</th>
<th class="inv-res2">Invoice #</th>
<th class="inv-res3">Bill Date</th>
<th class="inv-res4">Amount</th>
<th class="inv-res5">Amount to Pay</th>
<th class="inv-res6"></th>
</tr>
</thead>
<tbody>
<tr ng-if="invoices.length" ng-repeat="item in invoices | filter: {status:'Unpaid'}">
<td class="first-cell"><input type="checkbox" /></td>
<td class="inv-res2">{{item.invoiceNum}}</td>
<td class="inv-res3">{{item.creationDate}}</td>
<td class="inv-res4" ng-init="invoices.total.amount = invoices.total.amount + item.amount">{{item.amount | currency}}</td>
<td class="inv-res5">$
<input ng-validate="number" type="number" class="input-mini" ng-model="item.payment" ng-change="getTotal()" min="0" step="0.01" /></td>
</tr>
</tbody>
</table>
<table class="table">
<tbody>
<tr class="totals-row" >
<td colspan="3" class="totals-cell"><h4>Account Balance: <span class="status-error">{{invoices.total.amount | currency }}</span></h4></td>
<td class="inv-res4"><h5>Total to pay:</h5></td>
<td class="inv-res5">${{total}}</td>
<td class="inv-res6"></td>
</tr>
</tbody>
</table>
And the Angular:
myBirkman.controller('invoiceList', ['$scope', '$http', function($scope, $http) {
$http.get('assets/js/lib/angular/invoices.json').success(function(data) {
$scope.invoices = data;
});
$scope.sum = function(list) {
var total=0;
angular.forEach(list , function(item){
total+= parseInt(item.amount);
});
return total;
};
$scope.total = 0;
$scope.getTotal = function() {
$scope.invoices.forEach(function(item){
$scope.tot += parseInt(item.payment);
});
};
}]);
Any help would be appreciated. I dont necessarily need to use the method in the fiddle if someone has a better idea.
It seems there are two issues. First a simple typo:
$scope.tot += parseInt(item.payment, 10);
should be
$scope.total += parseInt(item.payment, 10);
And you should also reset $scope.total to 0 at the beginning of getTotal().
Edit: The way you did it, you always have to remember to update the total when something changes. Instead, you could let getTotal just return the total and write {{ getTotal() }} in the template. You don't have to trigger getTotal() in via ng-change then. If you don't have a lot of inputs you shouldn't worry about performance here.
Related
Basically, i have a structure like this example
Every cell of last column needs to have this formula:
value[i][2] = value[i-1][2] + value[i][0] - value[i][1]
I'm actually having 2 problems. The first one comes when i just try to program the first row of the table. What's wrong with this extremely simple thing?
angular.module('calc', [])
.controller('cont', function($scope) {
$scope.addNumbers = function() {
var c = aCom[30][5];
var a = parseFloat($scope.entrata1);
var b = parseFloat($scope.uscita1);
return c+a-b;
}
});
considering entrata1 and uscita1 as they are value[0][0] and value[0][1].
But most important, how can I extend the formula to all other rows? Consider that every row except the first one is created dinamically with an appendChild()function to the body, do i have to use at every appended item the function setAttribute("ng-model","entrata")?
Thanks
I would suggest forget appendchild. Use ng-repeat and add rows adding to the scope
like this
angular.module('plunker', []).controller('tableCtrl', function($scope,$filter) {
$scope.rows = [{'a': 0,'u': 300},{'a': 0,'u': 150},{'a': 200,'u': 0},{'a': 0,'u': 300}];
$scope.rowscalc = function(val){
var total=0;
angular.forEach($scope.rows, function(values, key){
if(key<=val) total += values.u-values.a;});
return total;
};
$scope.addRowM = function(){
$scope.rows.push({'a': 0, 'u': 0});
};
});
<script src="//unpkg.com/angular/angular.js"></script>
<div ng-app="plunker" ng-controller="tableCtrl">
<table class="table">
<thead>
<tr>
<td class="dthead">A</td>
<td class="dthead">U</td>
<td class="dthead">Total</td>
</tr>
</thead>
<tbody>
<tr ng-repeat="row in rows">
<td><input type="number" ng-model="row.a"/></td>
<td><input type="number" ng-model="row.u"/></td>
<td>{{rowscalc($index)}}</td>
</tr>
<tr>
<td colspan="3">
<button ng-click="addRowM()">Add Row</button>
</td>
</tr>
</tbody>
</table>
</div>
you can check in plunker
I use list.js to make a simple sorting of my table's data. Sorting functionality works fine but I want to modify it's initial behaviour. When an user sees the table for the first time he/she should have records with some certain value in one column (with my local currency in this example) put to the top. Just initially, later sorting should work in a standard way.
Let's see the code:
<table id="accountsList">
<thead>
<tr>
<th scope="col" class="sort" data-sort="currency-td" aria-role="button"><span>Currency</span></th>
<th scope="col" class="sort" data-sort="accountNo-td" aria-role="button"><span>Account number</span></th>
<th scope="col" class="sort td-centered" data-sort="used-td" aria-role="button"><span>Used</span></th>
</tr>
</thead>
<tbody class="list">
<tr>
<td class="currency-td">EUR</td>
<td class="accountNo-td">53106010151036926643566665</td>
<td class="used-td td-centered">
<input type="checkbox" checked>
</td>
</tr>
<tr>
<td class="currency-td">PLN</td>
<td class="accountNo-td">83106010151036926643522665</td>
<td class="used-td td-centered">
<input type="checkbox">
</td>
</tr>
<tr>
<td class="currency-td">PLN</td>
<td class="accountNo-td">59996010151036926643566665</td>
<td class="used-td td-centered">
<input type="checkbox" checked>
</td>
</tr>
<tr>
<td class="currency-td">USD</td>
<td class="accountNo-td">33106010151036999643566675</td>
<td class="used-td td-centered">
<input type="checkbox">
</td>
</tr>
</tbody>
<script type="application/javascript">
$(document).ready(function(){
var options = {
valueNames: ['currency-td', 'accountNo-td', 'used-td']
};
var accountsList = new List('accountsList', options);
accountsList.sort("currency-td", {
order: "desc"
});
});
</script>
The only thing I'd like to do is to put all the records with the 'PLN' currency at the top at the beginning. Why I don't just order them the way I want in HTML the way I want and later enable sorting, without initial sorting? Because in fact, these records are generated by PHP (I simplified the code above, just showing an example of generated HTML) and I can't predict what data I will get.
I need to write a sorting function in this place:
accountsList.sort("currency-td", {
order: "desc",
sortFunction: function () {}
});
Do you have any ideas? :)
try using alphabet feature of List.js, smthg like :
var options = {
valueNames: ['currency-td', 'accountNo-td', 'used-td']
};
var accountsList = new List('accountsList', options);
accountsList.sort("currency-td", { alphabet: "PLNABCDEFGHIJKMOQRSTUVXYZplnabcdefghijkmoqrstuvxyz" }
);
This is documented here http://listjs.com/api/#sort
I figured it out this way:
accountsList.sort('currencyTd', {
order: 'asc',
sortFunction: function (a, b) {
if ((a.currencyTd === 'PLN') != (b.currencyTd === 'PLN')) {
return a.currencyTd === 'PLN' ? 1 : -1;
}
return a.currencyTd > b.currencyTd ? 1 :
a.currencyTd < b.currencyTd ? -1 : 0;
}
});
The solution suggested in:
https://stackoverflow.com/a/17254561/5420497
Having a problem with in ng-init and ng-repeat angularjs
i'm trying to loop the fields rates to get the total
for example this is my table
nane +++++++id+++++++rate
joe +++++++++1+++++++3
joe +++++++++2+++++++3
joe +++++++++3+++++++3
joe +++++++++4+++++++3
this my code.
<table>
<tr>
<td>name</td>
<td>rate</td>
</tr>
<tr ng-repeat="item in videos">
<td>{{item.name}}</td>
<td ng-init="videos.total.rate = videos.total.rate + item.rate">{{item.rate}}</td>
</tr>
<tr>
<td>Total</td>
<td>{{videos.total.rate}}</td>
</tr>
The Result that I get is 3333 instead of 12 when added all together
this is the line with problem
<td ng-init="videos.total.rate = videos.total.rate + item.rate">{{item.rate}}</td>
if i change it to a number it works fine.
<td ng-init="videos.total.rate = videos.total.rate + 3">{{item.rate}}</td>
your help would be great.thanks
Try something like this in controller.
JS
$scope.RateTotal= 0;
for (var i = 0; i < data.length; i++) {
$scope.RateTotal= $scope.RateTotal + data[i].rate;
}
HTML
<p>{{RateTotal}}</p>
Option above is better, but if you want use ng-init use something like this.
<table ng-init="RateTotal = 0">
<thead>
<th>Rate</th>
</thead>
<tbody>
<tr ng-repeat="item in videos">
<td ng-init="$parent.RateTotal= $parent.RateTotal + item.rate">{{item.rate}}</td>
</tr>
<tr>
<thead>
<tr>
<th>Total</th>
<th>{{RateTotal}}</th>
</tr>
</thead>
</tr>
</tbody>
</table>
P.S.
This directive can be abused to add unnecessary amounts of logic into
your templates. There are only a few appropriate uses of ngInit, such
as for aliasing special properties of ngRepeat, as seen in the demo
below; and for injecting data via server side scripting. Besides these
few cases, you should use controllers rather than ngInit to initialize
values on a scope. - ngInit
move this logic into the controller. That is what it is for. The view should be displaying data.
Now you have to worry about how to cast strings to an integer and writing 4x more code in a language that angular must interpret into javascript.
The complexity you are adding here is going to be fun to maintain.
but you probably want to continue doing it wrong, in which case this should work: <table ng-init='videos.total = {"rate": 0}'>
Define a filter to calculate the total:
app.filter('calculateRateTotal',function(){
return function(input){
var total = 0;
angular.forEach(input,function(value,key){
total = total+value.rate;
});
return total;
};
});
HTML:
<td ng-bind="videos | calculateRateTotal"></td>
After 10hrs of no luck just my managed to get it right. turned to be very simple.
This is the code.
In the Controller added
$scope.getTotal = function(){
var total = 0;
for(var i = 0; i < $scope.videos.length; i++){
var item = $scope.videos[i];
total += (item.rate*1);
}
return total; }
And HTML
<table>
<tr>
<th>Rate</th>
</tr>
<tr ng-repeat="item in videos">
<td>{{item.rate}}</td>
</tr>
<tr>
<td>Total: {{ getTotal() }}</td>
</tr>
</table>
Thanks everyone for helping
Every time the toggle is clicked, all payments are getting replaced with new payments. My problem is how to maintain the payments of a particular index of every click and show at respective index. please help me out
here is my html
<tbody data-ng-repeat="invoice in relatedInvoices>
<tr>
<td class="td-bottom-border">
{{invoice.PayableCurrencyCode}} {{invoice.PayablePaidAmount | number: 2}}<br />
<small>
<a data-ng-click="isOpenPayablePayments[$index] = !isOpenPayablePayments[$index]; togglePayablePayments(invoice.PayableInvoiceId)">Paid</a>
</small>
</td>
</tr>
<tr data-ng-show="isOpenPayablePayments[$index]">
<td>
<table>
<thead>
<tr>
<th>Transaction Id</th>
</tr>
</thead>
<tbody>
<tr data-ng-repeat="payment in payablePayments">
<td>{{payment.TransactionId}}</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
Here is my javascript
var getPayments = function (invoiceId) {
paymentService.getPayments(invoiceId).then(function (paymentsResponse) {
return paymentsResponse.data;
});
};
$scope.togglePayablePayments = function(invoiceId) {
$scope.payablePayments = getPayments(invoiceId);
};
If I understood correctly, you want to have "payablePayments" for every invoice.
This is working: http://plnkr.co/edit/cj3jxZ?p=info
Try something like
// init at beginning
$scope.payablePayments = [];
$scope.togglePayablePayments = function(invoiceId) {
$scope.payablePayments[invoiceId] = getPayments(invoiceId);
};
and then
<tr data-ng-repeat="payment in payablePayments[invoice.PayableInvoiceId]">
Otherwise you overwrite the object for the preceding invoice.
I used ng-repeat to repeat json array. I calculated Night(s) by using dayDiff() function. Now I want to get total night all invoices. I am using angularjs.
How can I get total nights for all invoices?
<table class="table" ng-show="filteredItems > 0">
<tr>
<td>No</td>
<td>Invoice No</td>
<td>Name</td>
<td>Eamil</td>
<td>Room Name</td>
<td>Check In Date</td>
<td>Check Out Date</td>
<td>No. Room</td>
<td>Night(s)</td>
<td>Booking Date</td>
<td>Amount</td>
</tr>
<tr ng-repeat="data in filtered = (list | filter:search ) | startFrom:(currentPage-1)*entryLimit | limitTo:entryLimit">
<td>{{$index+1}}</td>
<td>{{data.invoicenumber}}</td>
<td>{{data.firtname}}{{data.lastname}}</td>
<td>{{data.email}}</td>
<td>{{data.roomname}}</td>
<td ng-model='fromDate'>{{data.cidt}}</td>
<td ng-model='toDate'>{{data.codt}}</td>
<td>{{data.qty}}</td>
<td ng-model='night'>{{dayDiff(data.cidt,data.codt)}}</td>
<td>{{data.bdt}}</td>
<td>{{data.btotal}}</td>
</tr>
</table>
You need to add an extra row to begin with. This extra row will look like this:
<tr>
<td colspan="11">Total nights: {{calcTotal(filtered)}}</td>
</tr>
Then in your controller you need to add a function to calculate the nights like
$scope.calcTotal = function(filtered){
var sum = 0;
for(var i = 0 ; i<filtered.length ; i++){
sum = sum + filtered[i].nights;
}
return sum;
};
You could first, use a factory for your JSON model, to store the computed nights:
// We inject the dayDiff function via the dayDiffService service
angular.factory('invoice', ['dayDiffService', function(dayDiffService) {
var Invoice = function(data) {
// merge json properties to self
angular.merge(this, data);
// we compute the night(s)
this.nights = dayDiffService.dayDiff(data.cidt, data.codt);
}
return Invoice;
}]);
Then, in your controller, you add a function to sum up the nights from a filtered list:
angular.controller('invoicesCtrl', ['$scope', 'invoice', function($scope, Invoice) {
$scope.list = [];
// let's say that JSON holds your json model from http's response
$scope.list = JSON.map(function() {
return new Invoice(i)
});
$scope.sumNights = function(filtered) {
filtered.reduce(function(sum, invoice) {
sum += invoice.nights;
sum
}, 0);
}
}]);
Then, in your html you add a new row to display the computed result:
<div ng-controller="invoicesCtrl as vm">
<table>
...
<tbody>
<tr ng-repeat="data in filtered = (vm.list | filter:search ) | startFrom:(currentPage-1)*entryLimit | limitTo:entryLimit">
<td>{{$index+1}}</td>
...
<tr>
</tbody>
<tfoot>
<tr>
<td colspan="8"></td
<td>{{vm.sumNights(filtered)}}</td
<td colspan="2"></td
</tr>
</tfoot>
</table>
</div>