How to make a timer stop and start on button press

200 views
Skip to first unread message

elmbran...@gmail.com

unread,
Feb 12, 2016, 2:28:58 AM2/12/16
to Elm Discuss
Here's the code I have so far:
  1 module SuperTimer where
  2
  3 import Html exposing (..)
  4 import Time
  5
  6 view : Int -> Html.Html
  7 view count =
  8     div []
  9     [ div [] [ text (toString count) ]
 10     , button [] [ text "Start Timer" ]
 11     ]
 12
 13 timer_signal : Signal Int
 14 timer_signal =
 15     Signal.foldp (\_ state -> state + 1) 0 (Time.every Time.second)
 16
 17 main : Signal.Signal Html.Html
 18 main =
 19     Signal.map view timer_signal
 
 What I'd like to do is to also have a signal for clicking the button. This tutorial http://yang-wei.github.io/blog/2016/02/04/a-step-to-step-guide-to-elm-signal/ shows how to merge two signals together, but, as I understand it, that would mean that I could not distinguish between signals.
 
 I wouldn't be able to tell if a signal was from time or from a click, which I need to be able to do in order to turn the timer on and off based on user input.

Alexey Shamrin

unread,
Feb 12, 2016, 8:21:30 AM2/12/16
to Elm Discuss
You could distinguish between two signals by creating so called "union type" or "tagged union":

type Action = Tick | Toggle | NoOp

Timer will create Tick values, button will create Toggle values, NoOp is here because Elm signals require some initial value.

So now timer signal would look like this:

ticks = Signal.map (\_ -> Tick) (Time.every Time.second)

And, as you've correctly noticed we would then merge two signals with Signal.merge:

actions = Signal.merge clicks ticks

How to get clicks, you would ask? Mailbox will help us.. Mailbox gives us a signal from address. And address is what Html.Events.onClick can throw stuff at:

mailbox = Signal.mailbox NoOp

clicks : Signal Action
clicks = mailbox.signal

Our button would now be able to produce those clicks:

import Html.Events exposing (onClick)

button [ onClick address Toggle ] [ … ]

Now we want to model the problem a little better than with just a single integer. We need to track if our timer is running or not:

type alias Model = { running: Bool, count: Int}

At the beginning our timer would be at "running" state, with 0 count:

model : Model
model = {running = True, count = 0}

Now we can call foldp to get current model signal from actions:

model_signal : Signal Model
model_signal = Signal.foldp update model actions

update is a more complicated version of your (\_ state -> state + 1). Compared to your version it takes an addtional argument - action (which is Tick, Toggle or NoOp):

update : Action -> Model -> Model

I suggest you try writing `update` and a new `view` functions yourself. Or you can look at the gist with a complete solution.

Essentially we've used Elm Architecture above, but without the start-app package.

Good luck!

P.S. http://elm-lang.org/try is surprisingly nice way to write and test short snippets like this one.

Alexey

d13

unread,
Feb 12, 2016, 8:39:53 AM2/12/16
to Elm Discuss
You might also be interested in looking at this Stopwatch example:

Alexey Shamrin

unread,
Feb 12, 2016, 8:46:04 AM2/12/16
to Elm Discuss
Correction:

button [ onClick address Toggle ] [ … ]

button [ onClick mailbox.address Toggle ] [ … ]

Max Goldstein

unread,
Feb 12, 2016, 9:42:56 AM2/12/16
to Elm Discuss
Alexey is definitely on the right path, but the timer always adds one second at a time. For more precision, we need Time.fpsWhen. I've forked the gist here, have a look at the Revisions tab to see what I changed.

Alexey Shamrin

unread,
Feb 14, 2016, 11:15:02 AM2/14/16
to Elm Discuss
Thank you! Your changes make a lot of sense. I've merged them to my original gist, with some minor cleanup. Check Revisions tab for details.

elmbran...@gmail.com

unread,
Feb 20, 2016, 10:08:02 PM2/20/16
to Elm Discuss
Thanks so much everybody, but after understanding dawned, I decided to use StartApp: https://github.com/NerdcoreSteve/SuperTimer/blob/master/SuperTimer.elm
Reply all
Reply to author
Forward
0 new messages