$scope an array consisting Code and Amount on controller. When calculating summary on a function, browser gets Uncaught Error: 10 $digest() iterations reached. Aborting! error which caused from infinite loop (Strange but it is working).
Is there any proper way to combine new Array while ng-repeat without getting infinite loop errors?
Any help would be appreciated
jsFiddle Link
Update: Lines variables are not static, can be added, modified or removed.
jsFiddle Line for Update
var myApp = angular.module('myApp',[]);
function MyCtrl($scope) {
$scope.Lines = [ {Code:'X', Amount:'10'},
{Code:'Y', Amount:'10'},
{Code:'Z', Amount:'20'},
{Code:'Y', Amount:'1'}];
$scope.Sums = function(){
var sums = new Array();
for (var i = 0; i < $scope.Lines.length; i++) {
var added = false;
for (var j = 0; j < sums.length; j++) {
if (sums[j].Code == $scope.Lines[i].Code) {
sums[j].Amount = parseFloat( sums[j].Amount) + parseFloat($scope.Lines[i].Amount);
added = true;
break;
}
}
if (!added) {
sums.push( { Code:$scope.Lines[i].Code, Amount: $scope.Lines[i].Amount } );
}
}
return sums;
}
}
Html:
<div ng-controller="MyCtrl">
<table style="border: 1px solid black;">
<tr ng-repeat="line in Lines">
<td>{{ line.Code }}</td>
<td>{{ line.Amount }}</td>
</tr>
</table>
Summary
<table style="border: 1px solid black;">
<tr ng-repeat="sum in Sums()">
<td>{{ sum.Code }}</td>
<td>{{ sum.Amount }}</td>
</tr>
</table>
</div>
Does this still happen if you do this:
var myApp = angular.module('myApp',[]);
function MyCtrl($scope) {
$scope.Lines = [ {Code:'X', Amount:'10'},{Code:'Y', Amount:'10'},
{Code:'Z', Amount:'20'},{Code:'Y', Amount:'1'}];
$scope.Sums = [];
calculate();
var calculate = function(){
$scope.Sums.length = 0;
for (var i = 0; i < $scope.Lines.length; i++) {
var added = false;
for (var j = 0; j < $scope.Sums.length; j++) {
if ($scope.Sums[j].Code == $scope.Lines[i].Code) {
$scope.Sums[j].Amount = parseFloat( $scope.Sums[j].Amount) + parseFloat($scope.Lines[i].Amount);
added = true;
break;
}
}
if (!added) {
$scope.Sums.push( { Code:$scope.Lines[i].Code, Amount: $scope.Lines[i].Amount } );
}
}
}
}
Note that it is important to never create a new array once Sums is watched by angular. Use $scope.Sums.length = 0 instead if you need to empty it.
In your view: <tr ng-repeat="sum in Sums">
As Andre Kreienbring pointed out the problem is because your sum() is returning an object.
I would suggest using filters to accomplish what you need, like so
HTML
<div ng-controller="MyCtrl">
<table style="border: 1px solid black;">
<tr ng-repeat="line in Lines">
<td>{{ line.Code }}</td>
<td>{{ line.Amount }}</td>
</tr>
</table>
Summary
<table style="border: 1px solid black;">
<tr ng-repeat="sum in Lines | unique : 'Code'">
<td>{{ sum.Code }}</td>
<td>{{ Lines | filter: { Code: sum.Code } : true | sum: 'Amount' }}</td>
</tr>
</table>
</div>
Script
var myApp = angular.module('myApp',[]);
function MyCtrl($scope) {
$scope.Lines = [ {Code:'X', Amount:'10'},{Code:'Y', Amount:'10'},
{Code:'Z', Amount:'20'},{Code:'Y', Amount:'1'}];
}
myApp.filter('unique', function() {
return function(input, key) {
var unique = {};
var uniqueList = [];
for(var i = 0; i < input.length; i++){
if(typeof unique[input[i][key]] == "undefined"){
unique[input[i][key]] = "";
uniqueList.push(input[i]);
}
}
return uniqueList;
};
});
myApp.filter('sum', function() {
return function(input, key) {
var sum = 0;
for(var i = 0; i < input.length; i++){
sum += Number(input[i][key]);
}
return sum;
};
});
The unique filter is from https://stackoverflow.com/a/18382680/360067
Fiddle - http://jsfiddle.net/34od99sz/
I'd rather put a $watch on $scope.Lines which will create an array and populate it as $scope.sums such that ng-repeat doesn't have to call the method again and again. See Fiddle
Edit (added missing parameter for deep watching):
Fiddle
if I am not mistaken in the line
for (var j = 0; j < sums.length; j++)
you try to take the length of sums whitch is a new array. it has no length though. Also why do you use
var sums = new Array();
to initialize the array and not
var sums = [];
Related
Sorry, i'm new to ng-repeat. How can i show/hide row table that is using ng-repeat? And the most bottom row is shown if dropdown value is 1.
var i ;
$scope.names = [];
$scope.tmp = [];
for(i=0;i<=10;i++){
$scope.tmp[i] = i;
$scope.names[i] = "name "+ i;
}
$scope.isShow = true
html
<select>
<option ng-repeat="x in tmp">{{x}}</option>
</select>
<table>
<tr ng-show='isShow' ng-repeat="name in names">
<td>{{name}}</td>
</tr>
</table>
May be you must add property isShow for each name in names?
Or create array with visible status for each name.
angular.module('app', [])
.directive('appDir', appDir);
angular.bootstrap(
document.getElementById('root'), ['app']
);
function appDir() {
return {
template: `
<table>
<tr
ng-repeat="name in names"
ng-show="name.isShow"
>
<td>
{{name.title}}
</td>
</tr>
</table>
<select
ng-model="selectedName"
ng-options="x as x for x in tmp"
ng-change="hiddenName()"
>
`,
link: appDirLink
}
}
function appDirLink($scope) {
$scope.names = [];
$scope.tmp = [];
$scope.hiddenName = hiddenName;
for (var i = 0; i < 10; i++) {
$scope.names[i] = {
id: i,
title: 'name_' + i,
isShow: true
};
$scope.tmp[i] = i;
}
function hiddenName() {
$scope.names.map((name, index) => {
name.isShow = (index < $scope.selectedName) ? true : false;
});
}
}
<div id="root">
<app-dir></app-dir>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.7.5/angular.min.js"></script>
I'm trying to convert table string (like csv) to html table.
My code works fine with simple table but when it merged row and column it fails. so how do i can use rowspan and column span in the script?
<!DOCTYPE html>
<html ng-app="" ng-controller="myCtrl">
<style>
table, th, td {
border: 1px solid black;
padding:5px;
}
table {
border-collapse: collapse;
margin:10px;
}
</style>
<body>
<button ng-click="processData(allText)">
Display CSV as Data Table
</button>
<div id="divID">
<table>
<tr ng-repeat="x in data">
<td ng-repeat="y in x">{{ y }}</td>
</tr>
</table>
</div>
<div>
<table>
</table>
</div>
JS
<script>
function myCtrl($scope, $http) {
$scope.allText="Series |Wire Range\n (AWG) |"+
"Wire Type |FW |Voltage\n (V) |Current \n (A) |"+
"UG |CA |\nSL-6100 RS#2|26 16, SOL,\n Unprepared |"+
"Cu RS#2|2 RS#2|300 RS#2|7 RS#2|B, D RS#2|"+
"2(105), 4 RS#2|\n24 - 16, STR,\n Unprepared |"+
"\nNominal Strip length: 9 - 10 mm CS#8|"+
"\nEnvironmental - Maximum ambient temperature"+
" rating for CNR: 85 C CS#8|\n";
$scope.processData = function(allText) {
// split content based on new line
var allTextLines = allText.split(/\|\n|\r\n/);
var headers = allTextLines[0].split('|');
var lines = [];
for ( var i = 0; i < allTextLines.length; i++) {
// split content based on comma
var data = allTextLines[i].split('|');
if (data.length == headers.length) {
var temp = [];
for ( var j = 0; j < headers.length; j++) {
temp.push(data[j]);
}
lines.push(temp);
}
};
$scope.data = lines;
};
}
</script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.15/angular.min.js"></script>
</body>
</html>
RS#2 ---indicates rowspan of 2
cs#8 ---indicates colspan of 8
| ---is the dilimiter for cell
|\n ---for new line
and value in $scope.allText is CSV table string
so essentially i should get this table as output-
You can create an object with rows and cols so that you can use that as rowspan and colspan.
Like this
Demo
$scope.processData = function(allText) {
// split content based on new line
var allTextLines = allText.split(/\|\n|\r\n/);
var headers = allTextLines[0].split('|');
var lines = [];
var r,c;
for ( var i = 0; i < allTextLines.length; i++) {
// split content based on comma
var data = allTextLines[i].split('|');
if (data.length == headers.length) {
var temp = [];
for ( var j = 0; j < headers.length; j++) {
if(data[j].indexOf("RS") !== -1) {
r = data[j].split("#").reverse()[0];
}
else {
r = 0;
}
if(data[j].indexOf("CS") !== -1) {
c = data[j].split("#").reverse()[0];
}
else {
c = 0;
}
temp.push({"rows":r,"cols":c,"data":data[j]});
}
lines.push(temp);
}
}
alert(JSON.stringify(lines));
$scope.data = lines;
}
You can add CS to your string and alter conditions as required in this code.
Controller
function myCtrl($scope, $http) {
$scope.allText = "Series |Wire Range\n (AWG) |Wire Type |FW |Voltage\n (V) |Current \n (A) |UG |CA |\nSL-6100 RS#2|26 16, SOL,\n Unprepared |Cu RS#2|2 RS#2|300 RS#2|7 RS#2|B, D RS#2|2(105), 4 RS#2|\n24 - 16, STR,\n Unprepared |\nNominal Strip length: 9 - 10 mm CS#8|\nEnvironmental - Maximum ambient temperature rating for CNR: 85 C CS#8";
$scope.processData = function (allText) {
var table = [];
allText.split(/\|\n|\r\n/).forEach(function (line) {
var tr = [];
line.split('|').forEach(function (cell) {
tr.push({
text: cell.replace(/RS#.*$/, '').replace(/CS#.*$/, ''),
rowspan: (cell + 'RS#1').replace(/^[\S\s]*?RS#(\d*).*$/, '$1'),
colspan: (cell + 'CS#1').replace(/^[\S\s]*?CS#(\d*).*$/, '$1'),
})
})
table.push(tr)
});
$scope.table = table;
};
}
HTML
<table>
<tr ng-repeat="tr in table">
<td ng-repeat="td in tr" ng-attr-colspan="{{td.colspan}}" ng-attr-rowspan="{{td.rowspan}}">{{ td.text }}</td>
</tr>
</table>
Code Snippet
function myCtrl($scope, $http) {
$scope.allText = "Series |Wire Range\n (AWG) |Wire Type |FW |Voltage\n (V) |Current \n (A) |UG |CA |\nSL-6100 RS#2|26 16, SOL,\n Unprepared |Cu RS#2|2 RS#2|300 RS#2|7 RS#2|B, D RS#2|2(105), 4 RS#2|\n24 - 16, STR,\n Unprepared |\nNominal Strip length: 9 - 10 mm CS#8|\nEnvironmental - Maximum ambient temperature rating for CNR: 85 C CS#8";
$scope.processData = function (allText) {
var table = [];
allText.split(/\|\n|\r\n/).forEach(function (line) {
var tr = [];
line.split('|').forEach(function (cell) {
tr.push({
text: cell.replace(/RS#.*$/, '').replace(/CS#.*$/, ''),
rowspan: (cell + 'RS#1').replace(/^[\S\s]*?RS#(\d*).*$/, '$1'),
colspan: (cell + 'CS#1').replace(/^[\S\s]*?CS#(\d*).*$/, '$1'),
})
})
table.push(tr)
});
$scope.table = table;
};
}
angular.module('myApp', [])
.controller('ctrlr', myCtrl)
table, th, td {
border: 1px solid black;
padding: 5px;
}
table {
border-collapse: collapse;
margin: 10px;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp">
<div ng-controller="ctrlr">
<button ng-click="processData(allText)">
Display CSV as Data Table
</button>
<div id="divID">
<table>
<tr ng-repeat="tr in table">
<td ng-repeat="td in tr" ng-attr-colspan="{{td.colspan}}" ng-attr-rowspan="{{td.rowspan}}">{{ td.text }}</td>
</tr>
</table>
</div>
<div>
<table></table>
</div>
</div>
</div>
I have a table with each tr having class="ass testPageRow" I want to be able to all selected the td tags. I currently have some code that allows me to select the td individually. I am trying to implement a select all button.
var testPagesList = document.getElementsByClassName("testPageRow");
for (var i = 0; i < testPagesList.length; i++) {
var testPageItems = testPagesList[i].getElementsByTagName("td");
for (var j = 0; j < testPageItems.length; j++) {
testPageItems[j].onclick = function(event) {
if (this.className == "selected") {
this.className = "unselected";
} else {
this.className = "selected";
}
};
}
}
The format of my html
<table class="table">
<tbody>
<tr class="ass testPageRow">
<td id="tp1">1</td>
<td id="tp4">4</td>
<td id="tp5">5</td>
</tr>
<tr class="ass testPageRow">
<td id="tp12">12</td>
<td id="tp13">13</td>
<td id="tp14">14</td>
</tr>
<tr class="ass testPageRow">
<td id="tp14TTU">14TTU</td>
<td id="tp15">15</td>
<td id="tp16">16</td>
</tr>
<tr class="ass testPageRow">
<td id="tp18">18</td>
<td id="tp20">20</td>
<td id="tp21">21</td>
</tr>
</tbody>
</table>
Here is my current javascript code to select the tags. When I click the button, nothing happens. I'm not sure why. My logic was to iterate through all the objects and change the className to selected as I did in my previous code.
function selectAllTestPages() {
var selectAllTP = document.getElementById("selectAllTestPages");
selectAllTP.onclick = function(event) {
for (var i = 0; i < testPagesList.length; i++) {
var testPageTDTags = testPagesList[i].getElementsByTagName("td");
for (var td in testPageTDTags) {
td.className = "selected";
}
}
};
}
Button click:
<div class="center">
<button id="selectAllTestPages">Select All</button>
</div>
Perhaps try something like this?
Array.prototype.slice.call(document.querySelectorAll('.testPageRow td')).forEach(function(e) {
e.className = 'selected';
});
Demo: http://jsfiddle.net/62d4La7w/
You should really separate the function logic from the event listener logic.
The part of your code that was causing the functionality to break was the for in loop that you run on the testPageTDTags variable. You should have been using a regular loop with a counter.
Here's a new version of your code that will do what you are looking for:
// Logic to change all tds classes to 'selected'
function selectAllTestPages() {
var testPagesList = document.getElementsByClassName("testPageRow");
for (var i = 0; i < testPagesList.length; i++) {
var testPageTDTags = testPagesList[i].getElementsByTagName("td");
for (var j = 0; j < testPageTDTags.length; j++) {
testPageTDTags[j].className = "selected";
}
}
}
// Event listener that listens for button click
var button = document.getElementById('selectAllTestPages');
button.addEventListener('click', function(){
selectAllTestPages();
});
Here's a working example on jsfiddle
I add dynamically rows in my table with ng-repeat, coming from an array.
Now I want to get the sum of all sums per row (group.sum * group.perc / 100.0). I need it in a variable because I need this value for further calculations. Thank you
HTML
<tr ng-repeat="group in groupsArr">
<td class="total-rows" ng-model="taxes">{{group.sum * group.perc / 100.0 | currency :""}}</td>
</tr>
SCRIPT
var taxTotals = 0;
var taxTotals =
for (i=0; i<group.length; i++) {
taxTotal = taxTotal + group[i].taxes;
};
console.log(taxTotals);
};
Create a Filter:
app.filter('sumFilter', function() {
return function(groups) {
var taxTotals = 0;
for (i=0; i<groups.length; i++) {
taxTotal = taxTotal + groups[i].taxes;
};
return taxTotals;
};
});
Use the $filter service:
app.controller('myController', function($scope, $filter) {
$scope.groups = [...];
var taxTotals = $filter('sumFilter')($scope.groups);
console.log(taxTotals);
});
Use it in your HTML:
<tr ng-repeat="group in groupsArr">
<td class="total-rows" ng-model="taxes">{{group.sum * group.perc / 100.0 | currency :""}} </td>
</tr>
<tr>
<b> Tax Totals: </b> {{ groupsArr | sumFilter | currency }}
</tr>
An addition for best answer... I am using filter in my very huge table, so it is how to implement with dynamic filters.
THE FILTER
app.filter('sumStatusFilter', function(){
return function (items, filtersStatus, filterLocations){
var filtered = [];
var filtered1 = [];
var total = 0;
if (typeof filtersStatus != 'undefined') {
angular.forEach(items, function(item) {
for(i = 0; i < filtersStatus.length; i ++){
if(filtersStatus[i] == item.status_message)
filtered.push(item);
}
});
}
if (typeof filterLocations != 'undefined') {
angular.forEach(filtered, function(item) {
for(i = 0; i < filterLocations.length; i ++){
if(filterLocations[i] == item.office_location)
filtered1.push(item);
}
});
filtered = [];
filtered = filtered1;
}
if (filtered.length == 0) {
filtered = this.jobs
}
angular.forEach(filtered, function(value, key){
total += value.restoration_reserve
});
return total;
}
});
in HTML
<tr><td>Total: {{ report_controller.items | sumStatusFilter:report_controller.status_message_selections:report_controller.office_selections | currency }}</td></tr>
UPDATE AFTER ANSWER coming from pixelbits
Thanks to pixelbits. Here is my filter, which works perfect within the view.
HTML
<tr ng-repeat="group in groupsArr">
<td class="total-rows" ng-model="taxes">{{group.sum * group.perc / 100.0 | currency :""}} </td>
</tr>
<tr>
<b> Tax Totals: </b> {{ groupsArr | sumFilter | currency }}
</tr>
Filter
angular.module('App.filters', []).filter('sumFilter', [function () {
// filter for tax sum
return function(groups, lenght) {
var taxTotal = 0;
for (i=0; i < groups.length; i++) {
taxTotal = taxTotal + ((groups[i].perc * groups[i].sum) / 100);
};
return taxTotal;
};
}]);
If I want to access from my controller, it doesn´t work: I cannot get the variable taxTotals *Cannot read property 'length' of undefined
As mentioned, in the view it works.
Filter Service
var taxTotal = $filter('sumFilter')($scope.groups);
console.log(taxTotal);
Or use Map Reduce!
Controller
$scope.mappers = {
tax: function(m){
return group.sum * group.perc / 100.0;
}
}
$scope.sum = function(m){
if($scope.groupsArr.length == 0) return;
return $scope.groupsArr.map(m).reduce(function(p, c){
return p + c;
}) || 0;
};
HTML
<tr ng-repeat="group in groupsArr">
<td class="total-rows" ng-model="taxes">{{group.sum * group.perc / 100.0 | currency :""}} </td>
</tr>
<tr>
<b> Tax Totals: </b> {{ sum(mappers.tax) }}
</tr>
I have problem with showing and hiding rows, when I select two select buttons at once.
Using only one button, I can show/hide the correct rows.
Using both buttons at once, no rows will be displayed.
Where is the logical error in the code?
Please check: http://jsfiddle.net/xEyJZ/83/
<div ng-controller="MyCtrl">
<p>
<input type="checkbox" ng-init="showNew=false" ng-click="showNew =! showNew"><span> Show new only</span> <br>
<input type="checkbox" ng-init="showOld=false" ng-click="showOld =! showOld"><span> Show old only </span>
</p>
<table border="1">
<tr ng-repeat="person in persons" ng-class="{'newp':person.newp, 'oldp':person.oldp}"
ng-hide="(!person.newp && showNew) || (!person.oldp && showOld)">
<td>{{ person.id }}</td>
<td>{{ person.name }}</td>
<td>{{ person.city }}</td>
</tr>
</table>
</div>
You can use comparator in your filter:
ng-repeat="person in persons | filter:{newp:showNew, oldp:showOld}:showTest"
and in controller set comparison function:
$scope.showTest = function(actual, expected){
if(!$scope.showNew && !$scope.showOld)
return true;
return angular.equals(expected, actual);
}
http://jsfiddle.net/D79F7/2/
You have error in your boolean expression:
Correctly as follows:
ng-hide="(!person.newp && showNew ) || (!person.oldp && showOld)"
http://jsfiddle.net/ZWy93/2/
Using a custom filter:
<input type="checkbox" ng-model="showNew">..Show new only..
<input type="checkbox" ng-model="showOld">..Show old only..
..
<tr ng-repeat="person in persons | personsFilter : showNew : showOld" ..>
JS
myApp.filter("personsFilter",function(){
return function(input, showNew, showOld){
if(!showNew && !showOld){
return input;
}
else if(showNew && !showOld){
var temp = [];
for(var i = 0; i < input.length; i++){
if(input[i].newp && !input[i].oldp)
temp.push(input[i]);
}
return temp;
}
else if(showOld && !showNew){
var temp = [];
for(var i = 0; i < input.length; i++){
if(input[i].oldp && !input[i].newp)
temp.push(input[i]);
}
return temp;
}
else if(showOld && showNew){
var temp = [];
for(var i = 0; i < input.length; i++){
if(input[i].oldp || input[i].newp)
temp.push(input[i]);
}
return temp;
}
}
});
Fiddle