Command Module
The Command module provides functions for performing side effects in your Elmish Land application. Commands are used to trigger asynchronous operations, send messages, navigate between pages, and communicate between components.
Where is this defined?
The Command module is automatically generated during the build process:
- Generated Location:
.elmish-land/Base/Command.fs(in your project) - Generated By: Running
elmish-land restore,elmish-land serverandelmish-land buildcommands - Namespace:
ElmishLand
The Command type is implemented as a discriminated union that wraps Elmish's standard Cmd<'msg> type, adding support for routing messages to Shared state and parent Layouts.
The .elmish-land/Base/Command.fs file is auto-generated and should not be edited directly. Any changes will be overwritten on the next build.
Command.none
Returns a command that does nothing. Use this when your init or update functions doesn't need to perform any side effects.
Signature
Command.none : Command<'msg, 'sharedMsg, 'layoutMsg>
Example
let update msg model =
match msg with
| Increment ->
{ model with Count = model.Count + 1 },
Command.none
Command.ofMsg
Creates a command that dispatches a message immediately after the current update completes.
Signature
Command.ofMsg : 'msg -> Command<'msg, 'sharedMsg, 'layoutMsg>
Parameters
msg- The message to dispatch
Example
let init () =
{ Users = []; Loading = true },
Command.ofMsg LoadUsers
Command.batch
Combines multiple commands into a single command that executes all of them.
Signature
Command.batch : Command<'msg, 'sharedMsg, 'layoutMsg> list -> Command<'msg, 'sharedMsg, 'layoutMsg>
Parameters
commands- List of commands to execute
Example
let init () =
model,
Command.batch [
Command.ofMsg LoadUsers
Command.ofMsg LoadSettings
Command.ofShared CheckAuthStatus
]
Command.ofPromise
Executes a promise and dispatches a message with the result. Throws an exception if the promise fails.
Signature
Command.ofPromise :
task: ('arg -> JS.Promise<'result>)
-> arg: 'arg
-> ofSuccess: ('result -> 'msg)
-> Command<'msg, 'sharedMsg, 'layoutMsg>
Parameters
task- Function that returns a promisearg- Argument to pass to the task functionofSuccess- Function that converts the result into a message
Example
type Msg =
| LoadUsers
| UsersLoaded of User list
let fetchUsers () =
promise {
let! response = Fetch.fetch "/api/users"
return! response.json<User list>()
}
let update msg model =
match msg with
| LoadUsers ->
{ model with Loading = true },
Command.ofPromise fetchUsers () UsersLoaded
| UsersLoaded users ->
{ model with Users = users; Loading = false },
Command.none
Command.tryOfPromise
Executes a promise and dispatches different messages based on success or failure. Does not throw exceptions.
Signature
Command.tryOfPromise :
task: ('arg -> JS.Promise<'result>)
-> arg: 'arg
-> ofSuccess: ('result -> 'msg)
-> ofError: (exn -> 'msg)
-> Command<'msg, 'sharedMsg, 'layoutMsg>
Parameters
task- Function that returns a promisearg- Argument to pass to the task functionofSuccess- Function that converts successful results into a messageofError- Function that converts errors into a message
Example
type Msg =
| LoadUsers
| UsersLoaded of User list
| LoadError of exn
let fetchUsers () =
promise {
let! response = Fetch.fetch "/api/users"
return! response.json<User list>()
}
let update msg model =
match msg with
| LoadUsers ->
{ model with Loading = true; Error = None },
Command.tryOfPromise
fetchUsers
()
UsersLoaded
LoadError
| UsersLoaded users ->
{ model with Users = users; Loading = false },
Command.none
| LoadError error ->
{ model with Loading = false; Error = Some error },
Command.none
Command.ofAsync
Executes an F# async workflow and dispatches a message with the result. Throws an exception if the async operation fails.
This function work seamlessly with Fable.Remoting for type-safe server communication and any F# libraries that return Async<'T> types.
Signature
Command.ofAsync :
task: ('arg -> Async<'result>)
-> arg: 'arg
-> ofSuccess: ('result -> 'msg)
-> Command<'msg, 'sharedMsg, 'layoutMsg>
Parameters
task- Function that returns an async workflowarg- Argument to pass to the task functionofSuccess- Function that converts the result into a message
Example, Fable.Remoting
// Shared API definition
type IUserApi = {
getUsers: unit -> Async<User list>
}
// Client code
type Msg =
| LoadUsers
| UsersLoaded of User list
let userApi =
Remoting.createApi()
|> Remoting.buildProxy<IUserApi>
let update msg model =
match msg with
| LoadUsers ->
{ model with Loading = true },
Command.ofAsync userApi.getUsers () UsersLoaded
| UsersLoaded users ->
{ model with Users = users; Loading = false },
Command.none
Command.tryOfAsync
Executes an F# async workflow and dispatches different messages based on success or failure. Does not throw exceptions.
This function work seamlessly with Fable.Remoting for type-safe server communication and any F# libraries that return Async<'T> types.
Signature
Command.tryOfAsync :
task: ('arg -> Async<'result>)
-> arg: 'arg
-> ofSuccess: ('result -> 'msg)
-> ofError: (exn -> 'msg)
-> Command<'msg, 'sharedMsg, 'layoutMsg>
Parameters
task- Function that returns an async workflowarg- Argument to pass to the task functionofSuccess- Function that converts successful results into a messageofError- Function that converts errors into a message
When to Use
Use Command.tryOfAsync when you need to handle errors explicitly in your application. This is especially useful with:
- Fable.Remoting for type-safe server communication
- F# libraries that return
Async<'T>types - .NET async APIs
Use Command.tryOfPromise when working with JavaScript libraries and browser APIs.
Example, Fable.Remoting with Error Handling
// Shared API definition
type IWeatherApi = {
getWeatherForecast: unit -> Async<WeatherForecast list>
}
// Client code
type Msg =
| LoadWeather
| WeatherLoaded of WeatherForecast list
| LoadError of exn
let weatherApi =
Remoting.createApi()
|> Remoting.buildProxy<IWeatherApi>
let update msg model =
match msg with
| LoadWeather ->
{ model with Loading = true; Error = None },
Command.tryOfAsync
weatherApi.getWeatherForecast
()
WeatherLoaded
LoadError
| WeatherLoaded forecasts ->
{ model with Forecasts = forecasts; Loading = false },
Command.none
| LoadError error ->
{ model with Loading = false; Error = Some error },
Command.none
Command.ofShared
Sends a message to the Shared module, allowing pages and layouts to update shared state.
Signature
Command.ofShared : 'sharedMsg -> Command<'msg, 'sharedMsg, 'layoutMsg>
Parameters
sharedMsg- The message to send to the Shared module
Example
// In a Page.fs file
type Msg =
| SignOutClicked
| LayoutMsg of Layout.Msg
let update msg model =
match msg with
| SignOutClicked ->
model,
Command.ofShared SharedMsg.SignOut
| LayoutMsg _ ->
model, Command.none
See Shared State for more details.
Command.ofLayout
Sends a message from a page to its layout.
Signature
Command.ofLayout : 'layoutMsg -> Command<'msg, 'sharedMsg, 'layoutMsg>
Parameters
layoutMsg- The message to send to the layout
Example
// In a Page.fs file
type Msg =
| UpdateTitle of string
| LayoutMsg of Layout.Msg
let update msg model =
match msg with
| UpdateTitle title ->
model,
Command.ofLayout (Layout.SetTitle title)
| LayoutMsg _ ->
model, Command.none
See Layouts - Sending messages for more details.
Command.navigate
Navigates to a different page in your application.
Signature
Command.navigate : Route -> Command<'msg, 'sharedMsg, 'layoutMsg>
Parameters
route- The route to navigate to (from the generatedRoutediscriminated union)
Example (Static Route)
type Msg =
| LoginSuccessful of User
| LayoutMsg of Layout.Msg
let update msg model =
match msg with
| LoginSuccessful user ->
model,
Command.navigate (Route.Dashboard ())
| LayoutMsg _ ->
model, Command.none
Example (Dynamic Route)
Navigate to routes with parameters:
// Navigate to /#users/123
Command.navigate (Route.UsersId { Id = "123"; Query = [] })
// Navigate to /#blog/my-post?highlight=true
Command.navigate (
Route.BlogSlug {
Slug = "my-post"
Query = [ "highlight", "true" ]
}
)
See Linking and Navigating for more details.
Command.ofCmd
Converts an Elmish Cmd<'msg> to an Elmish Land Command<'msg, 'sharedMsg, 'layoutMsg>. This is useful when integrating with libraries that return standard Elmish commands.
Signature
Command.ofCmd : Cmd<'msg> -> Command<'msg, 'sharedMsg, 'layoutMsg>
Parameters
cmd- The Elmish command to convert
Example
open Elmish
let update msg model =
match msg with
| UseElmishLibrary ->
let elmishCmd = SomeLibrary.createCommand ()
model,
Command.ofCmd elmishCmd
Best Practices
Prefer tryOfPromise/tryOfAsync over ofPromise/ofAsync
Always use Command.tryOfPromise or Command.tryOfAsync instead of their non-try counterparts in production code to handle errors gracefully:
// Good - Handles errors (Promise-based)
Command.tryOfPromise fetchData () DataLoaded LoadError
// Good - Handles errors (Async-based)
Command.tryOfAsync fetchData () DataLoaded LoadError
// Avoid - Errors will crash the application
Command.ofPromise fetchData () DataLoaded
Command.ofAsync fetchData () DataLoaded
Batch Related Commands
When you need to perform multiple side effects, use Command.batch to keep your code organized:
let init () =
model,
Command.batch [
Command.tryOfAsync loadUser () UserLoaded LoadError
Command.tryOfAsync loadSettings () SettingsLoaded LoadError
Command.ofShared CheckAuthStatus
]
Use Specific Message Types
Create specific message types for different command results rather than reusing generic ones:
// Good
type Msg =
| UsersLoaded of User list
| SettingsLoaded of Settings
| ProfileLoaded of Profile
// Avoid
type Msg =
| DataLoaded of obj
This improves type safety and makes your code more maintainable.