timeelmelm-architecture

Elm, how to get current time and post it to server


According to this answer and its helpful Ellie I now have an idea of how we get the current time in Elm 0.18.

I want to get the current time and then post it in JSON to my server. I already have a POST working with a hardcoded timestamp, but I just don't see how to get the current time and then include it in the JSON I am POSTing.

My guess is that I need to chain a couple of commands (get current time, make a POST to server).

[I have also read another SO which shows how you can run a few commands in a row by directly calling the update function, which sounds like a nice idea but I am not sure if it is what I need for my situation.]

Experiment ([edit] perhaps more of a distraction than was intended)

In order to get my head around this I thought I would try to solve a similar problem that I can more easily set up in Ellie.

In the original Ellie the current time is gotten, updated into the model, and then the view function shows it.

In my version I wanted this to be a two-step process and therefore have grown the model to be a Maybe Float for the time and a String message. The view function shows the message string and a button -- the plan is that when the button is pressed it tells the runtime to 'go get the current time and then copy it across into the message slot'.

If I can solve this problem then I feel like I can solve my original problem.

My Ellie does not do this yet. When you press the button the time is gotten and is put into the time slot in the model, but I do not know how to tell the runtime to '...now copy that time across into the message slot'. The PutTimeInMessage message is in place, but I don't know how to get it to run after the GetCurrentTime message/command.

Any suggestions?

Here is my code so far (it compiles), which you can run here in Ellie:

module Main exposing (..)

import Html exposing (..)
import Html.Events exposing (..)
import Time exposing (Time)
import Date
import Task


type alias Model =
    { time : Maybe Float
    , message : String
    }


type Msg
    = OnTime Time
    | GetCurrentTime
    | PutTimeInMessage


update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        OnTime t ->
            ( { model | time = Just t }, Cmd.none )

        GetCurrentTime ->
            ( model, getTime )

        PutTimeInMessage ->
            case model.time of
                Nothing ->
                    ( model, Cmd.none )

                Just t ->
                    ( { model | message = toString t }, Cmd.none )


view : Model -> Html Msg
view model =
    div []
        [ div []
            [ button [ onClick GetCurrentTime ] [ Html.text "Get now time." ]
            ]
        , model.message |> Html.text
        ]


getTime : Cmd Msg
getTime =
    Time.now
        |> Task.perform OnTime


main =
    Html.program
        { init = ( Model Nothing "Empty message.", Cmd.none )
        , update = update
        , view = view
        , subscriptions = always Sub.none
        }

Solution

  • I came across an SO which explained how to do a sequence of Http requests with Task.andThen. Since I can see the type of Time.now is a Task I figured that I could adapt that example for my purposes if I use Http.toTask.

    Below is the solution I came up with and here it is in Ellie:

    module Main exposing (..)
    
    import Html exposing (..)
    import Html.Events exposing (..)
    import Http
    import Json.Decode as JD
    import Json.Encode as JE
    import Task
    import Time
    
    
    type alias Model =
        { url : String
        }
    
    
    type Msg
        = PostTimeToServer
        | PostDone (Result Http.Error String)
    
    
    update : Msg -> Model -> ( Model, Cmd Msg )
    update msg model =
        case msg of
            PostTimeToServer ->
                ( model, postTimeToServer model.url )
    
            PostDone _ ->
                ( model, Cmd.none )
    
    
    view : Model -> Html Msg
    view model =
        div []
            [ div []
                [ button [ onClick PostTimeToServer ] [ Html.text "POST the current time." ]
                ]
            ]
    
    
    postTimeToServer : String -> Cmd Msg
    postTimeToServer url =
        let
            getTime =
                Time.now
    
            postTime t =
                JD.string
                    |> Http.post url (JE.float t |> Http.jsonBody)
                    |> Http.toTask
    
            request =
                getTime                                            <<-- Here is
                    |> Task.andThen postTime                       <<-- the key bit.
        in
            Task.attempt PostDone request
    
    
    main =
        Html.program
            { init = ( Model "url_here", Cmd.none )
            , update = update
            , view = view
            , subscriptions = always Sub.none
            }