I am developing a basic time tracking chrome extension and have run into an issue integrating FlipClock.js (a jQuery plugin) with Knockout.
The goal is to have a foreach binding template the relevant HTML and instantiate a FlipClock; up to a total of four FlipClocks. Currently, I'm getting the following error from FlipClock: 'Trying to start timer when countdown already at 0'. My guess is that this is short for "Knockout is destroying your flipclock when the viewmodel updates."
My question is this: Is there a way to prevent the flipclocks from being updated with knockouts viewmodel?
I've pasted in the relevant code below.
JS:
function Timer (name) {
//Declaring Observables, etc.
var timer = this;
timer.name = ko.observable(name);
timer.format = function () {
return name.replace(/ /g, '');
};
timer.key = (timer.format()+ Math.floor((Math.random()*1000000000000)+1));
timer.clock = $('#' + timer.key + '').FlipClock({countdown: false, autoStart: true, ClockFace: 'HourlyCounter'});
timer.started = ko.observable(false);
timer.running = ko.observable(false);
timer.stopped = ko.observable(false);
timer.reset = ko.observable(false);
timer.cleared = ko.observable(false);
timer.value = timer.clock.getTime();
timer.start = timer.clock.start();
timer.stop = timer.clock.stop();
timer.reset = timer.clock.reset();
//Time Tracker Viewmodel
function ViewModel () {
var self = this;
self.timers = ko.observableArray([]);
//New Timer Method
self.createTimer = function () {
var name = $('.projectInput').val(),
css = name.replace(/ /g, '');
self.timers.push(new Timer(name));
$('.projectInput').val("");
}
}
//Applying bindings to view model
ko.applyBindings (new ViewModel());
});
HTML:
<div class="timers" data-bind="foreach: timers()">
<div class="instance">
<div class="timerUI">
<div class="title" data-bind="text: name()"></div>
<div class="controls">
<div class="start button" data-bind="click: clock.start()"></div>
<div class="stop button" data-bind="click: clock.stop()"></div>
<div class="reset button" data-bind="click: clock.reset()"></div>
<div class="clear button" data-bind="click: $parent.clearTimer"></div>
</div>
</div>
<div class="time" data-bind="attr: {id: key}"></div>
</div>
</div>
Your Timer
constructor is trying to call methods of your FlipClock before it's been instantiated. Move the line
timer.clock = $('#' + timer.key + '').FlipClock({countdown: false, autoStart: true, ClockFace: 'HourlyCounter'});
before any code that references timer.clock
(this should have caused error messages; make sure you have your console and debugger open when you get mysterious misbehavior like this).
UPDATE
<div class="start button" data-bind="click: clock.start()"></div>
<div class="stop button" data-bind="click: clock.stop()"></div>
<div class="reset button" data-bind="click: clock.reset()"></div>
The start
, stop
and reset
methods are being called at the time your bindings are evaluated (which means they're being called in rapid succession), not when the buttons are clicked. You need to take the parentheses off so that what's being passed is a function reference, not the result of a function invocation.