Layouts
Overview
A layout is UI that is shared between multiple routes. On navigation, layouts preserve state and remain interactive.
Adding layouts
You can create a layout by running the following command:
dotnet elmish-land add layout "/User"
You need to manually add the new layout to your project file by using an IDE or by adding the following line to an ItemGroup in the project file ./MyProject.fsproj
:
<Compile Include="src/Pages/User/Layout.fs" />
The "add layout" command generates src/Pages/User/Layout.fs with the following content:
module MyProject.Pages.User.Layout
open Feliz
open ElmishLand
open MyProject.Shared
type Props = unit
type Model = unit
type Msg = | NoOp
let init () =
(),
Command.none
let update (msg: Msg) (model: Model) =
match msg with
| NoOp -> model, Command.none
let routeChanged (model: Model) =
model, Command.none
let view (_model: Model) (content: Feliz.ReactElement) (_dispatch: Msg -> unit) =
Html.div [
Html.text "User Layout"
Html.div [
content
]
]
let layout (_props: Props) (_route: Route) (_shared: SharedModel) =
Layout.from init update routeChanged view
Layouts have the same structure as pages except for the Props
type and the layout
and routeChanged
functions.
Understanding layouts
layout
The layout
function is our starting point for the layout. From this function we need to call the Layout.from
function to setup our layout.
let layout (_props: Props) (_route: Route) (_shared: SharedModel) =
Layout.from init update routeChanged view
Props
The Props
type is used to enable pages to pass initial data to a layout.
The Props
is passed to the layout
function so it's available to init
, update
, routeChanged
and view.
type Props = unit
routeChanged
The routeChanged
function is called every time the user navigates with commands or links.
This function is NOT called the first time the layout loads. Use the init
function for this.
let routeChanged (model: Model) =
model, Command.none
If you need to access the current route from this function, you can pass it from the layout
function.
let routeChanged (route: Route) (model: Model) =
model, Command.none
let layout (_props: Props) (route: Route) (_shared: SharedModel) =
Layout.from init update (routeChanged route) view
Layout selection
All pages that are in a sub-folder of a layout will use that layout.
In the following example :
MyProject
└── src
└── Pages
├── Layout.fs
└── User
└── Page.fs
the page /src/Pages/User/Page.fs
will use the layout /src/Pages/Layout.fs
.
Root Layout
The root layout is defined at the top level of the /src/Pages
folder and is automatically
created when you initialize a new Elmish Land project.
Sending messages to and from pages
Sending messages to pages
When you need to send messages from a layout to it's current page you will use the Command.ofMsg
function from the layout
and handle the LayoutMsg
on the page. The message will be sent both to the layout and the page.
// A Layout.fs file
type Msg =
| LoadUser of string
let routeChanged (_route: Route) (model: Model) =
model, Command.ofMsg (LoadUser "John Doe")
// A Page.fs file
let update (msg: Msg) (model: Model) =
match msg with
| LayoutMsg (Layout.LoadUser name) -> model, Command.none
In the Commands section, you'll learn more about commands.
Sending messages from pages
When you need to send messages from a page to it's layout you will use the Command.ofLayout
function.
// A Page.fs file
let update (msg: Msg) (model: Model) =
match msg with
| LayoutMsg _ -> model, Command.none
| SignOut -> model, Command.ofLayout Layout.SignOutClicked
In the Commands section, you'll learn more about commands.