Refer to scope variable in directive two way data binding - javascript

I am trying to create a reusable progress bar directive with isolate scope. This directive will have the public functions to start, stop and reset the progress bar. This directive will be used within ng-repeat
Here is the definition of directive:
chatApp.directive('jsProgress', function() {
var Stopwatch = function(options, updateCallback) {
// var timer = createTimer(),
var offset, clock, interval;
// default options
options = options || {};
options.delay = options.delay || 1;
// initialize
reset();
function start() {
if (!interval) {
offset = Date.now();
interval = setInterval(update, options.delay);
}
}
function stop() {
if (interval) {
clearInterval(interval);
interval = null;
}
}
function reset() {
clock = 0;
// render(0);
}
function update() {
clock += delta();
// render();
updateCallback();
}
function delta() {
var now = Date.now(),
d = now - offset;
offset = now;
return d;
}
// public API
this.start = start;
this.stop = stop;
this.reset = reset;
this.update = update;
};
return {
restrict : 'AE',
replace : true,
scope: { api: '=', key: '#'},
template: '<div class="dot" ng-attr-id="dots"><ul id="{{key}}" data-currentState="2"><li class="dot-red"></li><li></li><li></li><li></li></ul></div>',
link : function($scope, elem, attr) {
var timer = new Stopwatch( {delay: 5000}, updateCallback);
timer.start();
function updateCallback()
{
var currentCount;
currentCount = $(elem).find('#' + $scope.key).attr('data-currentState');
currentCount++;
$(elem).find('#' + $scope.key).attr('data-currentState',currentCount);
$(elem).find('#' + $scope.key+' li:nth-child(' + currentCount + ')').addClass('dot-red');
}
$scope.api =
{
reset: function()
{
timer.reset();
},
start: function()
{
timer.start();
},
stop: function()
{
timer.stop();
}
};
}
};
});
This is how it will be used within ng-repeat
<js-progress api="{{activeUserId}}" key="{{activeUserId}}_{{activeCompanyId}}" />
Now I want to get a particular instance of directive within ng-repeat and call its public API to start, stop and reset the particular progress bar. How can I do the same? In the above definition, it doesn't allow me to use the variable {{activeUserId}} because I want to refer each instance individually in the ng-repeat.

You are overwriting your activeUserId which is being passed from your Ctrl to your directive at at this line:
$scope.api = {};
I believe that you should keep track of your api objects in your controller in this way:
in your controller
$scope.bars = [
{
activeUserId: "id",
activeCompanyId: "companyId",
api: {} //this allows angularjs to reuse this object instance
},
{
activeUserId: "id2",
activeCompanyId: "companyId2",
api: {} //this allows angularjs to reuse this object instance
},
];
the html template for your controller
<div ng-repeat="b in bars">
<js-progress api="b.api" your-extra-params-here />
</div>
Later on in your controller, you will be able to do:
$scope.bars[0].api.start();

Related

JavaScript + jQuery + Angular: Element is not found

I have a JavaScript function defined. Inside this function, I define variables of type jQuery elements. So, the variables refer to divs on the HTML. This function returns an object that has a single function init().
In the $(document).ready function, I call init() function.
The problem is when the script loads, the DOM is not ready and hence the variables referring to jQuery items are being set to undefined.
Later, I call the init() function inside Angular ngOnInit() to make sure things are well initialized, so nothing is happening as the variables above are undefined and they are not being re-calculated again.
Seems, when a function in JavaScript is defined, its body runs, and hence the variables are run and set to undefined as the HTML elements were not in the DOM yet.
How can I re-calculate the variables when init() runs? I cannot get my mind on this thing.
Thanks
var mQuickSidebar = function() {
var topbarAside = $('#m_quick_sidebar');
console.log('Function: ', Date.now());
var topbarAsideTabs = $('#m_quick_sidebar_tabs');
var topbarAsideClose = $('#m_quick_sidebar_close');
var topbarAsideToggle = $('#m_quick_sidebar_toggle');
var topbarAsideContent = topbarAside.find('.m-quick-sidebar__content');
var initMessages = function() {
var messenger = $('#m_quick_sidebar_tabs_messenger');
if (messenger.length === 0) {
return;
}
var messengerMessages = messenger.find('.m-messenger__messages');
var init = function() {
var height = topbarAside.outerHeight(true) -
topbarAsideTabs.outerHeight(true) -
messenger.find('.m-messenger__form').outerHeight(true) - 120;
// init messages scrollable content
messengerMessages.css('height', height);
mApp.initScroller(messengerMessages, {});
}
init();
// reinit on window resize
mUtil.addResizeHandler(init);
}
var initSettings = function() {
var settings = $('#m_quick_sidebar_tabs_settings');
if (settings.length === 0) {
return;
}
// init dropdown tabbable content
var init = function() {
var height = mUtil.getViewPort().height - topbarAsideTabs.outerHeight(true) - 60;
// init settings scrollable content
settings.css('height', height);
mApp.initScroller(settings, {});
}
init();
// reinit on window resize
mUtil.addResizeHandler(init);
}
var initLogs = function() {
// init dropdown tabbable content
var logs = $('#m_quick_sidebar_tabs_logs');
if (logs.length === 0) {
return;
}
var init = function() {
var height = mUtil.getViewPort().height - topbarAsideTabs.outerHeight(true) - 60;
// init settings scrollable content
logs.css('height', height);
mApp.initScroller(logs, {});
}
init();
// reinit on window resize
mUtil.addResizeHandler(init);
}
var initOffcanvasTabs = function() {
initMessages();
initSettings();
initLogs();
}
var initOffcanvas = function() {
topbarAside.mOffcanvas({
class: 'm-quick-sidebar',
overlay: true,
close: topbarAsideClose,
toggle: topbarAsideToggle
});
// run once on first time dropdown shown
topbarAside.mOffcanvas().one('afterShow', function() {
mApp.block(topbarAside);
setTimeout(function() {
mApp.unblock(topbarAside);
topbarAsideContent.removeClass('m--hide');
initOffcanvasTabs();
}, 1000);
});
}
return {
init: function() {
console.log('Inside Init(): ', Date.now());
console.log($('#m_quick_sidebar')); // topbarAside is undefined here!
if (topbarAside.length === 0) {
return;
}
initOffcanvas();
}
}; }();
$(document).ready(function() {
console.log('document.ready: ', Date.now());
mQuickSidebar.init();
});
The problem is that you immediately invoke your function mQuickSidebar not
Seems, when a function in JavaScript is defined, its body runs
var mQuickSidebar = function() {
var topbarAside = $('#m_quick_sidebar');
console.log('Function: ', Date.now());
var topbarAsideTabs = $('#m_quick_sidebar_tabs');
var topbarAsideClose = $('#m_quick_sidebar_close');
var topbarAsideToggle = $('#m_quick_sidebar_toggle');
var topbarAsideContent = topbarAside.find('.m-quick-sidebar__content');
...
}(); // <---- this runs the function immediately
So those var declarations are run and since the DOM is not ready those selectors don't find anything. I'm actually surprised that it does not throw a $ undefined error of some sort
This looks like a broken implementation of The Module Pattern
I re-wrote your code in the Module Pattern. The changes are small. Keep in mind that everything declared inside the IIFE as a var are private and cannot be accessed through mQuickSidebar. Only the init function of mQuickSidebar is public. I did not know what you needed to be public or private. If you need further clarity just ask.
As a Module
var mQuickSidebar = (function(mUtil, mApp, $) {
console.log('Function: ', Date.now());
var topbarAside;
var topbarAsideTabs;
var topbarAsideClose;
var topbarAsideToggle;
var topbarAsideContent;
var initMessages = function() {
var messenger = $('#m_quick_sidebar_tabs_messenger');
if (messenger.length === 0) {
return;
}
var messengerMessages = messenger.find('.m-messenger__messages');
var init = function() {
var height = topbarAside.outerHeight(true) -
topbarAsideTabs.outerHeight(true) -
messenger.find('.m-messenger__form').outerHeight(true) - 120;
// init messages scrollable content
messengerMessages.css('height', height);
mApp.initScroller(messengerMessages, {});
};
init();
// reinit on window resize
mUtil.addResizeHandler(init);
};
var initSettings = function() {
var settings = $('#m_quick_sidebar_tabs_settings');
if (settings.length === 0) {
return;
}
// init dropdown tabbable content
var init = function() {
var height = mUtil.getViewPort().height - topbarAsideTabs.outerHeight(true) - 60;
// init settings scrollable content
settings.css('height', height);
mApp.initScroller(settings, {});
};
init();
// reinit on window resize
mUtil.addResizeHandler(init);
};
var initLogs = function() {
// init dropdown tabbable content
var logs = $('#m_quick_sidebar_tabs_logs');
if (logs.length === 0) {
return;
}
var init = function() {
var height = mUtil.getViewPort().height - topbarAsideTabs.outerHeight(true) - 60;
// init settings scrollable content
logs.css('height', height);
mApp.initScroller(logs, {});
};
init();
// reinit on window resize
mUtil.addResizeHandler(init);
};
var initOffcanvasTabs = function() {
initMessages();
initSettings();
initLogs();
};
var initOffcanvas = function() {
topbarAside.mOffcanvas({
class: 'm-quick-sidebar',
overlay: true,
close: topbarAsideClose,
toggle: topbarAsideToggle
});
// run once on first time dropdown shown
topbarAside.mOffcanvas().one('afterShow', function() {
mApp.block(topbarAside);
setTimeout(function() {
mApp.unblock(topbarAside);
topbarAsideContent.removeClass('m--hide');
initOffcanvasTabs();
}, 1000);
});
};
return {
init: function() {
console.log('Inside Init(): ', Date.now());
console.log($('#m_quick_sidebar')); // topbarAside is undefined here!
topbarAside = $('#m_quick_sidebar');
topbarAsideTabs = $('#m_quick_sidebar_tabs');
topbarAsideClose = $('#m_quick_sidebar_close');
topbarAsideToggle = $('#m_quick_sidebar_toggle');
topbarAsideContent = topbarAside.find('.m-quick-sidebar__content');
if (topbarAside.length === 0) {
return;
}
initOffcanvas();
}
};
})(mUtil, mApp, $);
$(document).ready(function() {
console.log('document.ready: ', Date.now());
mQuickSidebar.init();
});

Pass event object from directive to callback

Im using Angular 1.5.6.
I have a directive for checking for a double click:
angular.module('redMatter.analyse')
.directive('iosDblclick',
function () {
var DblClickInterval = 300; //milliseconds
var firstClickTime;
var waitingSecondClick = false;
return {
restrict: 'A',
link: function (scope, element, attrs) {
element.bind('click', function (e) {
if (!waitingSecondClick) {
firstClickTime = (new Date()).getTime();
waitingSecondClick = true;
setTimeout(function () {
waitingSecondClick = false;
}, DblClickInterval);
}
else {
waitingSecondClick = false;
var time = (new Date()).getTime();
if (time - firstClickTime < DblClickInterval) {
scope.$apply(attrs.iosDblclick);
}
}
});
}
};
});
I use this here:
<div ios-dblclick="onDoubleClick($event, graph)" ></div>
graph is an object inside an ng-repeat directive. In onDoubleClick, I need access to $event:
$scope.onDoubleClick = function($event, graph){
console.log('in onDoubleClick and arguments are ', arguments);
var element = $event.srcElement;
However I'm unsure of how to pass the event from the directive to onDoubleClick. In the console log, arguments prints out as:
[undefined, Object]
Where Object is graph. How can I also pass back the event?
Since you could pass locals with $eval method, consider using it while calling attrs.iosDblclick. Internally it uses $parse API to evaluate method and uses local as a parameter.
scope.$eval(attrs.iosDblclick, {$event: e});
Plunker Demo
See also
Custom ng-enter directive not passing $event from html to the controller
UPDATED
http://jsfiddle.net/ADukg/14470/ - working example.
So, you could pass the function to directive like this:
<div ios-dblclick="onDoubleClick" ios-dblclick-arg="graf" ></div>
inside your directive:
return {
restrict: 'A',
scope: {
myCallback: '=iosDblclick',
graph: '=iosDblclickArg'
},
link: function (scope, element, attrs) {
element.bind('click', function (e) {
if (!waitingSecondClick) {
firstClickTime = (new Date()).getTime();
waitingSecondClick = true;
setTimeout(function () {
waitingSecondClick = false;
}, DblClickInterval);
}
else {
waitingSecondClick = false;
var time = (new Date()).getTime();
if (time - firstClickTime < DblClickInterval) {
scope.myCallback(e, scope.graph)
}
}
});
}
}
You can pass callback to directive as: onDoubleClick: '&' - isolate scope
webApp.directive('iosDblclick',
function () {
var DblClickInterval = 300; //milliseconds
var firstClickTime;
var waitingSecondClick = false;
return {
restrict: 'A',
scope: {
onDoubleClick: '&'
},
link: function (scope, element, attrs) {
element.bind('click', function (e) {
if (!waitingSecondClick) {
firstClickTime = (new Date()).getTime();
waitingSecondClick = true;
setTimeout(function () {
waitingSecondClick = false;
}, DblClickInterval);
}
else {
waitingSecondClick = false;
var time = (new Date()).getTime();
if (time - firstClickTime < DblClickInterval) {
scope.onDoubleClick({data: {e:e, val: "someValue"}});
}
}
});
}
};
});
Where HTML is:
<div ios-dblclick on-double-click="onDoubleClick(data)" ></div>
And controller:
$scope.onDoubleClick = function(data){
var element = data.e.srcElement;
}
Plunker Demo

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

Jquery Mobile stopwatch

Im trying to make a stopwatch in a JqueryMobile app. I've been following the guide from a previous post How to create a stopwatch using JavaScript?
This works but the function to create the button, essential just makes 3 links, where as I want them as buttons. So at present it will generate the html of:
start
where as I need it to be
start
I've played around with the function to try to get it to work, and even just added my own buttons into the HTML with hrefs of #start, #stop, #reset but cant get them to work
The function is:
function createButton(action, handler) {
var a = document.createElement("a");
a.href = "#" + action;
a.innerHTML = action;
a.addEventListener("click", function(event) {
handler();
event.preventDefault();
});
return a;
}
Add the classes ui-btn ui-btn-inline to the links in createButton. As you are using jQuery anyway, I hvae also updated the stopwatch to use jQuery for DOM manipulation:
(function($) {
var Stopwatch = function (elem, options) {
var timer = createTimer(),
startButton = createButton("start", start),
stopButton = createButton("stop", stop),
resetButton = createButton("reset", reset),
offset,
clock,
interval;
// default options
options = options || {};
options.delay = options.delay || 1;
var $elem = $(elem);
// append elements
$elem.empty()
.append(timer)
.append(startButton)
.append(stopButton)
.append(resetButton);
// initialize
reset();
// private functions
function createTimer() {
return $('<span class="swTime"></span>');
}
function createButton(action, handler) {
var a = $('<a class="' + action + ' ui-btn ui-btn-inline">' + action + '</a>');
a.on("click",function (event) {
handler();
event.preventDefault();
});
return a;
}
function start() {
if (!interval) {
offset = Date.now();
interval = setInterval(update, options.delay);
}
}
function stop() {
if (interval) {
clearInterval(interval);
interval = null;
}
}
function reset() {
clock = 0;
render();
}
function update() {
clock += delta();
render();
}
function render() {
timer.text(clock / 1000);
}
function delta() {
var now = Date.now(),
d = now - offset;
offset = now;
return d;
}
// public API
this.start = start;
this.stop = stop;
this.reset = reset;
};
$.fn.stopwatch = function(options) {
return this.each(function(idx, elem) {
new Stopwatch(elem, options);
});
};
})(jQuery);
$(document).on("pagecreate","#page1", function(){
$(".stopwatch").stopwatch();
});
DEMO

How to define a repeating function in a Javascript prototype?

I'm trying to define a Javascript class with a repeating function but I can't get it to work:
var Repeater = function() {
this.init.apply(this, arguments);
};
Repeater.prototype = {
run: 0, // how many runs
interval: 5, // seconds
init: function() {
this.repeat();
},
repeat: function() {
console.log(++this.run);
setTimeout(this.repeat, this.interval * 1000);
}
};
var repeater = new Repeater();
How should this be done?
Try this code:
var Repeater = function() {
this.run = 0; // how many runs
this.interval = 5; // seconds
this.init.apply(this, arguments);
};
Repeater.prototype.init = function() {
this.repeat();
}
Repeater.prototype.repeat = function() {
var _this = this;
console.log(++this.run);
setTimeout(function () { _this.repeat() }, this.interval * 1000);
};
var repeater = new Repeater();
I've moved run and interval into constructor, because if you add this to prototype then this will be spread over all instances.
Your problem lies into seTimeout - in your code this timer set new scope for repeater and this was no longer pointing to Repeater instance but for Timeout instance. You need to cache this (I've called this cache _this) and call it into new function passed to setTimeout.
Try like that:
var Repeater = function() {
this.init.apply(this, arguments);
};
Repeater.prototype = {
run: 0, // how many runs
interval: 5, // seconds
init: function() {
this.repeat();
},
repeat: function() {
console.log(++this.run);
var that = this;
setTimeout(function() {that.repeat()}, this.interval * 1000);
}
};
var repeater = new Repeater();
You can read more on how this behaves in this question : How does the "this" keyword work?
Change your repeat function to use a closure in the setTimeout call like so:
repeat: function() {
var ctx = this;
console.log(++this.run);
setTimeout(function(){ctx.repeat()}, this.interval * 1000);
}
You need to set the context explicitly in these kinds of scenarios- that's what the ctx variable is for

Categories

Resources