elmish


Working with external sources of events

Sometimes we have a source of events that doesn't depend on the current state of the model, like a timer. We can setup forwarding of those events to be processed by our update function like any other change.

Let's define our Model and Msg types. Model will hold the current state and Msg will tell us the nature of the change that we need to apply to the current state.

1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
open System


type Model =
    { current : DateTime }

type Msg =
    | Tick of DateTime

This time we'll define the "simple" version of init and update functions, that don't produce commands:

1: 
2: 
3: 
4: 
5: 
6: 
7: 
let init () =
    { current = DateTime.Now }

let update msg model =
    match msg with
    | Tick current ->
        { model with current = current }

Note that "simple" is not a requirement and is just a matter of convenience for the purpose of the example!

Now lets define our timer subscription:

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
open Elmish
open Browser

let timer initial =
    let sub dispatch =
        window.setInterval(fun _ ->
            dispatch (Tick DateTime.Now)
            , 1000) |> ignore
    Cmd.ofSub sub

Program.mkSimple init update (fun model _ -> printf "%A\n" model)
|> Program.withSubscription timer
|> Program.run

In this example program initialization will call our subscriber (once) with inital Model state, passing the dispatch function to be called whenever an event occurs. However, any time you need to issue a message (for example from a callback) you can use Cmd.ofSub.

Aggregating multiple subscribers

If you need to aggregate multiple subscriptions follow the same pattern as when implementing init, update, and view functions - delegate to child components to setup their own subscriptions.

For example:

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
24: 
25: 
26: 
27: 
28: 
29: 
30: 
31: 
32: 
33: 
34: 
35: 
36: 
37: 
38: 
39: 
40: 
41: 
42: 
43: 
44: 
45: 
46: 
47: 
48: 
49: 
50: 
51: 
52: 
53: 
54: 
55: 
56: 
57: 
58: 
59: 
60: 
61: 
62: 
63: 
64: 
65: 
module Second =
    type Msg =
        | Second of int

    type Model = int

    let subscribe initial =
        let sub dispatch =
            window.setInterval(fun _ ->
                dispatch (Second DateTime.Now.Second)
                , 1000) |> ignore
        Cmd.ofSub sub

    let init () =
        0

    let update (Second seconds) model =
        seconds

module Hour =
    type Msg =
        | Hour of int

    type Model = int

    let init () =
        0

    let update (Hour hours) model =
        hours

    let subscribe initial =
        let sub dispatch =
            window.setInterval(fun _ ->
                dispatch (Hour DateTime.Now.Hour)
                , 1000*60) |> ignore
        Cmd.ofSub sub

module App =
    type Model =
        { seconds : Second.Model
          hours : Hour.Model }

    type Msg =
        | SecondMsg of Second.Msg
        | HourMsg of Hour.Msg

    let init () =
        { seconds = Second.init()
          hours = Hour.init() }

    let update msg model =
        match msg with
        | HourMsg msg ->
            { model with hours = Hour.update msg model.hours }
        | SecondMsg msg ->
            { model with seconds = Second.update msg model.seconds }

    let subscription model =
        Cmd.batch [ Cmd.map HourMsg (Hour.subscribe model.hours)
                    Cmd.map SecondMsg (Second.subscribe model.seconds) ]

    Program.mkSimple init update (fun model _ -> printf "%A\n" model)
    |> Program.withSubscription subscription
    |> Program.run
namespace System
type Model =
  {current: DateTime;}
Model.current: DateTime
Multiple items
type DateTime =
  struct
    new : ticks:int64 -> DateTime + 10 overloads
    member Add : value:TimeSpan -> DateTime
    member AddDays : value:float -> DateTime
    member AddHours : value:float -> DateTime
    member AddMilliseconds : value:float -> DateTime
    member AddMinutes : value:float -> DateTime
    member AddMonths : months:int -> DateTime
    member AddSeconds : value:float -> DateTime
    member AddTicks : value:int64 -> DateTime
    member AddYears : value:int -> DateTime
    ...
  end

--------------------
DateTime ()
   (+0 other overloads)
DateTime(ticks: int64) : DateTime
   (+0 other overloads)
DateTime(ticks: int64, kind: DateTimeKind) : DateTime
   (+0 other overloads)
DateTime(year: int, month: int, day: int) : DateTime
   (+0 other overloads)
DateTime(year: int, month: int, day: int, calendar: Globalization.Calendar) : DateTime
   (+0 other overloads)
DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int) : DateTime
   (+0 other overloads)
DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, kind: DateTimeKind) : DateTime
   (+0 other overloads)
DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, calendar: Globalization.Calendar) : DateTime
   (+0 other overloads)
DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, millisecond: int) : DateTime
   (+0 other overloads)
DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, millisecond: int, kind: DateTimeKind) : DateTime
   (+0 other overloads)
type Msg = | Tick of DateTime
union case Msg.Tick: DateTime -> Msg
val init : unit -> Model
property DateTime.Now: DateTime
val update : msg:Msg -> model:Model -> Model
val msg : Msg
val model : Model
val current : DateTime
val timer : initial:'a -> 'b
val initial : 'a
val sub : ('c -> unit)
val dispatch : 'c
val ignore : value:'T -> unit
val printf : format:Printf.TextWriterFormat<'T> -> 'T
type Msg = | Second of int
union case Msg.Second: int -> Msg
Multiple items
val int : value:'T -> int (requires member op_Explicit)

--------------------
type int = int32

--------------------
type int<'Measure> = int
type Model = int
val subscribe : initial:'a -> 'b
property DateTime.Second: int
val init : unit -> int
val update : Msg -> model:'a -> int
val seconds : int
val model : 'a
type Msg = | Hour of int
union case Msg.Hour: int -> Msg
val hours : int
property DateTime.Hour: int
module App

from Subscriptions
type Model =
  {seconds: Model;
   hours: Model;}
Model.seconds: Second.Model
module Second

from Subscriptions
Model.hours: Hour.Model
module Hour

from Subscriptions
type Msg =
  | SecondMsg of Msg
  | HourMsg of Msg
union case Msg.SecondMsg: Second.Msg -> Msg
union case Msg.HourMsg: Hour.Msg -> Msg
val msg : Hour.Msg
val update : Hour.Msg -> model:'a -> int
val msg : Second.Msg
val update : Second.Msg -> model:'a -> int
val subscription : model:'a -> 'b