$Watch is not working until a event is fired - javascript

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);

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 to reset and set up timer function?

I have a quiz app with a controller where I should set up a timer that should be counting for 30s, and then stop the quiz if there is no activity in that time. If there is some activity in the meantime it should be reset and start counting again. I have set up web socket listeners for that. How should I do the timer setup?
This is my controller:
angular.module('quiz.controllers')
.controller('MultiPlayerQuestionController', function(
$scope,
$rootScope,
$state,
$stateParams,
$timeout,
UserService,
QuizService,
InviteService,
MessageService,
SocketService
) {
$scope.user = UserService.get();
$scope.quiz = QuizService.getCurrent();
$scope.opponent = $scope.user.player.id == $scope.quiz.players[0].id
? $scope.quiz.players[1]
: $scope.quiz.players[0]
|| null;
$scope.currentQuestion = {};
$scope.answer = {};
$scope.showAlternatives = false;
$scope.showCorrect = false;
$scope.isLast = false;
$scope.opponentAnswered = false;
var timeouts = {
stop: null,
activate: null
};
var startTime,
loadBar,
initiated = false;
// Opponent has answered; update score display
SocketService.socket.removeAllListeners('gameEvent:opponentAnswer');
SocketService.socket.on('gameEvent:opponentAnswer', function(message) {
$scope.opponentAnswered = true;
QuizService.updateOpponentScore(message.data.totalScore);
});
// Next question is triggered from all players having answered
SocketService.socket.removeAllListeners('gameEvent:nextQuestion');
SocketService.socket.on('gameEvent:nextQuestion', function(message) {
$timeout(function() {
QuizService.setCurrentQuestion(message.data.question);
setupQuestion(message.data.question);
}, 3000);
});
// Game is finished, go to score screen
SocketService.socket.removeAllListeners('gameEvent:quizFinished');
SocketService.socket.on('gameEvent:quizFinished', function(message) {
stopQuiz();
$timeout(function() {
$state.go('multiplayer.score');
}, 3000);
});
// An opponent has quit, go to score screen
SocketService.socket.removeAllListeners('gameEvent:opponentQuit');
SocketService.socket.on('gameEvent:opponentQuit', function(message) {
stopQuiz();
MessageService.alertMessage('Motstanderen din har enten gitt opp eller blitt frakoblet.');
$state.go('multiplayer.score');
});
// Disconnected. Go back to home screen.
SocketService.socket.removeAllListeners('reconnecting');
SocketService.socket.on('reconnecting', function() {
MessageService.alertMessage('Du har mistet tilkoblingen. Spillet har blitt avbrutt.');
SocketService.socket.removeAllListeners('reconnecting');
$state.go('main.front');
});
// The app was paused (closed), equals giving up.
var pauseEvent = $rootScope.$on('app:paused', function() {
QuizService.giveUpCurrent($scope.user.player);
var resumeEvent = $rootScope.$on('app:resumed', function() {
stopQuiz();
$state.go('multiplayer.score');
resumeEvent();
});
pauseEvent();
});
/**
* Give up the current quiz.
*/
$scope.giveUp = function (player) {
MessageService.confirm('Ønsker du å avbryte quizen?').then(function(result) {
if (result) {
QuizService.giveUpCurrent(player);
$state.go('multiplayer.score', {}, { reload: true });
stopQuiz();
}
});
};
/**
* Go to next question for current quiz.
*/
$scope.nextQuestion = function() {
$timeout.cancel(timeouts.stop);
$timeout.cancel(timeouts.activate);
QuizService.nextQuestion().$promise.then(function(question) {
setupQuestion(QuizService.getCurrentQuestion());
});
};
/**
* Finish quiz.
*/
$scope.finish = function() {
QuizService.finish();
$state.go('multiplayer.score');
};
/**
* Choose an alternative (aka answer current question).
*/
$scope.chooseAlternative = function(alternative) {
if (!$scope.showAlternatives || $scope.showCorrect) {
return;
}
var answerTime = Date.now() - startTime;
$scope.answer = alternative;
QuizService.answer(alternative, answerTime);
if (timeouts.stop) {
$timeout.cancel(timeouts.stop);
}
stopQuestion();
};
/**
* Set up a new question - change data and start countdown to activate question.
*/
var setupQuestion = function(question) {
$scope.showAlternatives = false;
$scope.showCorrect = false;
$scope.currentQuestion = question;
$scope.answer = {};
$scope.isLast = parseInt($scope.quiz.questionCount) == parseInt($scope.currentQuestion.questionNumber);
var prepareTime = 5000;
var newLoadBar = loadBar.cloneNode(true);
loadBar.parentNode.replaceChild(newLoadBar, loadBar);
loadBar = newLoadBar;
setAnimationDuration(loadBar, 'loadbar', prepareTime);
timeouts.activate = $timeout(activateQuestion, prepareTime);
};
/**
* A question timed out; stop and send empty answer.
*/
var questionTimeout = function() {
// Delay answering by a random delay between 0 and 500ms.
$timeout(function() {
stopQuestion();
QuizService.noAnswer($scope.currentQuestion.id);
}, Math.floor((Math.random() * 500) + 1));
};
/**
* Activate the current question: show alternatives and open answering.
*/
var activateQuestion = function() {
$scope.showAlternatives = true;
var timeToAnswer = 10000;
startTime = Date.now();
var newLoadBar = loadBar.cloneNode(true);
loadBar.parentNode.replaceChild(newLoadBar, loadBar);
loadBar = newLoadBar;
setAnimationDuration(newLoadBar, 'loadbar', timeToAnswer);
timeouts.stop = $timeout(questionTimeout, timeToAnswer);
};
/**
* Stop the current question and show the correct answer info.
*/
var stopQuestion = function() {
$scope.showCorrect = true;
stopAnimation(loadBar);
$timeout.cancel(timeouts.stop);
};
/**
* End the current quiz.
*/
var stopQuiz = function() {
SocketService.socket.removeAllListeners('gameEvent:opponentAnswer');
SocketService.socket.removeAllListeners('gameEvent:nextQuestion');
SocketService.socket.removeAllListeners('gameEvent:quizFinished');
SocketService.socket.removeAllListeners('gameEvent:opponentQuit');
SocketService.socket.removeAllListeners('reconnecting');
$timeout.cancel(timeouts.stop);
$timeout.cancel(timeouts.activate);
};
/**
* Set the animation duration for an element. Used to stop and start the
* progress bar.
*/
var setAnimationDuration = function(element, keyframes, duration) {
var animationSetting = keyframes + ' ' + duration + 'ms linear';
element.style.webkitAnimation = animationSetting;
element.style.animation = animationSetting;
}
var stopAnimation = function(element) {
element.style.webkitAnimation = 'none';
element.style.animation = 'none';
};
if (!initiated) {
initiated = true;
loadBar = document.getElementById('load-bar');
setupQuestion(QuizService.getCurrentQuestion());
}
});
I have tried with calling the responseTimer function that I have made in my controller. At the begging of the file I am calling it like this:
responseTimer(30000);
And then later I am defining it like this:
var responseTimer = function (time) {
responseTimer = $timeout(stopQuiz, time);
console.log('Started timer');
};
var resetResponseTimer = function () {
$timeout.cancel(responseTimer);
responseTimer(30000);
console.log("Timer reset");
};
But I get an error:
TypeError: responseTimer is not a function
problem comes from a scope conflict. In your code
// responseTimer is declared as a global function
var responseTimer = function (time) {
// as the var keyword is not specify, scope of responseTimer becomes global and not local and overrides the previous declaration
responseTimer = $timeout(stopQuiz, time);
That is why you get the error
responseTimer is not a function
To solve the problem add the var keyword before the second declaration and name your variables appropriately. A good practice is to add an action verb when naming function/object's methods, in your case triggerResponseTimer as name of your function and responseTimer as name of the variable so final porposition would be:
var triggerResponseTimer = function (time) {
var responseTimer = $timeout(stopQuiz, time);
You should better use $intervall:
Here you will find a good sample:
http://tutorials.jenkov.com/angularjs/timeout-interval.html
Also good sample:
https://docs.angularjs.org/api/ng/service/$interval

Interference between a countdown directive and an angular controller?

I have some extremely weird interference between two angular functions in my application that I cannot explain. The end result of the problem causes this to happen:
I have a countdown directive embedded in one of my pages which is also the domain of an angular controller. Here is the countdown directive:
(function() {
var app = angular.module('app');
app.directive('countdown', ['$interval', function($interval) {
return {
restrict: 'E',
scope: {
specificity: '=',
countdownTo: '=',
callback: '&?'
},
link: function($scope, elem, attrs) {
$scope.isLaunchExact = ($scope.specificity == 6 || $scope.specificity == 7);
$scope.$watch('specificity', function(newValue) {
$scope.isLaunchExact = (newValue == 6 || newValue == 7);
});
var countdownProcessor = function() {
var launchUnixSeconds = $scope.launchUnixSeconds;
var currentUnixSeconds = Math.floor(Date.now() / 1000);
if (launchUnixSeconds >= currentUnixSeconds) {
$scope.secondsAwayFromLaunch = launchUnixSeconds - currentUnixSeconds;
var secondsBetween = $scope.secondsAwayFromLaunch;
// Calculate the number of days, hours, minutes, seconds
$scope.days = Math.floor(secondsBetween / (60 * 60 * 24));
secondsBetween -= $scope.days * 60 * 60 * 24;
$scope.hours = Math.floor(secondsBetween / (60 * 60));
secondsBetween -= $scope.hours * 60 * 60;
$scope.minutes = Math.floor(secondsBetween / 60);
secondsBetween -= $scope.minutes * 60;
$scope.seconds = secondsBetween;
$scope.daysText = $scope.days == 1 ? 'Day' : 'Days';
$scope.hoursText = $scope.hours == 1 ? 'Hour' : 'Hours';
$scope.minutesText = $scope.minutes == 1 ? 'Minute' : 'Minutes';
$scope.secondsText = $scope.seconds == 1 ? 'Second' : 'Seconds';
} else {
}
if (attrs.callback) {
$scope.callback();
}
};
// Countdown here
if ($scope.isLaunchExact) {
$scope.launchUnixSeconds = moment($scope.countdownTo).unix();
$interval(countdownProcessor, 1000);
} else {
$scope.countdownText = $scope.countdownTo;
}
},
templateUrl: '/js/templates/countdown.html'
}
}]);
})();
Here is the Angular controller that is being interfered with:
(function() {
var app = angular.module('app', []);
//app.value('duScrollDuration', 1000);
app.controller("homeController", ['$scope', 'Statistic', function($scope, Statistic) {
$scope.statistics = [];
$scope.activeStatistic = false;
$scope.goToClickedStatistic = function(statisticType) {
history.replaceState('', document.title, '#' + statisticType);
$scope.activeStatistic = statisticType;
};
$scope.goToNeighborStatistic = function(index) {
if (index >= 0 && index < $scope.statistics.length) {
var stat = $scope.statistics[index];
history.replaceState('', document.title, '#' + stat.camelCaseType); // WhyIsThisFlashing
$scope.activeStatistic = stat.camelCaseType;
return stat.camelCaseType;
} else {
$scope.goHome();
}
};
$scope.goToFirstStatistic = function() {
};
$scope.goHome = function() {
history.replaceState('', document.title, window.location.pathname);
$scope.activeStatistic = false;
return 'home';
};
/*$window.on('scroll',
$.debounce(100, function() {
$('div[data-stat]').fracs('max', 'visible', function(best) {
$scope.activeStatistic($(best).data('stat'));
});
})
);*/
(function() {
laravel.statistics.forEach(function(statistic) {
$scope.statistics.push(new Statistic(statistic));
});
if (window.location.hash) {
$scope.activeStatistic = window.location.hash.substring(1);
}
})();
}]);
app.factory('Statistic', function() {
return function(statistic) {
var self = {};
self.changeSubstatistic = function(newSubstatistic) {
self.activeSubstatistic = newSubstatistic;
};
statistic.forEach(function(substatistic) {
if (!self.substatistics) {
self.substatistics = [];
self.activeSubstatistic = substatistic;
self.type = substatistic.type;
self.camelCaseType = self.type.replace(" ", "");
}
self.substatistics.push(substatistic);
});
return self;
}
});
})();
Each time my countdown directive's countdownProcessor function present in the $interval is run, goToNeighborStatistic in my angular controller is, somehow, called. I don't have a clue why.
If I step through my countdown directive, after stepping through the countdownProcessor function, an "Anonymous Script" named SCRIPT0 is called with the following contents:
"use strict";
var fn=function(s,l,a,i){var v0,v1,v2,v3=l&&('goToNeighborStatistic' in l),v4,v5,v6=l&&('\u0024index' in l);v2=v3?l:s;if(!(v3)){if(s){v1=s.goToNeighborStatistic;}}else{v1=l.goToNeighborStatistic;}if(v1!=null){ensureSafeFunction(v1,text);if(!(v6)){if(s){v5=s.$index;}}else{v5=l.$index;}v4=ifDefined(v5,0)-ifDefined(1,0);ensureSafeObject(v2,text);v0=ensureSafeObject(v2.goToNeighborStatistic(ensureSafeObject(ifDefined(v5,0)-ifDefined(1,0),text)),text);}else{v0=undefined;}return v0;};return fn;
Why is "goToNeighborStatistic" present in this? Why is this script even present? From there, goToNeighborStatistic() runs, at which point, the countdown is called again and everything repeats. What's going on here?
EDIT:
In Chrome, stepping through everything produces different results. At the end of the countdownProcessor function, stepping into produces this:

Auto refresh in AngularJS

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

Error in angular js controller for pagination

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.

Categories

Resources