Error in angular js controller for pagination - javascript

I am coding in angular js for client side pagination.
Starting in other question here about pagination, I've transformed the code to this but doesn't work.
What is wrong?
myapp.controller('MainCtrl', function($scope, Events) {
$scope.mydata = [];
Events.query(function(data) {
$scope.mydata = data;
});
$scope.get = function (offset, limit) {
return $scope.mydata.slice( offset, offset+limit );
};
$scope.count = function () {
return $scope.mydata.length;
};
$scope.numPerPage = 5;
$scope.noOfPages = Math.ceil($scope.count / $scope.numPerPage);
$scope.currentPage = 1;
$scope.setPage = function () {
$scope.data = $scope.get( ($scope.currentPage - 1) * $scope.numPerPage, $scope.numPerPage );
};
$scope.$watch( 'currentPage', $scope.setPage );
});
$scope.get and $scope.count don't work. Events.query is getting all JSON data from service in array format.
I can test this code in chrome angular js extension.
Any idea?

Use {{mydata.length}} in your html instead of the $scope.count function.

Related

How to show my top posts first in my Infinite Scroll, in descending order with firebase?

WHAT I TRIED (DOES NOT WORK CORRECTLY):
CODE:
<script>
var app = angular.module('app', ['firebase']);
app.controller('ctrl', function ($scope, $firebaseArray, $timeout) {
$scope.data = [];
var _n = Math.ceil(($(window).height() - 50) / (350)) + 1;
var start = 0;
var end = _n - 1;
var lastScore = <%=lastScore%>;
console.log("FIRST FIRST FIRST LAST SCORE:" + lastScore);
var firstElementsLoaded = false;
$scope.getDataset = function() {
fb.orderByChild('score').endAt(lastScore).limitToLast(_n).on("child_added", function(dataSnapshot) {
lastScore = dataSnapshot.child("score").val() - 1;
console.log("LAST TOP LIKED:"+ lastScore);
$scope.data.push(dataSnapshot.val());
$scope.$apply();
console.log("THE VALUE:"+$scope.data);
$scope.data.splice(start, end).concat($scope.data.reverse());
$scope.$apply();
start = start + _n;
end = end + _n
firstElementsLoaded = true;
});
};
$scope.getDataset();
window.addEventListener('scroll', function() {
if (firstElementsLoaded == true) {
if (window.scrollY === document.body.scrollHeight - window.innerHeight) {
$scope.$apply($scope.getDataset());
}
}
});
});
// Compile the whole <body> with the angular module named "app"
angular.bootstrap(document.body, ['app']);
QUESTION:
How do I revert the data client-side to get my posts from top to bottom according to their score (from highest to lowest)?
WHAT I WOULD LIKE TO ACHIEVE:
Get my posts in descending order according to score which is a bit trickier with the infinite scroll.
Since the posts are displayed in reverse order, you should reverse the order of "pages"(use endAt instead of startAt), and sort posts reversely in every page.
See also this answer
Example
Setup:
$scope.data = [];
var n = Math.ceil(($(window).height() - 50) / (350)) + 1;
var firstElementsLoaded = false;
var lastScore = MAX_SCORE, lastKey
Function for scroll event listener:
$scope.getDataset = function() {
fb.orderByChild('score')
.endAt(lastScore, lastKey)
//endAt is inclusive, take one more for the n-th
.limitToLast(n + firstElementsLoaded)
.once('value', loadPosts)
function loadPosts(snapshot) {
var posts = snapshot.val()
function compare(a, b) {
if (posts[a].score != posts[b].score) {
return b.score - a.score
}
if (a < b) return 1
if (a > b) return -1
return 0
}
//skip the post included by endAt
var ordered = Object.keys(posts).sort(compare).slice(firstElementsLoaded)
lastKey = ordered[ordered.length-1]
lastScore = posts[lastKey].score
ordered.forEach(function(key) {
$scope.data.push(posts[key])
})
$scope.$apply();
firstElementsLoaded = true;
}
}
For a more elegant way, you may try store scores reversely, or use an additional value.
fb.orderByChild('reversedScore')
I ended up using an inversedScore property:
<script>
var app = angular.module('app', ['firebase']);
app.controller('ctrl', function ($scope, $firebaseArray, $timeout) {
$scope.data = [];
var _n = Math.ceil(($(window).height() - 50) / (350)) + 1;
var firstElementsLoaded = false;
var lastScore = -100000;
$scope.getDataset = function() {
fb.orderByChild('inversedScore').startAt(lastScore).limitToFirst(_n).on("child_added", function(dataSnapshot) {
lastScore = dataSnapshot.child("inversedScore").val() + 1;
console.log("LAST LIKE SCORE:"+ lastScore);
$scope.data.push(dataSnapshot.val());
$scope.$apply();
console.log("THE VALUE:"+$scope.data);
firstElementsLoaded = true;
});
};
$scope.getDataset();
window.addEventListener('scroll', function() {
if (firstElementsLoaded == true) {
if (window.scrollY === document.body.scrollHeight - window.innerHeight) {
$scope.$apply($scope.getDataset());
}
}
});
});
// Compile the whole <body> with the angular module named "app"
angular.bootstrap(document.body, ['app']);
</script>

How can I call a function in an angular controller?

I have a angularJS file like this; I need to call the newGame function in the end of the controller not in html. Can anyone help me with this?
var app = angular.module("myApp", []);
app.controller("myCtrl", function ($scope) {
$scope.newGame = function () {
$scope.random = Math.floor(Math.random() * 100) + 1;
$scope.display = "Start guessing";
};
$scope.giveUp = function () {
$scope.display = $scope.random;
};
$scope.check = function () {
if ($scope.guess > $scope.random) {
$scope.display = "Guess Higher"
} else if ($scope.guess < $scope.random) {
$scope.display = "Guess Lower"
} else {
$scope.display = "You got it!"
}
};
});
It's a function just like any other function. Add $scope.newGame(); at the end of your controller.

$Watch is not working until a event is fired

I am using $watch for pagination of the data in my page.But It is not showing data till i click on any of the buttons
Here is the code.
.controller('AppCtrl', function ($scope, $modal, Faq) {
$scope.filteredFaqData = [];
$scope.currentPage = 1;
$scope.numPerPage = 5;
$scope.maxSize = 5;
$scope.faqData = [];
$scope.faqData = Faq.getFaqs();
$scope.$watch('currentPage + numPerPage', function () {
var begin = (($scope.currentPage - 1) * $scope.numPerPage)
, end = begin + $scope.numPerPage;
$scope.filteredFaqData = $scope.faqData.slice(begin, end);
});
})
I am getting the data in the $scope.faqData from the service.But the $scope.filteredFaqData is empty till I click on the paging tabs
Maybe this will do the trick:
.controller('AppCtrl', function ($scope, $modal, Faq) {
//create function that can be called on several places
var updateFilteredData = function () {
var begin = (($scope.currentPage - 1) * $scope.numPerPage),
end = begin + $scope.numPerPage;
$scope.filteredFaqData = $scope.faqData.slice(begin, end);
};
$scope.filteredFaqData = [];
$scope.currentPage = 1;
$scope.numPerPage = 5;
$scope.maxSize = 5;
$scope.faqData = Faq.getFaqs();
$scope.$watchGroup(['currentPage', 'numPerPage'], function () {
//call on update
updateFilteredData();
});
//initial call
updateFilteredData();
});
Note: Better use watchGroup as it is angulars way to do what you want (documentation).
Actually my function Faq.getFaqs() was executing asynchronously . when the page loads for the first time, there was no data to display. So i used $timeout to dilay the $watch and it is working finr now.
Here is the code
$scope.filteredFaqData = [];
$scope.currentPage = 1;
$scope.numPerPage = 5;
$scope.maxSize = 5;
$scope.faqData = [];
$scope.faqData = Faq.getFaqs();
$timeout(function lateDisplay(){
$scope.$watchGroup(['currentPage','numPerPage'], function (newValues, oldValues, scope) {
var begin = ((newValues[0] - 1) * newValues[1])
, end = begin + newValues[1];
$scope.filteredFaqData = $scope.faqData.slice(begin, end);
});
},100);

Ionic infinite scroll

I am using wordpress as a backend for an app and I want to use infinite scroll but I am having trouble concatenating articles.
I am calling the service using a factory:
.factory('Worlds', function ($http) {
var worlds = [];
storageKey = "worlds";
function _getCache() {
var cache = localStorage.getItem(storageKey );
if (cache)
worlds = angular.fromJson(cache);
}
return {
all: function () {
return $http.get("http://www.examplesite.com/tna_wp/wp-json/posts?filter[category_name]=international&filter[posts_per_page]=10").then(function (response) {
worlds = response.data;
console.log(response.data);
return worlds;
});
},
GetNewPosts: function () {
return $http.get("http://www.examplesite.com/tna_wp/wp-json/posts?filter[category_name]=international&filter[posts_per_page]=2").then(function (response) {
worlds = response.data;
console.log(response.data);
return worlds;
});
},
get: function (worldId) {
if (!worlds.length)
_getCache();
for (var i = 0; i < worlds.length; i++) {
if (parseInt(worlds[i].ID) === parseInt(worldId)) {
return worlds[i];
}
}
return null;
}
}
})
and my controller looks like this:
.controller('WorldCtrl', function ($scope, $stateParams, $timeout, _, Worlds) {
$scope.worlds = [];
Worlds.all().then(function (data){
$scope.worlds = data;
window.localStorage.setItem("worlds", JSON.stringify(data));
},
function (err) {
if(window.localStorage.getItem("worlds") !== undefined) {
$scope.worlds = JSON.parse(window.localStorage.getItem("worlds"));
}
}
);
$scope.loadMore = function() {
Worlds.GetNewPosts().then(function (worlds){
var loadedIdss = _.pluck($scope.worlds, 'id');
var newItemss = _.reject(worlds, function (item){
return _.contains(loadedIdss, item.id);
});
$scope.worlds = newItemss.concat($scope.worlds);
$scope.$broadcast('scroll.infiniteScrollComplete');
});
};
})
I am trying to use underscore to ignore the posts that are already loaded, however when i try the infinite scroll it just goes into a loop calling more posts but doesnt add them to my ng-repeat and ionicLoading renders the app useless.
ion-infinite-scroll works with some sort of paginated result and you seem to feed your list with all the results.
Your API should look something like this:
http://www.examplesite.com/tna_wp/wp-json/posts?filter[category_name]=international&filter[posts_per_page]=2&filter[page]=1
notice I've added a page filter.
and your service responsible to fetch the data should look something like this:
.factory('dataService', function($http) {
return {
GetPosts: function(page, pageSize) {
return $http.get("http://mywebservice/api/test/posts/" + page + "/" + pageSize);
}
};
});
Your controller
.controller('mainController', function($scope, dataService) {
$scope.posts = [];
$scope.theEnd = false;
var page = 0;
var pageSize = 10;
$scope.$on('$stateChangeSuccess', function() {
$scope.loadMore();
});
$scope.loadMore = function(argument) {
page++;
dataService.GetPosts(page, pageSize)
.then(function(result) {
console.log('items fetched: ' + result.data.length);
if (result.data.length > 0) {
angular.forEach(result.data, function(value, key) {
$scope.posts.push(value);
});
}
else {
$scope.theEnd = true;
}
})
.finally(function() {
$scope.$broadcast("scroll.infiniteScrollComplete");
});
};
})
would initialize an array of objetct - as you're doing - and a boolean which tells the directive ion-infinite-scroll when you've reached the end:
$scope.posts = [];
$scope.theEnd = false;
Then you can have some variables to control the pagination:
var page = 0;
var pageSize = 10;
I start loading when the view is loaded:
$scope.$on('$stateChangeSuccess', function() {
$scope.loadMore();
});
$scope.loadMore then would increment the page number:
page++;
and call the service layer:
dataService.GetPosts(page, pageSize)
when I've reached the end of the stream I would set the variable:
$scope.theEnd = true;
to let the directive know we don't have other items to append.
.finally(function() {
$scope.$broadcast("scroll.infiniteScrollComplete");
});
finally is always called when the promise is resolved.
Instead of ng-repeat you can use collection-repeat which should be much faster.
You can play with it here.
Try this create a function infiniteScrollCompleteCancelLoadMore and call it when you want to complete the scroll and you have reached the end of your list.
function infiniteScrollCompleteCancelLoadMore() {
$timeout(function () {
$timeout(function () {
$scope.$broadcast('scroll.infiniteScrollComplete');
$rootScope.canLoad = false;
});
});
}
$scope.loadMore = function() {
Worlds.GetNewPosts().then(function (worlds){
var loadedIdss = _.pluck($scope.worlds, 'id');
var newItemss = _.reject(worlds, function (item){
return _.contains(loadedIdss, item.id);
});
$scope.worlds = newItemss.concat($scope.worlds);
infiniteScrollCompleteCancelLoadMore() // CHANGE HERE
});
};
and your HTML
<ion-infinite-scroll on-infinite="loadMore()" ng-if="canLoad" distance="1%"
immediate-check="false"></ion-infinite-scroll>
OR call This is you just want to cancel loadMore loop.
function infiniteScrollComplete() {
$timeout(function () {
$timeout(function () {
$scope.$broadcast('scroll.infiniteScrollComplete');
});
});
}

Implementing a delay ($timeout) to a $http request with AngularJS

I have a particular function dependant on a (previously executed) service that will trigger multiple queries to a 3rd party API. This API with cancel the requests if it gets too many of them too quick. Is there a way to set a delay to the $http query?
Here is my $http code:
$scope.getImages = function (title, name) {
$http.get('http://ws.audioscrobbler.com/2.0/?method=album.getinfo&api_key=e8aefa857fc74255570c1ee62b01cdba&artist=' + name + '&album='+ title +'&format=json').
success(function(data4) {
$scope.images = data4;
});
}
EDIT Upon request I paste my whole JS (probably I should have done so from the start)
angular.module('myApp', ['ngResource'])
function Ctrl($scope, $http) {
var search = function(name) {
if (name) {
$http.get('http://api.discogs.com/database/search?type=artist&q='+ name +'&page=1&per_page=5').
success(function(data3) {
$scope.clicked = false;
$scope.results = data3.results;
});
}
$scope.reset = function () {
$scope.sliding = false;
$scope.name = undefined;
}
}
$scope.$watch('name', search, true);
$scope.getDetails = function (id) {
$http.get('http://api.discogs.com/artists/' + id).
success(function(data) {
$scope.artist = data;
});
$http.get('http://api.discogs.com/artists/' + id + '/releases?page=1&per_page=10').
success(function(data2) {
$scope.releases = data2.releases;
});
$scope.clicked = true;
$scope.sliding = true;
}
$scope.getImages = function (title, name) {
$http.get('http://ws.audioscrobbler.com/2.0/?method=album.getinfo&api_key=e8aefa857fc74255570c1ee62b01cdba&artist=' + name + '&album='+ title +'&format=json').
success(function(data4) {
$scope.images = data4;
});
}
};
I assume you will want to debounce your calls somehow, something simple like this should work. It will make the call every 300ms unless search(name) is called again before it fires.
var debounce = null;
var search = function(name) {
$timeout.cancel(debounce);
if (name) {
debounce = $timeout(function() {
$http.get('http://api.discogs.com/database/search?type=artist&q='+ name +'&page=1&per_page=5')
.success(function(data3) {
$scope.clicked = false;
$scope.results = data3.results;
});
},300);
}
$scope.reset = function () {
$scope.sliding = false;
$scope.name = undefined;
}
}

Categories

Resources