I'm trying to paginate over a grouped list, but I have the error of circular dependencies.
I'm new about angular, got this code from other answer on SO here, but cannot paginate it.
This is a fiddle: http://jsfiddle.net/Tropicalista/qyb6N/1/
angular.module('test', ['ui.bootstrap']);
function Main($scope, $q) {
$scope.players = [//my data]
// create a deferred object to be resolved later
var teamsDeferred = $q.defer();
// return a promise. The promise says, "I promise that I'll give you your
// data as soon as I have it (which is when I am resolved)".
$scope.teams = teamsDeferred.promise;
// create a list of unique teams
var uniqueTeams = unique($scope.players, 'team');
// resolve the deferred object with the unique teams
// this will trigger an update on the view
teamsDeferred.resolve(uniqueTeams);
// function that takes an array of objects
// and returns an array of unique valued in the object
// array for a given key.
// this really belongs in a service, not the global window scope
function unique(data, key) {
var result = [];
for (var i = 0; i < data.length; i++) {
var value = data[i][key];
if (result.indexOf(value) == -1) {
result.push(value);
}
}
$scope.noOfPages = Math.ceil(result.length / 10);
return result;
}
$scope.currentPage = 1;
$scope.pageSize = 5;
$scope.maxSize = 2;
}
angular.filter('startFrom', function() {
return function(input, start) {
start = +start; //parse to int
return input.slice(start);
}
});
Fixed the code for you. http://jsfiddle.net/93Rd3/
You need to change the order when applying the filters.
<div ng-repeat="team in teams| startFrom:(currentPage - 1)*pageSize | limitTo:pageSize ">
And you'd better check the text passing into the filter if it is undefined:
app.filter('startFrom', function () {
return function (input, start) {
if (input === undefined || input === null || input.length === 0) return [];
start = +start; //parse to int
return input.slice(start);
}
});
Related
I am using ng-repeat to print array data from Firebase. As seen below; foreeach quote I want to count all the votes assosiated with it but any attempt to iterate results in undefined
my Markup
<div ng-repeat="q in quotes" ng-init="total_votes(q)">
{{q.name}}
</div>
my controller
app.controller('account', function ($scope, $timeout, $rootScope, $location, $firebaseObject, $firebaseArray) {
//======== Get Quotes ========//
var ref = firebase.database().ref().child('quotes');
$scope.quotes_obj = $firebaseObject(ref);
$rootScope.quotes = $firebaseArray(ref);
//======== item totals ========//
$scope.total_votes = function(itm) {
// attempt ONE
var itm_votes = $scope.quotes_obj[itm.$id].votes;
console.log(itm_votes); // returns "{-KzFkQYHWKwbAPjIekWz: "sad", -KzLT76T14doKgYbjRc1: "wow"}"
console.log(itm_votes.length); // returns "undefined"
// attempt TWO
console.log(itm.votes) // returns "{-KzFkQYHWKwbAPjIekWz: "sad", -KzLT76T14doKgYbjRc1: "wow"}"
console.log(itm.votes[0]) // returns "undefined"
console.log(itm.votes.length) // returns "undefined"
// count votes
var counts = {};
for (var i = 0; i < itm_votes.length; i++) {
counts[itm_votes[i]] = 1 + (counts[itm_votes[i]] || 0);
if(i == itm_votes.length){
console.log('done');
}
}
return counts;
};
});
Here is a picture of a single quote on my firebase database
Does anyone know why the index is undefined for the data? I am open to new ways to count all the like votes if you see something wrong with this approach
anything helps. Thanks!
I had to $.map to convert the obj to array.
$scope.total_votes = function(itm) {
var itm_votes = $.map($scope.quotes_obj[itm.$id].votes, function(value, index) {
return [value];
});
console.log(itm_votes.length);
var counts = {};
for (var i = 0; i < itm_votes.length; i++) {
counts[itm_votes[i]] = 1 + (counts[itm_votes[i]] || 0);
if(i == itm_votes.length-1){
console.log('done');
console.log(counts);
}
}
return counts;
};
Consider the below Angularjs 'service'. I would like to keep all my 'entries' related variables in this service so I can use them across controllers - as I believe the ideal angular pattern calls for. However, if I manipulate anyone of the variables from a controller - entries, entries_Sorted, entries_Loaded within the service object - they all seem to take on the same new value. I understand the factory object is a singleton but shouldn't these variables be independent? I don't expect or understand the behavior I am seeing. How is this useful? I must be doing something wrong.
To be clear:
If I set local variables within my controllers using this service's return methods, then update those local variables, all the three entries variables within the service will take on the new values.
Service code:
angular.
module('core.entry').
factory('Entry', ['$http', 'Topten', 'Stack', 'User',
function($http, Topten, Stack, User) {
var entries = [];
var entries_Sorted = [];
var entries_Loaded = [];
var service = {};
service.getEntries = function(stackId, callback) {
return $http.get('stacks/' + stackId + '/entries/')
.success(function(data) {
entries = data["entries"];
Topten.setToptens(data["topTen"]);
Stack.setOpenStack(data["stack"]);
callback(null, data);
})
.error(function(err) {
callback(err, null);
});
};
service.returnEntries = function() {
return entries;
}
service.sortEntries = function(callback) {
// 1. Loop through entries inner looping on toptens - adding topten score to total score
for (var i = 0; i < entries.length; i++) {
var thisEntry = entries[i];
var totalScore = 0;
var toptens = Topten.returnToptens();
for (var j = 0; j < toptens.length; j++) {
var thisTopten = toptens[j];
if (thisTopten["entryId"]) {
if (thisEntry["_id"] == thisTopten["entryId"]._id) {
totalScore = totalScore + thisTopten["score"];
}
}
}
thisEntry.totalScore = totalScore;
// 2. Add net (likes - dislikes) to entry.totalScore
for (var j = 0; j < thisEntry.votes.length; j++) {
var thisVote = thisEntry.votes[j]["vote"];
if (thisVote == "up") {
thisEntry["up"] = thisEntry["up"] + 1;
} else if (thisVote == "down") {
thisEntry["down"] = thisEntry["down"] + 1;
}
}
var netLikes = thisEntry["up"] - thisEntry["down"]; // one point each
thisEntry["totalScore"] = thisEntry["totalScore"] + netLikes;
}
// 3. Sort entries by entry.totalScore and return
entries_Sorted = entries.sort(function(a, b) {
return b.totalScore - a.totalScore;
});
callback();
};
service.returnEntries_Sorted = function() {
return entries_Sorted;
};
return service;
}
]);
My controller's code:
Entry.getEntries($routeParams.stackId, function(err, data) {
if(err) {
}
// get sorted entries (after return from getEntries)
Entry.sortEntries(function() {
self.entries_Sorted = Entry.returnEntries_Sorted();
self.loadMore();
});
});
self.loadMore = function() {
self.entries_Loaded = self.entries_Loaded.concat(self.entries_Sorted.splice(page * increment, increment));
self.page +=1;
}
Problem: After I call this local 'load_More' function, the properties in my service - entries, _Sorted, _Loaded - will all have the new 'spliced' value. ie. Entry.entries will have the same value as the controller's local self.entries_Sorted.
I am new for AngularJS and I am trying to access function parameter value inside nested angular for each loop , but that variable gets undefined error. here is my code .
var pieChart = function (_data, _fieldName) {
var data = _data;
var cost_max = 0;
var cost_min = 99999;
angular.forEach(groupBy($scope.api_data, _fieldName), function (obj, index) {
var total = 0;
var name = '';
angular.forEach(obj, function (row, i) {
name = row._fieldName;
total += 1;
})
data.push([name, total]);
if (cost_max < obj.cost) cost_max = obj.cost;
if (cost_min > obj.cost) cost_min = obj.cost;
})
$scope.chart.data = data;
$scope.loaded = 1;
}
row._fieldName is undefined here , what was the issue ? kindly help me.
var groupBy = function (xs, key) {
return xs.reduce(function (rv, x) {
(rv[x[key]] = rv[x[key]] || []).push(x);
return rv;
}, {});
};
In your second angular.forEach loop, you have to replace row._fieldName with row[_fieldName].
angular.forEach(obj, function (row, i) {
name = row[_fieldName];
total += 1;
})
By writing row._fieldName, you try to get the key named _fieldName from object row instead of the real field.
Little JSFiddle
Have some trouble with Angular promise between two loops... First loop walk through an array of value, and for each value, make a PouchDB Query to retrieve some datas. Finally, would like to return to controller a JSON Object that would look like :
{
items: [
{
"attribute": "some value"
},
{
"attribute": "some other value"
},
...
],
"a_total": "some_total",
"another_total": "some_other_total"
}
In this object, "items"
Basically, put the code in a function that looks like :
var _stockByAreas = function(){
var deferred = $q.defer();
var data = {}; // Final datas to return to controller
// Get first array to loop into
var storageAreas = storageAreaService.storageAreaList();
var areas = []; // All of area
// Walk across array
angular.forEach(storageAreas, function(zone){
var area = {}; // First object to return
area.id = zone.id;
area.libelle = zone.libelle;
// Then make a PouchDB query to get all datas that involved
MyKitchenDB.query(function(doc, emit){
emit(doc.storage);
}, { key: area.id, include_docs: true }).then(function (result) {
area.sRef = "tabsController.addTo({id: '" + area.id + "'})";
area.nbProduct = 0;
area.totalQuantity = 0;
area.totalValue = 0;
// ... process result
if(result.rows.length > 0){
// Some results, so... let's go
area.sRef = "tabsController.outFrom({id: '" + area.id + "'})";
var rows = result.rows;
// Counter initialization
var total = 0;
var value = 0;
angular.forEach(rows, function(row){
total++;
var stocks = row.doc.stock;
angular.forEach(stocks, function(stock){
var nearOutOfDate = 0;
var nearStockLimit = 0;
quantity += stock.quantity;
value += stock.quantity * stock.price;
// Evalue la date de péremption
var peremptionDate = moment(stock.until);
var currentDate = moment();
if(currentDate.diff(peremptionDate, 'days') <= 1){
nearOutDate += 1;
}
});
area.nbProduct = total;
area.qteTotale = quantity;
area.valeur = value;
if(quantite == 1){
nearLimitOfStock += 1;
}
areas.push(area); // Add result to main array
});
}
}).catch(function (err) {
// Traite les erreurs éventuelles sur la requête
});
/**
* Hey Buddy... what i have to do here ?
**/
data.items = areas;
data.nearLimitOfStock = nearLimitOfStock;
data.nearOutOfDate = nearOutOfDate;
});
deferred.resolve(data);
return deferred.promise;
}
... But, console returns that "areas" is not defined, and other value too...
I think i don't really understand how promises runs...
Someone is abble to explain why i can't get the result that i expect in my case ?
Thx
Your code is too long, I just give you the approach.
Use $q.all() to ensure all your queries are completed. And use deferred.resolve(data) whenever your data for each query is arrived.
var _stockByAreas = function() {
var query = function(zone) {
var queryDef = $q.defer();
// timeout is for query and response simulations
setTimeout(function() {
// ...
queryDef.resolve( {data: 'MeTe-30'} );
}, 1000);
return queryDef.promise;
}
var promises = [];
angular.forEach(storageAreas, function(zone) {
// ...
promises.push( query(zone) );
});
return $q.all(promises);
}
_stockByAreas().then(function(res) {
// res[0] resolved data by query function for storageAreas[0]
// res[1] resolved data by query function for storageAreas[1]
// ...
});
I use Angular with Firebase and now stuck with error when trying to make a factory to work with filter.
app.factory('itemsFactory', ["$scope", '$rootScope', "$firebase", "simpleLogin",
function($scope, $rootScope, $firebase, simpleLogin) {
var ref = new Firebase("https://------.firebaseio.com/");
var items = $scope.items
$scope.items = [];
var sync = $firebase(ref);
$scope.items = sync.$asArray();
$rootScope.auth = simpleLogin;
return items;
}]);
app.filter('orderObjectBy',['itemsFactory', function (itemsFactory) {
return function (items) {
var filtered = [];
for (var i = 0; i < items.length; i++) {
var item = items[i];
if (item.hot) {
filtered.push(item);
}
};
for (var i = 0; i < items.length; i++) {
var item = items[i];
if (!item.hot) {
filtered.push(item);
}
};
return filtered;
};
}]);
This is the related HTML:
< tr ng-repeat="item in items | orderObjectBy:'hot' track by $index">
Here's what i got in console:
ngRepeat: item in items | orderObjectBy:"hot" track by $index
copeProvider%20%3C-%20%24scope%20%3C-%itemsFactory%20%3C-%orderObjectByFilter at Error (native)
Seems I messed up with dependency injection. But what would be the proper way to do it?
You can take advantage of AngularFire's extensibility to order the list without the need for a directive:
app.factory('HotList', function($firebase) {
function hotComparator(a,b) {
if( a.hot === b.hot ) {
// if both are hot or not, then sort by $id
return strcmp(a.$id, b.$id);
}
else {
// place hot items at the top
return a.hot? -1 : 1;
}
}
function strcmp(str1, str2) {
// http://phpjs.org/functions/strcmp/
return ((str1 == str2) ? 0 : ((str1 > str2) ? 1 : -1));
}
return function(ref) {
var list = $firebase(ref).$asArray();
function resort() {
list.sort(hotComparator);
}
list.$watch(resort);
resort();
return list;
};
});
app.controller('ctrl', function(HotList) {
// automagically sorted by hot/not and $id
$scope.list = HotList(new Firebase(URL));
});
To make your directive work in place, you will want to copy the array and call sort on the copy which will be a great deal simpler.
app.filter('orderObjectBy', function($firebase) {
function hotComparator(a,b) {
if( a.hot === b.hot ) {
// if both are hot or not, then sort by $id
return strcmp(a.$id, b.$id);
}
else {
// place hot items at the top
return a.hot? -1 : 1;
}
}
function strcmp(str1, str2) {
// http://phpjs.org/functions/strcmp/
return ((str1 == str2) ? 0 : ((str1 > str2) ? 1 : -1));
}
return function(items) {
var list = items.slice();
list.sort(hotComparator);
return list;
};
});
And your usage in the view is not quite right, so try it this way:
ng-repeat="item in items track by $id | orderObjectBy:'hot'"