How to check if all documents are loaded with Firebase.util pagination - javascript

How can I check if I have to stop calling the loadMore() function, because all the documents have already been loaded from the database?
In the example below I'm using Ionic, but it's the same also with ng-infinite-scroll in AngularJS apps.
This is my actual code:
HTML:
...
<ion-infinite-scroll
ng-if="!noMoreItemsToLoad"
on-infinite="loadMore()"
distance="5%">
</ion-infinite-scroll>
</ion-content>
JS Controller:
$scope.loadMore = function(){
console.log('Loading more docs...');
Items.loadMore(); // calling the .next() method inside the Items service
if( !Items.hasNext()) { $scope.noMoreItemsToLoad = true; }
$scope.$broadcast('scroll.infiniteScrollComplete');
}
JS Items Factory:
.factory('Items', function (FIREBASE_URL, $firebaseArray, $firebaseObject) {
var itemsRef = new Firebase(FIREBASE_URL + 'items/');
var scrollRef = new Firebase.util.Scroll(itemsRef, 'name');
var self = {
getAllItems : function(){ ... },
loadMore: function(){
scrollRef.scroll.next(4);
},
hasNext: function(){
if(scrollRef.scroll.hasNext()) { return true; }
else { return false; }
}
}
return self;
}

Do the scroll.next in timeout, for example:
loadMore: function(){
$timeout(function() {
scrollRef.scroll.next(4);
});
},

I had the same issue and I think the solution is to modify the hasNext() function on firebase.util.js:
Cache.prototype.hasNext = function() {
return this.count === -1 || this.endCount >= this.start + this.count;
};
I put a missing equal sign (=) before this.start
I hope it works for you.

Related

object delegation with module pattern

I'm trying to seperate concerns using the module pattern and everything is going Ok except that I'm trying to delegate the dom strings from a module (the UIController module) to another actually I succeeded at doing it once but I don't know what is happening know it didn't work
as you see above the Domstrings object is inside the UIcontroller module so I expose it to the public so the other modules could use it
and as you see I did it before and it works fine without any problem as you see below
but when I use it inside the internalController module I got this error
so here is where I'm using it in:
so here is my code and thank you in advance:
JS
var internalController = (function(UICtrl) {
addItem: function(day, from, to, text, goingToCkecked) {
var newPlan, ID,Dom=UICtrl.getDOMstrings();
if (day === 'pick the day') {
document.querySelector(Dom.errorCase).style.visibility = "visible";
document.querySelector(".optionList").classList.add("error-red");
} else {
document.querySelector(".error-case").style.visibility = "hidden";
document.querySelector(".optionList").classList.remove("error-red");
console.log("that is me");
}
document.querySelector("#optionList").addEventListener("change", function(e) {
document.querySelector(".error-case").style.visibility = "hidden";
document.querySelector(".optionList").classList.remove("error-red");
});
})(UIController);
var UIController = (function() {
var DOMstrings = {
inputDay: ".optionList",
inputTimeF: ".inputTime",
inputTimeT: ".inputTime2",
inputText: ".inputText",
goingToCkecked: ".checkboxx",
inputBtn: ".add__btn",
planContainer: ".container",
errorCase: ".error-case",
optionList: ".optionList",
};
return {
getInput: function() {
return {
inputDay: document.querySelector(DOMstrings.inputDay).value,
inputTimeF: document.querySelector(DOMstrings.inputTimeF).value,
inputTimeT: document.querySelector(DOMstrings.inputTimeT).value,
inputText: document.querySelector(DOMstrings.inputText).value,
goingToCkecked: document.querySelector(DOMstrings.goingToCkecked).checked,
};
},
getDOMstrings: function() {
return DOMstrings;
},
}
}
};
})();
var controller = (function(interCtrl, UICtrl) {
var input, newPlan;
function setupEventListeners() {
var DOM = UICtrl.getDOMstrings();
document.querySelector(DOM.inputBtn).addEventListener("click", ctrlAddPlans);
document.addEventListener("keypress", function(e) {
if (e.keyCode === 13) {
ctrlAddPlans();
}
});
}
return {
init: function() {
console.log('the app has started');
setupEventListeners();
},
};
})(internalController, UIController);
controller.init();
// setInterval(function() {
// }, 100);
setTimeout(function() {
document.querySelector(".plansBackground").classList.add("height");
}, 1000);

AngularJS $timeout service

I have to have something visible for 3-4 seconds. I am trying to use $timeout for achieving that. Here's what I got so far:
$timeout(function() {
debugger;
$scope.$on(broadcastService.topErrorBar.show,
function(event, message) {
$scope.rootElement.addClass('is-visible');
$scope.isVisible = true;
$scope.message = message;
});
}, 3000);
$timeout.cancel(function() {
$scope.close();
});
$scope.close = function() {
$scope.rootElement.removeClass('is-visible');
$scope.isVisible = false;
};
This is not working and I'm not able to resolve the issue. What am I doing wrong? Should I use a timeout in this case.
what about:
$scope.$on(broadcastService.topErrorBar.show,
function(event, message) {
$scope.isVisible=false;
$timeout(function () { $scope.isVisible= true; }, 3000);
});
you have to use in html ng-show="isVisible">
It should be like this :
$scope.$on(broadcastService.topErrorBar.show,
function(event, message) {
$scope.rootElement.addClass('is-visible');
$scope.isVisible = true;
$scope.message = message;
$timeout(function() {
$scope.close();
}, 3000);
$scope.close = function() {
$scope.rootElement.removeClass('is-visible');
$scope.isVisible = false;
};
On Broadcast , make element visible , start a timeout so that after 3 seconds $scope.close will be called. No need for $timeout.cancel in your case.
Your logic is inverted. The function in a timeout fires after the time has elapsed. You want the element to be visible, and then set visibility to false in your timeout function. Here's an example.
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope, $timeout) {
$scope.visible = true;
$timeout(function () {
$scope.visible = false;
}, 3000);
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="myCtrl">
This is always visible.
<span ng-show="visible">This should hide after 3 seconds</span>
</div>

AngularJS + Ionic is truncating scope value after the return from second page to the main page.

I have following code in the "First" Ctrl of the my app, which is displaying timer for countdown.
Everything is working absolutely fine until i visited second page which is in the app.js defined by this way:
.state('app.home', {
url: '/home:playlistData',
views: {
'menuContent' :{
templateUrl: 'templates/home.html',
controller: 'HomeCtrl'
}
}
})
.state('app.saved', {
url: '/saved',
views: {
'menuContent' :{
templateUrl: 'templates/saved.html',
controller: 'SavedCtrl'
}
}
})
If i came back from second view to the first counter is still displayed but value in the
$scope.minutesLeft
Is not updated but
setInterval
function is still executing the code in the background and updated data values are still holded in the Dataholdingservice.
I tried scope apply and timeout functions, but without the luck.
Could somebody tell me how can i solve this issue?
Many thanks for any help.
Code of the HomeCtrl for countdown timer is following:
$scope.setTimer = function(timer) {
console.log(timer);
$scope.timer = timer;
};
$scope.saveTimer = function(timer) {
if($scope.selectedSounds.length == 0) {
$scope.showAlert("Add some sounds", "Cannot run timer for empty list");
} else {
$scope.clearCountDownAnimation();
var animationTimerId = setInterval(function () {
$("#minutesLeft").fadeTo(100, 0.1).fadeTo(200, 1.0);
}, 1000);
Dataholdingservice.setAnimationId(animationTimerId);
Dataholdingservice.setMinutesLeft(timer);
$scope.closePopover();
$scope.countDown();
}
};
$scope.clearCountDownAnimation = function() {
$("#minutesLeft").clearQueue().finish();
// Clear previously set animations
console.log(Dataholdingservice.getAnimationId());
if (Dataholdingservice.getAnimationId() != null) {
console.log("Interval cleared");
clearInterval(Dataholdingservice.getAnimationId());
}
};
$scope.countDown = function() {
var minutesLeft = Dataholdingservice.getMinutesLeft();
$scope.minutesLeft = minutesLeft;
$scope.isCounterDisplayed = Dataholdingservice.isCounterDisplayed();
var timerId = setInterval(function() {
console.log("Counting down");
minutesLeft -- ;
console.log("Decreasing minutes");
console.log(minutesLeft);
Dataholdingservice.setMinutesLeft(minutesLeft);
console.log("minutes left " + Dataholdingservice.getMinutesLeft());
$scope.$apply(function () {
$scope.minutesLeft = Dataholdingservice.getMinutesLeft();
});
if(minutesLeft <= 0) {
console.log("Time left");
clearInterval(Dataholdingservice.getTimerId());
clearInterval(Dataholdingservice.getAnimationId());
console.log(Dataholdingservice.isCounterDisplayed());
$scope.hideCounter();
$scope.stopAllSelectedSounds();
}
}, 1000 * 1);
Dataholdingservice.setTimerId(timerId);
};
$scope.hideCounter = function() {
console.log("Hidding the counter");
$scope.isCounterDisplayed = false;
$scope.$apply();
};
$scope.cancelTimer = function() {
clearInterval(Dataholdingservice.getTimerId());
clearInterval(Dataholdingservice.getAnimationId());
$("#minutesLeft").hide();
$ionicLoading.show({
duration: 500,
template: 'Timer canceled'
});
};
Since the $scope.minutesLeft is a primitive datatype, sometimes the changes happening in the controller will not get reflected in the view. You can create an object like $scope.viewModel = {} and then add the minutesLeft as a property to it like $scope.viewModel.minutesLeft = mintesLeft in your countdown function and bind viewModel.minutesLeft to the view. you should see the value getting updated properly.
I am not sure of your exact requirement, but I have put together the code for creating a simple timer that runs in the background in an angular service. The working code is available at http://codepen.io/svswaminathan/pen/MYXOPM

Change hash without triggering Sammy event

function UsersVM(start_page){
var self = this;
console.log('start form ' + start_page);
self.go_to = function(page) {
location.hash = '#Users/' + pageNumber;
}
}
Sammy(function() {
this.get('/app/?#Users/:page', function () {
var vm = new UsersVM(this.params.page);
ko.applyBinding(vm);
});
}).run();
I would like to change the page's hash with the following code:
location.hash = '#Users/' + pageNumber;
But in this case Sammy triggers routing. Say in Backbone we can do it this way:
app.navigate("help/troubleshooting", {trigger: false});
Is it possible to do it in Sammy?
Thanks!
I don't know of a native way to do this in Sammy, but here is a solution that has worked for me:
var sam = $.sammy(function () {
var sammy = this; //get a persistent reference to this
sammy.quiet = false; //set quiet to false by default
//I set quiet to true before running a route
sammy.quietRoute = function (location) {
sammy.quiet = true;
sammy.setLocation(location);
}
//I'm called after every route to reset quiet to false
sammy.after(function () {
sammy.quiet = false;
});
//I'm a 'normal' route that does not have the capability to be 'quiet'
this.get('#normalRoute', function () {
//routing code
});
//I am a route that can be 'quieted' so that when the url or
//hash changes my routing code doesn't run
this.get('#quietableRoute', function () {
if (!sammy.quiet) {
//routing code
} else {
return;
}
});
});
Then call the quietRoute function in your code:
//This will work
sam.quietRoute("#quietableRoute");
//This will not work because the "if(!sammy.quiet)..." code has not been
//implemented on this route
sam.quietRoute("#normalRoute");
Use the following code:
var new_location = '#foo';
app.trigger('redirect', {to: new_location});
app.last_location = ['get', new_location];
app.setLocation(new_location);

Knockoutjs custom binding to display loading gif

I'm trying to create a custom binding that will show a loading gif while content is loading.
ko.bindingHandlers.loader = {
init: function (element) {
$('<div>').addClass('loader').hide().appendTo($(element));
},
update: function (element, valueAccessor) {
var isLoading = ko.utils.unwrapObservable(valueAccessor());
var $element = $(element);
var $children = $element.children(':not(.loader)');
var $loader = $(element).find('.loader');
if(isLoading) {
$children.stop(true).css('visibility', 'hidden').attr('disabled', 'disabled');
$loader.stop().fadeIn();
} else {
$loader.stop(true).fadeOut(function () {
$children.css('visibility', 'visible').removeAttr('disabled');
});
}
}
};
I can see in the init that div.loader is being appended to the element, and can see the update function fire when isLoading is changed to true. But once the images have loaded (by loaded i mean each image returns a resolved promise on the their respective load event) I don't see the update firing once isLoading is set back to false.
viewModel
function viewModel() {
var self = this;
self.movies = ko.observableArray([]);
self.searchValue = ko.observable();
self.isLoading = ko.observable(false);
self.search = function () {
self.isLoading = true;
$.getJSON(arguments[0].action, { name: this.searchValue() }, function (data) {
self.movies(data);
$.when.apply($, promises).done(function() {
setThumbnailHeight(function () {
self.isLoading = false;
});
});
});
};
var setThumbnailHeight = function(callback) {
var $items = $('.thumbnails li');
var maxHeight = Math.max.apply(null, $items.map(function () {
return $(this).innerHeight();
}).get());
$items.css('height', maxHeight);
callback();
};
}
ko.applyBindings(new viewModel());
setThumbnailHeight is being called at the correct time (once all promises have resolved) and is working properly, in that I see it setting the height of each li to the max height and can see the callback (in this case function(){ self.isLoading = false; } being called.
my binding
<ul class="content thumbnails" data-bind="foreach: movies, loader: $root.isLoading">
<li class="movie">
...
</li>
</ul>
So just to recap, the problem is that the loading gif will be displayed when isLoading is set to true but is not hiding and showing the newly loaded content when it's set back to false.
All observables are function so you cannot assign value to it using =. Use self.isLoading(true); instead of self.isLoading = true;
self.search = function () {
self.isLoading(true);
$.getJSON(arguments[0].action, { name: this.searchValue() }, function (data) {
self.movies(data);
$.when.apply($, promises).done(function() {
setThumbnailHeight(function () {
self.isLoading(false);
});
});
});
};
function(){ self.isLoading(false); }

Categories