I have a drop down which has options of price range (generating dynamically depending upon the prices of cars in my model). On selecting a price-range, let's say '3000 to 4000', I want cars belonging to those price range to appear.
Here's my change event listener function controller:
$('#priceRange').change(function(){
$scope.init(); //init updates cars[] with all the 20 objects
var min, max;
var index = $('#priceRange :selected').index();
/*algo to find min and max range*/
if(index == 0){
min = $scope.minRange;
max = $scope.maxRange;
}else{
min = (index-1)*1000;
if(index*1000 == $scope.maxRange){
max = index*1000;
}else{
max = index*1000-1;
}
}
//For 3000 to 4000, I am passing min = 3000, max = 4000 and all the cars which then priceFilter function updates $scope.cars with cars belonging to that range
$scope.cars = $scope.priceFilter($scope.cars, min, max);
});
$scope.priceFilter = function(items, min, max) {
var filtered = [];
angular.forEach(items, function(item, key) {
if(item.price <= max && item.price >= min){
filtered.push(item);
}
});
return filtered;
};
But these updated $scope.cars doesn't reflect in my view as I am expecting. Here's the markup:
<label>Make: </label> <input ng-model="search.make">
<select id="priceRange"><option>All</option></select>
<div class="col-md-3" ng-repeat="car in cars | filter:search">
<img class="carImg" src="{{car.imgPath}}" alt="{{car.make}}">
<div class="col-sm-8">
<h4>{{car.make}} {{car.model}}</h4>
<h4>{{car.year}}, {{car.price | currency}}</h4>
</div>
</div>
As the change is handled via jQuery, Angular has no notion of it, unless you wake up Angular explicitly by calling $apply():
$scope.cars = $scope.priceFilter($scope.cars, min, max);
$scope.$apply();
See also my Fiddle: http://jsfiddle.net/masa671/hs9vu0r2/
I agree with #dfsq that, with Angular, jQuery is usually not the best way for handling this type of functionality.
Related
I need to have a list of numbers display with the total. When someone enters a new number into an input box, the number will be added to the list displayed and the total will change accordingly.
using angular.js...
While itamar's answer works without any doubt, you can write this like this (a bit cleaner in my opinion) :
Controller
Use reduce for total computation :
$scope.numbers = [1,2,3];
$scope.updateTotal = function () {
$scope.total = numbers.reduce(function (total, number) {
if (number) {
total += number;
}
return total;
}, 0);
};
Markup
<span>{{ total }}</span>
<ul>
<li ng-repeat="number in numbers">
<input type="number" ng-model="number" ng-change="updateTotal()">
</li>
</ul>
Since you know when the total value should get updated, better use ng-change here so the computation is only done when needed.
On a side note, you can use type="number" in the input so only numbers can be entered.
Welcome to StackOverflow - next time please provide some code you wrote yourself. In the meantime - I wrote yours for you :-)
JS in your controller:
$scope.numbers = [1,2,3];
$scope.getTotals = function(){
var total = 0;
total = $scope.numbers.reduce(function(prev, curr) {
return prev + curr;
});
return total;
}
HTML:
<ul>
<li ng-repeat="number in numbers"><input ng-model="number"></li>
<li>{{getTotals()}}</li>
</ul>
I want to calculate the total sum, of a dynamic number of variables within angular's brace syntax.
The for loop I would normally use would look like :
var total = 0;
for(x=0; x< objects.length; x++) {
total += objects[x].num;
}
I would like to recreate this as {{ total }} or {{total()}} and have the total update when ever one of the variables is changed.
Here is what you are looking for: JSFiddle
HTML
<div ng-app="myApp" ng-controller="ctrl" ng-strict-di>
{{total()}}<br/>
<input type="text" ng-model="item.num" ng-repeat="item in objects"/>
</div>
JS:
angular.module('myApp',[]).controller('ctrl',function($scope) {
angular.extend($scope,{
objects:[
{num:7},
{num:8},
{num:9}
],
total : function(){
var res = 0;
for (var i=0;i<$scope.objects.length;i++)
res+=parseInt($scope.objects[i].num);
return res;
}
});
});
UPDATE
To test if it's updating the total on object's num update play with code like this:
setInterval(function() {
$scope.objects[1].num++;
$scope.$apply();
},500); //increase num of 2nd object by 1, every half of second
To test if it's still updating when adding new objects, play with code like that:
setInterval(function() {
$scope.objects.push({num:Math.floor(Math.random()*100)});
},5000); //Add new object, with random num: value, every 5 seconds
I've got a list of events on a site I'm building and I would like the past events to be removed automatically. I've written a filter that is doing this, except it is removing today's events as well. I'm trying to use Angular Moment. Here is my code:
angular.module('zenCityApp')
.filter('filterPastDates', function (moment) {
return function (events) {
var filterByDate = [];
for (var i = 0; i < events.length; i++) {
var currentDate = new Date();
if(moment(currentDate).isBefore(events[i].date, 'hour')) {
console.log("we're in!");
filterByDate.push(events[i]);
console.log(filterByDate);
}
}
return filterByDate;
};
});
And here is the markup:
div ng-repeat="event in events | limitTo:100 | filter:tfilter | orderBy: 'date' | filterPastDates">
<div class="row">
<div class="col-md-6">
<h4>{{event.date | amDateFormat:'MMMM Do'}}</h4>
</div>
<div class="col-md-6">
<h4>{{event.name}}</h4>
</div>
</div>
Any help would be greatly appreciated.
What would be better is to create a cut-off moment for which you want to test. Moments api gives you a pretty easy way to do it by using .startOf('day'). That will give you a moment that represents today at 12:00am (the first second in the day). But since you also want to include that value in your filter, you can then subtract 1 millisecond from the value.
var cutOffDate = moment().startOf('day').subtract(1,'millisecond');
And now you can easily use that in your filter. Notice that I created that object outside of the loop (since it's not supposed to change), and I created it as a moment directly instead of creating a new moment object each time in the loop.
app.filter('filterPastDates', function () {
return function (events) {
if (events && events.length) {
var filtered = [];
var cutOffDate = moment().startOf('day').subtract(1,'millisecond');
for (var i = 0; i < events.length; i++) {
var evt = events[i];
if (cutOffDate.isBefore(evt.date)) {
filtered.push(evt);
}
}
return filtered;
} else {
return events;
}
};
});
Here's a sample plunker: http://plnkr.co/edit/kSXu0Z3J7zoyBMBjoE84?p=preview
I have this data : http://www.monde-du-rat.fr/API/moulinette/radio/posts.json , from cakephp app, it's song by song, with attached votes
I use it as a service into a angularjs app, and i displayed it like this in html :
<div ng-repeat="song in songs | orderBy:'Radio.idSong' | notesong:'Radiovote'" class="list-group-item" id="{{song.Radio.idSong}}" ng-class="{ 'active' : songPlayed_name == song.Radio.name }" ng-if="songs">
<span>{{song.Radio.idSong}} - {{song.Radio.title}}</span><br />
<span>{{note}}%</span>
</div>
So, i want to count each attached vote, and define with values 'good' or 'bad', the % of likes
I try to made this filter :
/* notesong */
app.filter('notesong', function() {
return function(input) {
// counter init
var countGood = 0;
// if there is no votes, so note is zero
if (!angular.isArray(input)) {
var note = 0;
} else {
// loop for each vote (from Radiovote array, each value)
angular.forEach(input, function () {
if (input.value == 'good') {
countGood = countGood + 1;
}
});
var note = (countGood * input.length) / 100;
}
// final return
return note;
};
});
It's not working apparently (no errors, and no data displayed), so, what is the correct way ?
You are applying the filter in the wrong place. Instead of using it on the ng-repeat you should use it on the property you want to bind, like this:
<div ng-repeat="song in songs | orderBy:'Radio.idSong'" class="list-group-item" id="{{song.Radio.idSong}}" ng-class="{ 'active' : songPlayed_name == song.Radio.name }" ng-if="songs">
<span>{{song.Radio.idSong}} - {{song.Radio.title}}</span><br />
<span>{{song.Radiovote | notesong}}%</span>
</div>
There's also a problem with the way you are looping the votes in your filter. Update the following lines:
// loop for each vote (from Radiovote array, each value)
angular.forEach(input, function (item) {
if (item.value == 'good') {
countGood = countGood + 1;
}
});
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;
};
}]);