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');
});
});
}
Related
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.
I'm having a hard time to get promises to work with the right this scope inside prototypes.
Here is my code:
'use strict';
angular.module('testApp').factory('UrlSearchApi',
function($resource, URL_SEARCH_API, PAGE_SIZE, $q){
var resource = $resource(URL_SEARCH_API);
resource.Scroll = function () {
return this.reset();
};
resource.Scroll.prototype.reset = function () {
this.visibleItems = [];
this.allItems = [];
this.busy = null;
return this;
};
resource.Scroll.prototype.fetch = function(query){
var params = {};
if(query) { params.q = query; }
return resource.query(params).$promise;
};
resource.Scroll.prototype.loadAllItems = function (results) {
var d = $q.defer();
angular.forEach(results, function (result, i) {
this.allItems.push(result);
if(i === results.length - 1 ) { d.resolve(); }
}, this);
return d.promise;
};
resource.Scroll.prototype.loadVisibleItems = function () {
var length = this.visibleItems.length,
offset = parseInt(length / PAGE_SIZE),
start = PAGE_SIZE * offset,
end = start + PAGE_SIZE,
subset = this.allItems.slice(start, end),
d = $q.defer();
angular.forEach(subset, function (item, i) {
this.visibleItems.push(item);
if(i === subset.length - 1 ) { d.resolve(); }
}, this);
return d.promise;
};
resource.Scroll.prototype.nextPage = function (query) {
if(this.busy) { return; }
console.log('nextPage ', query);
var tasks = [],
that = this;
if(!this.allItems.length) {
this.reset();
this.busy = true;
return this.fetch(query)
.then(this.loadAllItems)
.then(this.loadVisibleItems)
.finally(function () {
this.busy = false;
});
} else {
this.busy = true;
return this.loadVisibleItems().finally(function () {
this.busy = false;
});
}
};
return resource;
});
Whenever I run the tests I get
describe('#nextPage', function () {
var scroll;
describe('when there is NO search term (show all)', function () {
beforeEach(function (done) {
scroll = new UrlSearchApi.Scroll();
$httpBackend.expectGET('/policy/search')
.respond(200, arrayGenerator(123));
scroll.nextPage().then(done);
$httpBackend.flush();
$rootScope.$apply();
});
it('should load all the items in all items variable', function () {
expect(scroll.allItems.length).toBe(123);
});
});
});
I get the following error:
TypeError: 'undefined' is not an object (evaluating 'this.allItems')
I now that $q in strict mode sets the this inside then to undefined. I tried using bind(this) in multiple places but not luck... Any ideas?
I've already answered a question like this here.
Just let me know in comments if you still have questions.
Upd. Try to update your resource.Scroll.prototype.nextPage method like this:
if(!this.allItems.length) {
this.reset();
this.busy = true;
return this.fetch(query)
.then(this.loadAllItems.bind(this)) //bind here
.then(this.loadVisibleItems.bind(this)) // here
.finally(function () {
this.busy = false;
}.bind(this)); //and here
But keep in mind - when you pass a function as a callback to a then or to forEach e.t.c it'll lose this context. So, use bind exactly when you pass the function which uses this syntax as a callback.
In the following code, on autorefresh the timer starts when all the queries are sent, but I want the timer to start when all the ajax queries are completed.
How can I go about it?
$scope.autoRefresh = function(){
$scope.running = !$scope.running;
$scope.timeoutsec = 10;
if($scope.running)
{
$scope.timer = setInterval($scope.refresh, 10000);
$scope.count = $timeout($scope.countdown,1000);
}
else
{
clearInterval($scope.timer);
$timeout.cancel($scope.count);
}
}
countdown function --
$scope.countdown = function(){
$scope.timeoutsec = $scope.timeoutsec - 1;
$scope.count = $timeout($scope.countdown, 1000);
}
refresh function --
$scope.refresh = function(){
// update function
$scope.timeoutsec = 11;
}
Edit:
update function --
$scope.update() =function(){
for(var i=0;i<cluster.length;i++)
{
var request = $.ajax(cluster[i]).success(function(data, status, headers, config)
{
// on success
});
}
}
If you are using xmlhttp request, you have to set third parameter in xhr.open() function as true. This will make the request synchronous, shown below :
xhr.open(method, url, true);
Here is some boilerplate code for you to see how it could be done.
(function() {
'use strict';
angular.module('app')
.controller('controller', function($scope, $http, $timeout, $q) {
$scope.update = function () {
// single request
$http.get('url')
.then(function (data) {
$timeout($scope.update, 10000);
});
// or with multiple requests
var request1 = $http.get(/****/);
var request2 = $http.get(/****/);
var request3 = $http.get(/****/);
$q.all([request1, request2, request3])
.then(function() {
$timeout($scope.update, 10000);
});
};
});
})();
Initialise $scope.running to be false, only on the press of auto refresh button running should be set to true.
$scope.running = false;
autorefresh function -
$scope.autoRefresh = function(){
$scope.running = !$scope.running;
$scope.timeoutsec = 10;
if($scope.running)
{
$scope.count = $timeout($scope.countdown,1000);
}
else
{
$timeout.cancel($scope.count);
}
}
countdown function --
$scope.countdown = function(){
$scope.timeoutsec = $scope.timeoutsec - 1;
if($scope.timeoutsec == 0)
{
$scope.refresh();
}
whenAll($scope.queries).done(function(){
$scope.count = $timeout($scope.countdown, 1000);
});
refresh function ---
$scope.refresh = function(){
div.style.visibility = "visible";
$scope.update();
$scope.timeoutsec = 11;
}
whenall function --
function whenAll(promises) {
var i, data = [],
dfd = $.Deferred();
for (i = 0; i < promises.length; i++) {
promises[i].done(function(newData) {
data.push(newData);
if (data.length == promises.length) {
dfd.resolve(data);
}
}).fail(function() {
data.push("NO DATA");
if (data.length == promises.length) {
dfd.resolve(data);
}
});
}
return dfd.promise();
}
Push all your ajax queries to $scope.queries.
Push all your $http ajax into an array, like so, since $http return promises, youcan use the $q API to handle when all requests are finished:
var httpPromises = [
$http({method:'GET', url: '/myjson0.json'}),
$http({method:'GET', url: '/myjson1.json'}),
$http({method:'GET', url: '/myjson2.json'}),
$http({method:'GET', url: '/myjson3.json'})
];
//handle your requests like you would normally do
httpPromises[0].success(...).fail(...);
httpPromises[1].success(...).fail(...);
httpPromises[2].success(...).fail(...);
httpPromises[3].success(...).fail(...);
// remember to inject $q in your controller/factory
$q.all(httpPromises,function(){
//start your timer here
});
$q documentation
Hi i have been trying to get into angular.j. I have tried the following code. But the console.log() not seems to be working. Anything am missing?? like angular concepts or something?
var foodshareModule= angular.module('food',['ui.router','ngResource']);
console.log("Main file getting included");
foodshareModule.controller("personController", function($scope) {
console.log($scope);
$scope.firstName = "John";
$scope.lastName = "Doe";
console.log($scope.firstName);
console.log($scope.lastName);
});
foodshareModule.controller('scratchListController', function($scope,$state,Notes){
console.log("working");
$scope.scratchpad =Food.query();
$scope.deleteScratch = function (scratch,flag) {
if(flag === 0) { //Checks if Bulk delete or single delete
if(confirm("You Clicked DELETE !! Are you sure ?")) {
scratch.$delete(function() { //Single delete
window.location.href = 'http://localhost:1337';
});
}
}
else { //Bulk delete
scratch.$delete(function() {
window.location.href = 'http://localhost:1337';
});
}
}
$scope.emptyScratchpad = function () {
var ask = false;
if (confirm ("You are about Empty Scratchpad. Sure ????")) {
ask = true;
}
if(ask === true) {
for (var i = 0; i < $scope.scratchpad.length; i++) {
$scope.deleteScratch($scope.scratchpad[i],1);
}
}
}
})
foodshareModule.factory('Food', function($resource) {
return $resource('http://localhost:1337/Food:id', { id: '#_id' }, {
update: {
method: 'PUT'
}
});
});
Any help would be appreciated. Thanks in advance.
By looking at you code seems like you injected wrong dependency in scratchListController, It should be Food instead of Notes
Code
foodshareModule.controller('scratchListController', function($scope, $state, Food) { //you were added Notes, but there should be Food
console.log("working");
//..other code here
})
var app = angular.module('myApp',[])
.factory('Food', function($resource) {
return $resource('http://localhost:1337/Food:id', { id: '#_id' }, {
update: {
method: 'PUT'
}
});
})
.controller("personController", function($scope) {
console.log($scope);
$scope.firstName = "John";
$scope.lastName = "Doe";
console.log($scope.firstName);
console.log($scope.lastName);
})
.controller('scratchListController', function($scope){
console.log("working");
$scope.deleteScratch = function (scratch,flag) {
if(flag === 0) { //Checks if Bulk delete or single delete
if(confirm("You Clicked DELETE !! Are you sure ?")) {
scratch.$delete(function() { //Single delete
window.location.href = 'http://localhost:1337';
});
}
}
else { //Bulk delete
scratch.$delete(function() {
window.location.href = 'http://localhost:1337';
});
}
}
$scope.emptyScratchpad = function () {
var ask = false;
if (confirm ("You are about Empty Scratchpad. Sure ????")) {
ask = true;
}
if(ask === true) {
for (var i = 0; i < $scope.scratchpad.length; i++) {
$scope.deleteScratch($scope.scratchpad[i],1);
}
}
}
});
Here is your solution. It shows working in the console.
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;
}
}