Commands
The init function returns an initial model, and the update function processes a message and returns a new model:
type Model = { TimerOn: bool }
type Message =
| TimerToggled of bool
let init () = { TimerOn = false }
let update msg model =
match msg with
| TimerToggled on -> { model with TimerOn = on }Commands
A command (type Cmd<'msg>) is a callback that can dispatch messages, i.e. gets access to dispatch when run.
Commands can be used for event subscriptions to callback, implement timers and so on. They can also be returned with the model to queue up long running operations such as network calls.
Commands are often asynchronous and nearly always dispatch messages. For example, the simplest way to make a command is Cmd.ofAsyncMsg which triggers a message dispatch when an async completes:
let timerCmd =
async { do! Async.Sleep 200
return TimedTick }
|> Cmd.ofAsyncMsgTriggering Commands on Initialization
The init function may trigger commands, e.g. initial database requests. This is permitted when using Program.mkProgram. For example here is a pattern to get an initial balance on startup:
Triggering Commands as Messages are Processed
The update function may trigger commands such as timers. This is permitted when using Program.mkProgram. For example, here is one pattern for a timer loop that can be turned on/off:
Triggering Commands from External Events
You can also set up global subscriptions, which are events sent from outside the view or the dispatch loop. For example, dispatching ClockMsg messages on a global timer:
Likewise, the general pattern to subscribe to external event sources is as follows:
Everything that wants access to dispatch must be mentioned in the composition of the overall app, or as part of a command produced as a result of processing a message, or in the view.
Threading and Long-running Operations
The rules:
updategets run on the UI thread.dispatchcan be called from any thread. The message will be processed byupdateon the UI thread.viewgets called on the UI thread. In the future an option may be added to offload theviewfunction automatically.
When handling any long running operation, the operation should initiate it’s thing and dispatch a message when done. If necessary, explicitly off-load and then dispatch at the end, e.g.
Optional commands
There might be cases where before a message is sent, you need to check if you want to send it (e.g. check user’s preferences, ask user’s permission, …)
Fabulous has 2 helper functions for this:
Cmd.ofMsgOption
Cmd.ofAsyncMsgOption
Web requests in a command
Sometimes it is needed to make some web requests. Which tool you use here does not matter. For example you could use FSharp.Data to make HttpRequests. These are the steps that you have to do, to make it work:
Create a case in the message type for a successful and failure webrequests
Implement the Command and return the correct message
Call the Command from update e.g. when a button is clicked
Create your view as you need
Platform-specific dispatch
Some platform-specific features (like deep linking, memory warnings, …) are not available in Xamarin.Forms, and need you to implement them in the corresponding app projet. In this case, you might want to dispatch a message from the app project to Fabulous to start a shared logic between platforms (to warn user, …).
To allow for this kind of use case, the dispatch function is exposed as a Dispatch(msg)method by the ProgramRunner. By default this runner is not accessible, but you can make a read-only property to let apps access it.
Once done, you can access it in the app project
Android
iOS
Last updated