I had asked this question already:
How do I get the current time in Elm?
And answered it by writing my own (now deprecated) variant of start-app:http://package.elm-lang.org/packages/z5h/time-app/1.0.1
Of course the Elm architecture has since changed, and my old way of doing things no longer works, because there are no signals or Time.timestamp
.
So....
Suppose I build an app with the standard update function signature:update : Msg -> Model -> (Model, Cmd Msg)
I'd like to timestamp my model with the time at update. One unacceptable almost-solution is to subscribe to Time.every
. Conceptually this is not what I want. This is updating the model with time and also separately updating model with messages.
What I want is to be able to write an update function with signature:updateWithTime : Msg -> Time -> Model -> (Model, Cmd Msg)
I started trying to solve this by adding some extra messages:Msg = ... When | NewTime Time
And creating a new command:timeCmd = perform (\x -> NewTime 0.0) NewTime Time.now
So in any action, I can fire off an extra command to retrieve the time. But this gets messy and out of hand quickly.
Any ideas on how I can clean this up?
One option without having to do the time fetch on every update path would be to wrap your Msg
in another message type that would fetch the time and then call your normal update
with the time. This is a modified version of http://elm-lang.org/examples/buttons that will update a timestamp on the model with every update.
import Html exposing (div, button, text)
import Html.App exposing (program)
import Html.Events exposing (onClick)
import Task
import Time exposing (Time)
main =
program { init = (Model 0 0, Cmd.none), view = view, update = update, subscriptions = (\_ -> Sub.none) }
type alias Model =
{ count: Int
, updateTime : Time
}
view model =
Html.App.map GetTimeAndThen (modelView model)
type Msg
= GetTimeAndThen ModelMsg
| GotTime ModelMsg Time
update msg model =
case msg of
GetTimeAndThen wrappedMsg ->
(model, Task.perform (\_ -> Debug.crash "") (GotTime wrappedMsg) Time.now)
GotTime wrappedMsg time ->
let
(newModel, cmd) = modelUpdate wrappedMsg time model
in
(newModel, Cmd.map GetTimeAndThen cmd)
type ModelMsg = Increment | Decrement
modelUpdate msg time model =
case msg of
Increment ->
({model | count = model.count + 1, updateTime = time}, Cmd.none)
Decrement ->
({model | count = model.count - 1, updateTime = time}, Cmd.none)
modelView model =
div []
[ button [ onClick Decrement ] [ text "-" ]
, div [] [ text (toString model.count) ]
, button [ onClick Increment ] [ text "+" ]
, div [] [ text (toString model.updateTime) ]
]
I've found what I believe to be a more elegant solution than the accepted answer. Instead of having two separate models, the GetTimeAndThen
message holds a handler that returns a message. The code feels much more natural and elm-like, and can be used in a more general fashion:
module Main exposing (..)
import Html exposing (div, button, text)
import Html.App as App
import Html.Events exposing (onClick)
import Task
import Time exposing (Time)
main =
App.program
{ init = ( Model 0 0, Cmd.none )
, view = view
, update = update
, subscriptions = (\_ -> Sub.none)
}
view model =
div []
[ button [ onClick decrement ] [ text "-" ]
, div [] [ text (toString model) ]
, button [ onClick increment ] [ text "+" ]
]
increment =
GetTimeAndThen (\time -> Increment time)
decrement =
GetTimeAndThen (\time -> Decrement time)
type Msg
= Increment Time
| Decrement Time
| GetTimeAndThen (Time -> Msg)
type alias Model =
{ count : Int, updateTime : Time }
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
GetTimeAndThen successHandler ->
( model, (Task.perform assertNeverHandler successHandler Time.now) )
Increment time ->
( { model | count = model.count + 1, updateTime = time }, Cmd.none )
Decrement time ->
( { model | count = model.count - 1, updateTime = time }, Cmd.none )
assertNeverHandler : a -> b
assertNeverHandler =
(\_ -> Debug.crash "This should never happen")
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With