I'll preface this by saying I am very new to AngularJS so forgive me if my mindset is far off base. I am writing a very simple single page reporting app using AngularJS, the meat and potatoes is of course using the angular templating system to generate the reports themselves. I have many many reports that I am converting over from a Jinja-like syntax and I'm having a hard time replicating any kind of counter or running tabulation functionality.
Ex.
{% set count = 1 %}
{% for i in p %}
{{ count }}
{% set count = count + 1 %}
{% endfor %}
In my controller I have defined a variable like $scope.total = 0; which I am then able to access inside of the template without issue. What I can't quite figure out is how to increment this total from within an ng-repeat element. I would imagine this would look something like -
<ul>
<li ng-repeat="foo in bar">
{{ foo.baz }} - {{ total = total + foo.baz }}
</li>
</ul>
<div> {{ total }} </div>
This obviously doesn't work, nor does something like {{ total + foo.baz}}, thanks in advance for any advice.
If all you want is a counter (as per your first code example), take a look at $index which contains the current (0 based) index within the containing ngRepeat. And then just display the array length for the total.
<ul>
<li ng-repeat="item in items">
Item number: {{$index + 1}}
</li>
</ul>
<div>{{items.length}} Items</div>
If you want a total of a particular field in your repeated items, say price, you could do this with a filter, as follows.
<ul>
<li ng-repeat="item in items">
Price: {{item.price}}
</li>
</ul>
<div>Total Price: {{items | totalPrice}}</div>
And the filter function:
app.filter("totalPrice", function() {
return function(items) {
var total = 0, i = 0;
for (i = 0; i < items.length; i++) total += items[i].price;
return total;
}
});
Or, for improved reusability, a generic total filter function:
app.filter("total", function() {
return function(items, field) {
var total = 0, i = 0;
for (i = 0; i < items.length; i++) total += items[i][field];
return total;
}
});
Which would be used like:
<div>Total price: {{items | total:'price'}}</div>
I needed running total rather that plain total, so I've added upon what #TimStewart left. Here the code:
app.filter("runningTotal", function () {
return function(items, field, index) {
var total = 0, i = 0;
for (i = 0; i < index+1; i++) {
total += items[i][field];
}
return total;
};
});
To use it in column you just do:
<div>Total price: {{items | runningTotal:'price':$index}}</div>
I'm not sure I totally understand the question, but are just needing to display the total number in the object you're iterating over? Just set $scope.total to the length of your array (bar in your example above). So, $scope.total = $scope.bar.length;
If you're wanting the total of all the foo.baz properties, you just need to calculate that in your controller.
$scope.total = 0;
angular.forEach($scope.bar, function(foo) {
$scope.total += foo.baz;
});
Related
I was playing with Angular Js when I came across following issue
<li ng-repeat="x in capitals | orderBy:'capital'">
{{ x.country|uppercase}} : {{ x.capital|uppercase}}
</li>
works but
<li ng-repeat="x in capitals | orderBy:'capital'">
{{ x.country|uppercase+ ":" + x.capital|uppercase}}
</li>
dose't work
Is there any work around?
That wont work as you cant use addition operator inside expression. you can write your custom filter and use that. check the below fiddle.
jsfiddle.net/7nzot7jh/
Found the Answer
This is my custom filter
app.filter('alterCap',function()
{
return function(x)
{
var i, c, txt = "";
for (i = 0; i < x.length; i++) {
c = x[i];
if (i % 2 == 0) {
c = c.toUpperCase();
}
txt += c;
}
return txt;
};
});
And in my controller I have passed
app.controller("myCtrl",function($scope,$filter){
$scope.capitals = [
{country:'India',capital:'Delhi'},
{country:'Sri Lanka',capital:'Colombo'},
{country:'Afganistan',capital:'Kabul'},
{country:'Bhutan',capital:'Norway'},
{country:'Nepal',capital:'Thimphu'},
{country:'Japan',capital:'Tokyo'},
{country:'China',capital:'Beijing'},
{country:'Russia',capital:'Moscow'},
{country:'USA',capital:'Washington, D.C.'}
];
$scope.applyAlterCap = function(movie){
return $filter('alterCap')(movie);
};
});
Now I can use Filter in my expression as
<p>Using Custom Filter with addition operator inside expression</p>
<ul>
<li ng-repeat="x in capitals | orderBy:'capital'">
{{ applyAlterCap(x.country) + ":" + x.capital }}
</li>
</ul>
{{ x.country + ":" + x.capital}}
I want want to display and count only batter in batters at index 0.
i.e id = 1001 type = regular
and count should be "4"
Maybe this updated JSFiddle will give you an idea.
HTML
<div ng-app="myApp" ng-controller="customersCtrl">
<ul ng-repeat="x in myData">
<li ng-repeat = "batter in x.batters.batter | filter: {'id': 1001, 'type': 'Regular'}">
{{ batter.id }} - {{ batter.type }}
</li>
</ul>
{{countBatters(1001, 'Regular')}}
</div>
Added function in controller:
$scope.countBatters = function(id, type) {
return $scope.myData.filter(function(obj) {
var batter = obj.batters.batter[0];
return batter.id == id && batter.type == type
}).length;
}
Use array filter method..
var getBatter1001 = (item) => { return item.batters.batter.filter((x) => {x.id === '1001'}); }
var batter1001 = $scope.myData.filter(getBatter1001);
console.log('Count of objects with batter id 1001: '+batter1001.length); // Logs 3
I am trying to have 4 boxes always show up. The boxes are filled by an image,but if there is no image available I want the boxes to be gray. The images variable holds an unknown amount of images, it could be 2 it could be 30.
<div class="container">
<div class="picture" ng-repeat="image in images | limitTo: 4"></div>
// PSEUDO CODE BELOW
<div class="empty" ng-repeat="i in 4 - images.length"></div>
// PSEUDO CODE ABOVE
</div>
"4 - images.length" is pseudo code, This is what I want to achieve, thinking if I only have 3 images available the result will be 1 gray box. But this syntax obviously does not work since ng-repeat require a collection.
Which made me try to provide it said collection through a function:
$scope.getNumber = function(num) {
return new Array(num);
}
and use in the following way:
<div class="empty" ng-repeat="n in getNumber(4 - cookbook.images.length)"></div>
But with no success.
<div class="picture" ng-repeat="image in images | limitTo: 4">hi</div>
<div class="empty" ng-repeat="n in [] | range:images.length:4">hey</div>
I've created a custom filter for it:
app.filter('range', function() {
return function(input, min, max) {
min = parseInt(min);
max = parseInt(max);
for (var i=min; i<max; i++) {
input.push(i);
}
return input;
};
});
DEMO
Possible repeat question
By using this updated syntax you can iterate with a specific index defined
HTML
<div ng-app="myapp">
<div ng-controller="ctrlParent">
<ul>
<li ng-repeat="i in getNumber(4) track by $index"><span>{{$index+1}}</span></li>
</ul>
</div>
Controllers
var app = angular.module('myapp',[]);
app.controller('ctrlParent',function($scope){
$scope.getNumber = function(num) {
return new Array(num);
}
});
Way to ng-repeat defined number of times instead of repeating over array?
I would probably handle this in whatever populates $scope.images. For example, something like:
$http.get('api/some/images?skip=10&take=4')
.success(function(response){
$scope.images = response;
for(var i = $scope.images.length; i<4; i++){
$scope.images.push('empty');
}
});
Then my template would just handle that:
<div class="container>
<div ng-repeat="image in images" ng-class="{empty:image=='empty',picture:image!='empty'}"></div>
</div>
wouldnt be easier to make operation modulo in controller?
function equal_number_of_images(){
for(var i = 0, add = 4 - $scope.some_files%4; i < add; i++ ){
$scope.some_files.push('');
}}
please help to fix the script.
http://jsfiddle.net/rrnJc/
this is a dynamic list of news. you can scroll through.
the problem that the logic is in the template:
<li ng-repeat="item in news" ng-show="$index >= currentPosition && $index <= (currentPosition + qntVisibleRecords)">
this is not right.
I would like to chtby visible part of the list of news was in the controller (in the function $scope.newsVisible). at the same conclusion in the template design and deliver this:
<li ng-repeat="item in newsVisible">
<span class="date">{{item.date}}</span>
<span class="title">{{item.title}} - {{$index}}</span>
</li>
Just slice your array of news when controller is initialized and each time you call the changeCurrent function and iterate through newsVisible. Possible code could be :
$scope.changeCurrent = function(value){
$scope.currentPosition = $scope.currentPosition + value;
if($scope.currentPosition < 0 ){
$scope.currentPosition = 0;
}
var end = Math.min($scope.currentPosition + $scope.qntVisibleRecords, $scope.news.length)
//ensure that you won t slice at an index greater than news.length
$scope.newsVisible = $scope.news.slice($scope.currentPosition, end);
}
BTW you can call this function when the controller initialized in order to init your array of newsVisible.
$scope.changeCurrent($scope.currentPosition);
You can check it here http://jsfiddle.net/rrnJc/1/
I've changed the meteor example leaderboard into a voting app. I have some documents with an array and in this array there are 6 values. The sum of this 6 values works fine, but not updating and showing the values in my app.
The values are only updating, if I click on them. The problem is, that I get the booknames (it's a voting app for books) from the "selected_books" variable (previously selected_players), but I don't know how I can get the book names.
By the way: _id are the book names.
I will give you some code snippets and hope, somebody have a solution.
This is a document from my database:
{
_id: "A Dance With Dragons: Part 1",
isbn: 9780007466061,
flag: 20130901,
score20130714: [1,2,3,4,5,0],
}
parts of my html file:
<template name="voting">
...
<div class="span5">
{{#each books}}
{{> book}}
{{/each}}
</div>
...
</template>
<template name="book">
<div class="book {{selected}}">
<span class="name">{{_id}}</span>
<span class="totalscore">{{totalscore}}</span>
</div>
</template>
and parts of my Javascript file:
Template.voting.books = function () {
var total = 0;
var book = Session.get("selected_book");
Books.find({_id:book}).map(function(doc) {
for (i=0; i<6; i++) {
total += parseInt(doc.score20130714[i], 10);
}
});
Books.update({_id:book}, {$set: {totalscore: total}});
return Books.find({flag: 20130901}, {sort: {totalscore: -1, _id: 1}});
};
Thanks in advance
Don't update data in the helper where you fetch it! Use a second helper for aggregating information or a transform for modifying data items. Example:
Template.voting.books = function() {
return Books.find({}, {sort: {totalscore: -1, _id: 1}});
};
Template.books.totalscore = function() {
var total = 0;
for(var i=0; i<6; i++) {
total += this.score[i];
}
return total;
};
As a side note, DO NOT USE the construct for (i=0; i<6; i++), it's deadly. Always declare your index variables: for (var i=0; i<6; i++).