If you have a Browser.application
that is getting large you may want pull some of the logic, such as the logic for a particular page, into its own module .
This is one common way of doing that.
”Nested TEA” is a way to “nest” the Elm architecture so a module can have its own model/view/update cycle,
with messages delegated to and from the top-level module.
Let’s say you have an application like this:
module Main exposing ( main )
import Browser exposing ( Document , UrlRequest (..))
import Browser . Navigation as Nav
import Html exposing ( Html )
import Html . Attributes as Attribute
import Html . Events as Event
import Url exposing ( Url )
, subscriptions = subscriptions
, onUrlRequest = UrlRequested
, onUrlChange = UrlChanged
pageFromUrl : Url -> Page
init : {} -> Url -> Nav .Key -> { model : Model , command : Cmd Msg }
= UrlRequested UrlRequest
update : Msg -> Model -> { model : Model , command : Cmd Msg }
UrlRequested urlRequest ->
Nav . pushUrl model . navKey (Url . toString url)
{ model = { model | page = pageFromUrl url }
{ model = { model | clicked = True }
view : Model -> Document Msg
[ Attribute . href " /clicker " ]
[ Html . text " Go click the button " ]
[ Event . onClick ButtonClicked ]
, body = [ Html . text " Can't find that url " ]
subscriptions : Model -> Sub Msg
Compile and run the application and you should see a link to a page with your button.
Clicking the button will change the text.
If we want to pull our Clicker
page into its own module, it could look like this (notice we created a src/Pages
directory for it to live in):
module Pages . Clicker exposing ( Model , Msg , init , update , view )
import Browser exposing ( Document )
import Html exposing ( Html )
import Html . Attributes as Attribute
import Html . Events as Event
update : Msg -> Model -> { model : Model , command : Cmd Msg }
{ model = { model | clicked = True }
view : Model -> Document Msg
[ Event . onClick ButtonClicked ]
Then we’d update our Main
module to delegate to that page’s init
, update
, and view
:
module Main exposing ( main )
import Browser exposing ( Document , UrlRequest (..))
import Browser . Navigation as Nav
import Html exposing ( Html )
import Html . Attributes as Attribute
import Html . Events as Event
import Url exposing ( Url )
, subscriptions = subscriptions
, onUrlRequest = UrlRequested
, onUrlChange = UrlChanged
| Clicker Pages . Clicker .Model
pageFromUrl : Url -> Page
" /clicker " -> Clicker Pages . Clicker . init
init : {} -> Url -> Nav .Key -> { model : Model , command : Cmd Msg }
= UrlRequested UrlRequest
| ClickerMsg Pages . Clicker .Msg
update : Msg -> Model -> { model : Model , command : Cmd Msg }
UrlRequested urlRequest ->
Nav . pushUrl model . navKey (Url . toString url)
{ model = { model | page = pageFromUrl url }
{ model = { model | clicked = True }
{ model = newClickerModel, command = clickerCmd } =
Pages . Clicker . update clickerMsg clickerModel
{ model = { model | page = Clicker newClickerModel }
, command = Cmd . map ClickerMsg clickerCmd
-- Should never receive this message on other pages.
view : Model -> Document Msg
[ Attribute . href " /clicker " ]
[ Html . text " Go click the button " ]
[ Event . onClick ButtonClicked ]
[ Pages . Clicker . view clickerModel
, body = [ Html . text " Can't find that url " ]
subscriptions : Model -> Sub Msg