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 Fable.Import.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;}

Full name: Subscriptions.Model
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

Full name: System.DateTime

--------------------
DateTime()
   (+0 other overloads)
DateTime(ticks: int64) : unit
   (+0 other overloads)
DateTime(ticks: int64, kind: DateTimeKind) : unit
   (+0 other overloads)
DateTime(year: int, month: int, day: int) : unit
   (+0 other overloads)
DateTime(year: int, month: int, day: int, calendar: Globalization.Calendar) : unit
   (+0 other overloads)
DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int) : unit
   (+0 other overloads)
DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, kind: DateTimeKind) : unit
   (+0 other overloads)
DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, calendar: Globalization.Calendar) : unit
   (+0 other overloads)
DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, millisecond: int) : unit
   (+0 other overloads)
DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, millisecond: int, kind: DateTimeKind) : unit
   (+0 other overloads)
type Msg = | Tick of DateTime

Full name: Subscriptions.Msg
union case Msg.Tick: DateTime -> Msg
val init : unit -> Model

Full name: Subscriptions.init
property DateTime.Now: DateTime
val update : msg:Msg -> model:Model -> Model

Full name: Subscriptions.update
val msg : Msg
val model : Model
val current : DateTime
namespace Elmish
val timer : initial:'a -> Cmd<'b>

Full name: Subscriptions.timer
val initial : 'a
val sub : ('c -> unit)
val dispatch : 'c
val ignore : value:'T -> unit

Full name: Microsoft.FSharp.Core.Operators.ignore
Multiple items
module Cmd

from Elmish

--------------------
type Cmd<'msg> = Sub<'msg> list

Full name: Elmish.Cmd<_>
val ofSub : sub:Sub<'msg> -> Cmd<'msg>

Full name: Elmish.Cmd.ofSub
Multiple items
module Program

from Elmish

--------------------
type Program<'arg,'model,'msg,'view> =
  {init: 'arg -> 'model * Cmd<'msg>;
   update: 'msg -> 'model -> 'model * Cmd<'msg>;
   subscribe: 'model -> Cmd<'msg>;
   view: 'model -> Dispatch<'msg> -> 'view;
   setState: 'model -> Dispatch<'msg> -> unit;
   onError: string * exn -> unit;}

Full name: Elmish.Program<_,_,_,_>
val mkSimple : init:('arg -> 'model) -> update:('msg -> 'model -> 'model) -> view:('model -> Dispatch<'msg> -> 'view) -> Program<'arg,'model,'msg,'view>

Full name: Elmish.Program.mkSimple
val printf : format:Printf.TextWriterFormat<'T> -> 'T

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.printf
val withSubscription : subscribe:('model -> Cmd<'msg>) -> program:Program<'arg,'model,'msg,'view> -> Program<'arg,'model,'msg,'view>

Full name: Elmish.Program.withSubscription
val run : program:Program<unit,'model,'msg,'view> -> unit

Full name: Elmish.Program.run
type Msg = | Second of int

Full name: Subscriptions.Second.Msg
union case Msg.Second: int -> Msg
Multiple items
val int : value:'T -> int (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.int

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

Full name: Microsoft.FSharp.Core.int

--------------------
type int<'Measure> = int

Full name: Microsoft.FSharp.Core.int<_>
type Model = int

Full name: Subscriptions.Second.Model
val subscribe : initial:'a -> Cmd<'b>

Full name: Subscriptions.Second.subscribe
property DateTime.Second: int
val init : unit -> int

Full name: Subscriptions.Second.init
val update : Msg -> model:'a -> int

Full name: Subscriptions.Second.update
val seconds : int
val model : 'a
type Msg = | Hour of int

Full name: Subscriptions.Hour.Msg
union case Msg.Hour: int -> Msg
type Model = int

Full name: Subscriptions.Hour.Model
val init : unit -> int

Full name: Subscriptions.Hour.init
val update : Msg -> model:'a -> int

Full name: Subscriptions.Hour.update
val hours : int
val subscribe : initial:'a -> Cmd<'b>

Full name: Subscriptions.Hour.subscribe
property DateTime.Hour: int
module App

from Subscriptions
type Model =
  {seconds: Model;
   hours: Model;}

Full name: Subscriptions.App.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

Full name: Subscriptions.App.Msg
union case Msg.SecondMsg: Second.Msg -> Msg
union case Msg.HourMsg: Hour.Msg -> Msg
val init : unit -> Model

Full name: Subscriptions.App.init
val update : msg:Msg -> model:Model -> Model

Full name: Subscriptions.App.update
val msg : Hour.Msg
val update : Hour.Msg -> model:'a -> int

Full name: Subscriptions.Hour.update
val msg : Second.Msg
val update : Second.Msg -> model:'a -> int

Full name: Subscriptions.Second.update
val subscription : model:Model -> Cmd<Msg>

Full name: Subscriptions.App.subscription
val batch : cmds:#seq<Cmd<'msg>> -> Cmd<'msg>

Full name: Elmish.Cmd.batch
val map : f:('a -> 'msg) -> cmd:Cmd<'a> -> Cmd<'msg>

Full name: Elmish.Cmd.map