AngularJs ng-repeat change loop increment - angularjs

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.

Related

Get filtered value from view to controller

I use ng-repeat to show my array of objects. One of the attributes is video duration, and I used filter to directly show sum of all meta_durations. here is filter
app.filter('sumProduct', function() {
return function (input) {
var i = input instanceof Array ? input.length : 0;
var a = arguments.length;
if (a === 1 || i === 0)
return i;
var keys = [];
while (a-- > 1) {
var key = arguments[a].split('.');
var property = getNestedPropertyByKey(input[0], key);
if (isNaN(property))
throw 'filter sumProduct can count only numeric values';
keys.push(key);
}
var total = 0;
while (i--) {
var product = 1;
for (var k = 0; k < keys.length; k++)
product *= getNestedPropertyByKey(input[i], keys[k]);
total += product;
}
return total;
function getNestedPropertyByKey(data, key) {
for (var j = 0; j < key.length; j++)
// data = dataDuration[key[j]];
data = data.meta_duration;;
return data;
}
}
})
and in view
<table>
<thead>
<tr>
<th>Media name</th>
<th>Media type</th>
<th>Media duration in sec</th>
<th>Media thumbnail</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="media in myMediaItems">
<td>{{media.name}}</td>
<td>{{media.type}}</td>
<td>>{{media.meta_duration}}</td>
<td><img src="{{media.thumbnail}}" alt="" /></td>
</tr>
</tbody>
<tr>
<td></td>
<td></td>
<td>Total duration {{ myMediaItems|sumProduct:'duration' }}</td> //here is where i use custom filter
<td></td>
</tr>
</table>
How to get total duration value in the controller?
Dynamically i add (push) media to list from another list with all media.
$scope.addToMediaList = function(item) {
var seconds = parseInt(item.meta_duration);
$scope.view_duration = Math.floor(moment.duration(seconds,'seconds').asHours()) + ':' + moment.duration(seconds,'seconds').minutes() + ':' + moment.duration(seconds,'seconds').seconds();($scope.view_duration_seconds, "HH:mm:ss");
$scope.myMediaItems.push(item);
$scope.sumDuration = $filter("sumProduct")(myMediaItems.meta_duration); //i try like this, set on $scope.sumDuration but in conole i get only number 0
$scope.myMediaItemsId.push(item.id);
$scope.input = "";
};
Thanks
I simplified your filter.
try this
app.filter('sumProduct', function() {
return function (input) {
var i = input instanceof Array ? input.length : 0;
var a = arguments.length;
if (i === 0)
return i;
var total = 0;
for(var x of input){
var duration = parseFloat(x.meta_duration);
if(isNaN(duration)){
throw 'filter sumProduct can count only numeric values';
}
total += duration;
}
return total;
}
});
You can use something like this
<div ng-init="sumDuration = myMediaItems|sumProduct:'duration'"> </div>
I solved this by forwarding the returned value from the custom filter to controller.
var filtered = $filter('sumProduct')($scope.myMediaItems);
console.log(filtered)
Filtered value is sum. thnx # Amaya San

Calculate the Sum of Values using ng-repeat in Angular js

I am newbie to Angular js.I am just trying to calculate the sum of Values of each row using angular js.Here, I am dynamically adding and removing rows.
For Eg: Let's say first row value is 30, Now I added one row with value 30, then total should be show 60 like this.
1. 30
2. 30
3. 50
Total 120
HTML Code:
<table id="t1" style="border:none;">
<tr><th>Start</th><th>Stop</th><th>Downtime(In Min)</th><th>Reason</th></tr>
<tr ng-repeat="item in invoice">
<td><input type="text" required ng-model="$invoice.start" name="r1[]"></td>
<td><input type="text" required ng-model="$invoice.stop" name="r2[]" ng-blur="diff($invoice)"></td>
<td><input type="text" name="r3[]" ng-model="$invoice.diff"/></td>
<td style="border:none;"><a href ng-click="remove(item)">X</a></td>
</tr>
<tr style="border:none;">
<td style="border:none;"><a href ng-click="add()">+</a></td>
</tr>
</table>
<span class="labelCode">Total Downtime</span><input required type="text" name="Tot_D" ng-value="d1()"/></span></br>
Angular JS:
$scope.d1=function(){
var total = 0;
for(var i = 0; i < $scope.invoice.length; i++){
var product = $scope.invoice[i];
total += parseInt(product.diff);
}
return total;
}
//Calculate the Difference between two times
$scope.diff = function(item) {
item.diff = computeDiff(item.start,item.stop);
}
function computeDiff(start, stop) {
if (start && stop) {
var s_hr = start.split(":")[0];
var s_min = start.split(":")[1];
var e_hr = stop.split(":")[0];
var e_min = stop.split(":")[1];
return Math.abs((parseInt(e_hr) - parseInt(s_hr)) * 60) + Math.abs(parseInt(e_min) - parseInt(s_min))
}
}
Now, it just showing NaN. I don't know what is wrong.
Please help out.Thanks in advance.

Dynamically updating ng-repeat for matrix

I have matrix(2D array) cells in model, what I represent in a table.
<tr ng-repeat='row in cells track by $index'>
<td ng-repeat='cell in row track by $index'>
{{cell}}
</td>
</tr>
I want add a drop down buttons to resize length and width,
<select ng-model="length" >
<option ng-repeat="size in sizes" ng-click="setLength(size)">{{size}} </option>
</select>
but can't refresh ng-repeat. For refresh page I use $scope.$apply() after set new matrix in cells
$scope.cells = new Array($scope.width);
for (var i = 0; i < $scope.cells.length; i++) {
$scope.cells[i] = new Array($scope.length);
for (var j = 0; j < $scope.length; j++) {
$scope.cells[i][j] = 'cell';
}
}
$scope.$apply();
but it doesn't help. How to refresh ng-repeat?
P.S. When user is changing size matrix, it should back to default values with new size.
my demo is here
In your fiddle your 2D array is not created corretly. Please have a look at this SO question for tips how to setup the array.
I've changed these points of your code:
used ng-options for length and width dropdown. That's easier than ng-repeat.
Removed click events from the width and length drop down and added a $watch for width and length scope variables for updating the map.
Updated creation of 2d array.
Below is a working demo of your code or in this fiddle.
var myapp = angular.module('mapApp', []);
myapp.controller('editorController', function ($scope) {
$scope.cells = [
[]
];
$scope.sizes = [];
makeSizes();
$scope.length = $scope.sizes[0];
$scope.width = $scope.sizes[0];
$scope.$watch('[width,length]', makeMap, true);
function makeMap() {
var cols = $scope.width,
rows = $scope.length;
console.log('makeMap');
$scope.cells = matrix(rows, cols, 'cell');
}
function matrix(rows, cols, defaultValue) {
// code from here https://stackoverflow.com/questions/966225/how-can-i-create-a-two-dimensional-array-in-javascript
var arr = [[]];
// Creates all lines:
for (var i = 0; i < rows; i++) {
// Creates an empty line
arr[i] = [];
// Adds cols to the empty line:
arr[i] = new Array(cols);
for (var j = 0; j < cols; j++) {
// Initializes:
arr[i][j] = defaultValue;
}
}
return arr;
}
makeMap();
function makeSizes() {
for (var i = 0; i < 5; i++) {
$scope.sizes.push(i + 3);
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="mapApp" ng-controller="editorController">
<label>Length</label>
<select ng-model="length" ng-options="length for length in sizes">
</select>
<table>
<label>Width</label>
<select ng-model="width" ng-options="width for width in sizes">
</select>
<table>
<tbody>
<tr ng-repeat="row in cells track by $index">
<td ng-repeat="cell in row track by $index">
{{cell}}
</td>
</tr>
</tbody>
</table>
</div>

auto increment field in bootstrap table using angular- meteor

I am building my first angular-meteor app.
Find below the app.js file:
libri = new Mongo.Collection("libri");
if (Meteor.isClient) {
angular.module('bookshelf',['angular-meteor']);
angular.module('bookshelf').controller('BookListCtrl', ['$scope','$meteor',
function($scope,$meteor){
$scope.libri = $meteor.collection(libri)
$scope.counter = 0;
for(i = 0; i < libri.find().count(); i++){
$scope.counter += 1;
}
}]);
}
if (Meteor.isServer) {
Meteor.startup(function () {
});
}
and the index.ng.html file:
<div class="container">
<table class="table" ng-controller="BookListCtrl">
<caption>Optional table caption.</caption>
<thead>
<tr>
<th>Contatore</th>
<th>Titolo</th>
<th>Autore</th>
<th>Casa Editrice</th>
</tr>
</thead>
<tbody ng-repeat="libro in libri">
<tr>
<td>{{counter}}</td>
<td>{{libro.titolo}}</td>
<td>{{libro.autore}}</td>
<td>{{libro.casaEditrice}}</td>
</tr>
</tbody>
</table>
</div>
And I get the following table:
As you can see the auto increment field {counter} is always 3. I would like to be 1,2,3 for each line of the table without creating another document-field in the collection ('libri'). That cell of the table is not part of the mongo collection.
Is there any suggestion? Why I cannot print the loop in the html table? Thanks
The reason why you are always getting only number 3 is because that is what is being prepared for the view.
When the view gets the data front the controller, the counter is already at the value 3.
What you should do is use $index instead of {{counter}} and it will automatically get the index from the ng-repeat iterating over the array, so you will get 1, 2, 3....
If you wanted to send it with your data from the controller, then you should imbue the index with your array.
Something like this:
var tempArr = [
{name: 'a'},
{name: 'b'},
{name: 'c'}
]
var i = 0;
for (; i < tempArr.length; i++) {
tempArr[i].index = i;
}
console.log('updated array', tempArr);
Just replace tempArr with your data, and it should run smoothly.
You can test this here, just check your console.
Hope this helps :)
Use {{$index}} instead of {{counter}}.

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;
};
}]);

Resources