ryannhg/elm-spa/1.0.0

NOTE: the latest version is 6.0.0

Renamed to ryan-haskell/elm-spa. It is published under that name, and new versions come out there.

Versions published as ryannhg/elm-spa still install under that name. 13 versions of 13 come from the new location (see their notes).

Rerouted (fork, verified). The original repository is gone. This copy comes from a fork that still has the tag. The Lamdera package server serves a copy recovered from https://github.com/tuohy/elm-spa (a fork).

What we checked: Rebuilt with git archive from commit 292236b2582f of https://github.com/tuohy/elm-spa (a fork) (zip prefix ryannhg-elm-spa-8ee73bf/, America/Los_Angeles times): the zip is byte-identical to the one package.elm-lang.org recorded, SHA-1 597d34671fca0206517a1cd488d425f4f335b10f.

The Lamdera compiler uses this copy and prints a one-line notice saying so. elm itself fails to download this version.

Details · package.elm-lang.org's zip: https://github.com/ryannhg/elm-spa/zipball/1.0.0/

Application.Page

Each page can be as simple or complex as you need:

  1. Static - for rendering a simple view

  2. Sandbox - for maintaining state, without any side-effects

  3. Element - for maintaining state, with side-effects

  4. Component - for a full-blown page, that can view and update global state

Static

For rendering a simple view.

page =
    Page.static
        { title = title
        , view = view
        }
{ title : String
, view : uiPageMsg
}
-> Page
pageRoute
()
Never
uiPageMsg
layoutModel
layoutMsg
uiLayoutMsg
globalModel
globalMsg
msg
uiMsg

Create an static page from a record. Here's an example

Sandbox

For maintaining state, without any side-effects.

page =
    Page.sandbox
        { title = title
        , init = init
        , update = update
        , view = view
        }
{ title : pageModel -> String
, init : pageRoute -> pageModel
, update : pageMsg -> pageModel -> pageModel
, view : pageModel -> uiPageMsg
}
-> Page
pageRoute
pageModel
pageMsg
uiPageMsg
layoutModel
layoutMsg
uiLayoutMsg
globalModel
globalMsg
msg
uiMsg

Create an sandbox page from a record. Here's an example

Element

For maintaining state, with side-effects.

page =
    Page.element
        { title = title
        , init = init
        , update = update
        , view = view
        , subscriptions = subscriptions
        }
{ title : pageModel -> String
, init : pageRoute -> ( pageModel, Cmd pageMsg )
, update : pageMsg -> pageModel -> ( pageModel, Cmd pageMsg )
, view : pageModel -> uiPageMsg
, subscriptions : pageModel -> Sub pageMsg
}
-> Page
pageRoute
pageModel
pageMsg
uiPageMsg
layoutModel
layoutMsg
uiLayoutMsg
globalModel
globalMsg
msg
uiMsg

Create an element page from a record. Here's an example

Component

For a full-blown page, that can view and update global state.

page =
    Page.component
        { title = title
        , init = init
        , update = update
        , view = view
        , subscriptions = subscriptions
        }
{ title : globalModel -> pageModel -> String
, init :
globalModel
-> pageRoute
-> ( pageModel, Cmd pageMsg, Cmd globalMsg )
, update :
globalModel
-> pageMsg
-> pageModel
-> ( pageModel, Cmd pageMsg, Cmd globalMsg )
, view : globalModel -> pageModel -> uiPageMsg
, subscriptions : globalModel -> pageModel -> Sub pageMsg
}
-> Page
pageRoute
pageModel
pageMsg
uiPageMsg
layoutModel
layoutMsg
uiLayoutMsg
globalModel
globalMsg
msg
uiMsg

Create an component page from a record. Here's an example

Composing pages together

The rest of this module contains types and functions that can be generated with the cli companion tool

If you're typing this stuff manually, you might need to know what these are for!

Layout

A page that is comprimised of smaller pages, that is able to share a common layout (maybe a something like a sidebar!)

page =
    Page.layout
        { map = Html.map
        , layout = Layout.view
        , pages =
            { init = init
            , update = update
            , bundle = bundle
            }
        }
{ map : (pageMsg -> msg) -> uiPageMsg -> uiMsg
, view :
{ page : uiMsg
, global : globalModel
}
-> uiMsg
, pages :
Recipe
pageRoute
pageModel
pageMsg
pageModel
pageMsg
uiPageMsg
globalModel
globalMsg
msg
uiMsg
}
-> Page
pageRoute
pageModel
pageMsg
uiPageMsg
layoutModel
layoutMsg
uiLayoutMsg
globalModel
globalMsg
msg
uiMsg

These are used by top-level files like src/Generated/Pages.elm to compose together pages and layouts.

We'll get a better understanding of init, update, and bundle below!

Page.layout
    { map = Html.map
    , layout = Layout.view
    , pages =
        { init = init
        , update = update
        , bundle = bundle
        }
    }

Recipe

Implementing the init, update and bundle functions is much easier when you turn a Page type into Recipe.

A Recipe contains a record waiting for page specific data.

  • init: just needs a route

  • upgrade : just needs a msg and model

  • bundle (view/subscriptions) : just needs a model

What's a "bundle"?

We can "bundle" the view and subscriptions functions together, because they both only depend on the current model. That's why this API exposes bundle instead of making you type this:

-- BEFORE
view model =
    case model_ of
        FooModel model ->
            foo.view model


        BarModel model ->
            bar.view model


        BazModel model ->
            baz.view model


subscriptions model =
    case model_ of
        FooModel model ->
            foo.subscriptions model


        BarModel model ->
            bar.subscriptions model


        BazModel model ->
            baz.subscriptions model


-- AFTER
bundle model =
    case model_ of
        FooModel model ->
            foo.bundle model


        BarModel model ->
            bar.bundle model


        BazModel model ->
            baz.bundle model

(Woohoo, less case expressions to type out!)

Page
pageRoute
pageModel
pageMsg
uiPageMsg
layoutModel
layoutMsg
uiLayoutMsg
globalModel
globalMsg
msg
uiMsg
-> { toModel : pageModel -> layoutModel
, toMsg : pageMsg -> layoutMsg
, map : (pageMsg -> layoutMsg) -> uiPageMsg -> uiLayoutMsg
}
-> Recipe
pageRoute
pageModel
pageMsg
layoutModel
layoutMsg
uiLayoutMsg
globalModel
globalMsg
msg
uiMsg

Turns a page and some upgrade information into a recipe, for use in a layout's init, update, and bundle functions!

Page.recipe Homepage.page
    { toModel = HomepageModel
    , toMsg = HomepageMsg
    , map = Element.map -- ( if using elm-ui )
    }

Update helper

keep : layoutModel -> Update layoutModel layoutMsg globalModel globalMsg

In the event that our case expression in update receives a msg that doesn't match it's model, we use this function to keep the model as-is.

update msg_ model_ =
    case ( msg_, model_ ) of
        ( FooMsg msg, FooModel model ) ->
            foo.update msg model


        ( BarMsg msg, BarModel model ) ->
            bar.update msg model


        ( BazMsg msg, BazModel model ) ->
            baz.update msg model


        _ ->
            Page.keep model_