7.8 KiB
Pages
In elm-spa, every URL connects to a single page. Let's take a closer look at the homepage created with the elm-spa new
command:
module Pages.Home_ exposing (view)
import Html
import View exposing (View)
view : View msg
view =
{ title = "Homepage"
, body = [ Html.text "Hello, world!" ]
}
This homepage renders "Homepage" in the browser tab, and "Hello, world!" onto the page.
Because the file is named Home_.elm
, elm-spa knows this is the homepage. Visiting http://localhost:1234
in a web browser will render this view.
A view
function is perfect when all you need is to render some HTML on the screen. But many web pages in the real world do more interesting things!
Upgrading "Hello, world!"
Let's start by adding a new page
function:
module Pages.Home_ exposing (page)
import Html
import Page exposing (Page)
import Request exposing (Request)
import Shared
import View exposing (View)
page : Shared.Model -> Request -> Page
page shared req =
Page.static
{ view = view
}
view : View msg
view =
{ title = "Homepage"
, body = [ Html.text "Hello, world!" ]
}
We haven't changed the original code much- except we've added a new page
function that:
- Accepts 2 inputs:
Shared.Model
andRequest
- Returns a
Page
value - Has been exposed at the top of the file.
Exposing
page
from this module lets elm-spa know we want to usepage
instead of the plainview
function from before.
The view
function from before is now passed into page
. In the web browser, we still see "Hello, world!". However, this page now has access to two new bits of information!
Shared.Model
is our global application state, which might contain the signed-in user, settings, or other things that should persist as we move from one page to another.Request
is a record with access to the current route, query parameters, and any other information about the current URL.
You can rely on the fact that the page
will always be passed the latest Shared.Model
and Request
value. If we want either of these values to be available in our view
function, we pass them in like so:
page : Shared.Model -> Request -> Page
page shared req =
Page.static
{ view = view req -- passing in req here!
}
Now our view
function can read the current URL
value:
view : Request -> View msg
view req =
{ title = "Homepage"
, body =
[ Html.text ("Hello, " ++ req.url.host ++ "!")
]
}
Now the browser should display "Hello, localhost!"
Beyond static pages
You might have noticed Page.static
earlier in our page function. This is one of the built in page types that is built-in to elm-spa.
The rest of this section will introduce you to the other page types exposed by the Page
module, so you know which one to reach for.
Always choose the simplest page type for the job– and reach for the more advanced ones when your page really needs the extra features!
- Page.static - for pages that only render a view.
- Page.sandbox - for pages that need to keep track of state.
- Page.element - for pages that send HTTP requests or continually listen for events from the browser or user.
- Page.advanced - for pages that need to sign in a user or work with other details that should persist between page navigation.
Page.static
elm-spa add /example static
This was the page type we looked at above. It is perfect for pages that render static HTML, but might need access to the Shared.Model
or Request
values.
module Pages.Example exposing (page)
page : Shared.Model -> Request -> Page
page shared req =
Page.static
{ view = view
}
view : View msg
Page.sandbox
elm-spa add /example sandbox
This is the first page type that introduces the Elm architecture, which uses Model
to store the current page state and Msg
to define what actions users can take on this page.
It's time to upgrade to Page.sandbox
when you need to track state on the page. Here are a few examples of things you'd store in page state:
- The current slide of a carousel
- The selected tab section to view
- The open / close state of a modal
All these examples require us to be able to initialize a Model
, update it based on Msg
values sent from the view.
If you are new to the Elm architecture, be sure to visit guide.elm-lang.org. We'll be using it for all the upcoming page types!
module Pages.Example exposing (Model, Msg, page)
page : Shared.Model -> Request -> Page.With Model Msg
page shared req =
Page.sandbox
{ init = init
, update = update
, view = view
}
init : Model
update : Msg -> Model -> Model
view : Model -> View Msg
Our
page
function now returnsPage.With Model Msg
instead ofPage
. This is because our page is now stateful.
( Inspired by Browser.sandbox )
Page.element
elm-spa add /example element
When you are ready to send HTTP requests or subscribe to events like keyboard presses, mouse move, or incoming data from JS– upgrade to Page.element
.
This is the same as Page.sandbox
, but introduces Cmd Msg
and Sub Msg
to handle side effects.
module Pages.Example exposing (Model, Msg, page)
page : Shared.Model -> Request -> Page.With Model Msg
page shared req =
Page.element
{ init = init
, update = update
, view = view
, subscriptions = subscriptions
}
init : ( Model, Cmd Msg )
update : Msg -> Model -> ( Model, Cmd Msg )
view : Model -> View Msg
subscriptions : Model -> Sub Msg
( Inspired by Browser.element )
Page.advanced
elm-spa add /example advanced
For many applications, Page.element
is all you need to store a Model
, handle Msg
values, and work with side-effects.
Some Elm users prefer sending global updates directly from their pages, so we've included this Page.advanced
page type.
Using a custom Effect
module, users are able to send Cmd Msg
value via Effect.fromCmd
or Shared.Msg
values with Effect.fromSharedMsg
.
module Pages.Example exposing (Model, Msg, page)
page : Shared.Model -> Request -> Page.With Model Msg
page shared req =
Page.advanced
{ init = init
, update = update
, view = view
, subscriptions = subscriptions
}
init : ( Model, Effect Msg )
update : Msg -> Model -> ( Model, Effect Msg )
view : Model -> View Msg
subscriptions : Model -> Sub Msg
This Effect Msg
value also allows support for folks using elm-program-test, which requires users to define their own custom type on top of Cmd Msg
. More about that in the testing guide
Page.protected
Each of those four page types also have a protected version. This means pages are guaranteed to receive a User
or redirect if no user is signed in.
-- not protected
Page.sandbox
{ init : Model
, update : Msg -> Model -> Model
, view : Model -> View Msg
}
-- protected
Page.protected.sandbox
{ init : User -> Model
, update : User -> Msg -> Model -> Model
, view : User -> Model -> View Msg
}
When you are ready for user authentication, you can learn more about using Page.protected
in the authentication guide.
Next up: Requests