ryan-haskell / elm-spa / ElmSpa.Page

Pages


type Page shared route effect view model msg

Pages are the building blocks of elm-spa.

Instead of importing this module, your project will have a Page module with a much simpler type:

module Page exposing (Page, ...)

type Page model msg

This makes all the generic route, effect, and view arguments disappear!

static : effect -> { view : view } -> Page shared route effect view () msg

A page that only needs to render a static view.

import Page

page : Page () Never
page =
    Page.static
        { view = view
        }

-- view : View Never

sandbox : effect -> { init : model, update : msg -> model -> model, view : model -> view } -> Page shared route effect view model msg

A page that can keep track of application state.

( Inspired by Browser.sandbox )

import Page

page : Page Model Msg
page =
    Page.sandbox
        { init = init
        , update = update
        , view = view
        }

-- init : Model
-- update : Msg -> Model -> Model
-- view : Model -> View Msg

element : (Platform.Cmd.Cmd msg -> effect) -> { init : ( model, Platform.Cmd.Cmd msg ), update : msg -> model -> ( model, Platform.Cmd.Cmd msg ), view : model -> view, subscriptions : model -> Platform.Sub.Sub msg } -> Page shared route effect view model msg

A page that can handle effects like HTTP requests or subscriptions.

( Inspired by Browser.element )

import Page

page : Page Model Msg
page =
    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

advanced : { init : ( model, effect ), update : msg -> model -> ( model, effect ), view : model -> view, subscriptions : model -> Platform.Sub.Sub msg } -> Page shared route effect view model msg

A page that can handles custom effects like sending a Shared.Msg or other general user-defined effects.

import Effect
import Page

page : Page Model Msg
page =
    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

User Authentication


type Protected user route
    = Provide user
    | RedirectTo route

Actions to take when a user visits a protected page

import Gen.Route as Route exposing (Route)

beforeProtectedInit : Shared.Model -> Request () -> Protected User Route
beforeProtectedInit shared _ =
    case shared.user of
        Just user ->
            Provide user

        Nothing ->
            RedirectTo Route.SignIn

protected : { effectNone : effect, fromCmd : Platform.Cmd.Cmd msg -> effect, beforeInit : shared -> Request route () -> Protected user route } -> { static : (user -> { view : view }) -> Page shared route effect view () msg, sandbox : (user -> { init : model, update : msg -> model -> model, view : model -> view }) -> Page shared route effect view model msg, element : (user -> { init : ( model, Platform.Cmd.Cmd msg ), update : msg -> model -> ( model, Platform.Cmd.Cmd msg ), view : model -> view, subscriptions : model -> Platform.Sub.Sub msg }) -> Page shared route effect view model msg, advanced : (user -> { init : ( model, effect ), update : msg -> model -> ( model, effect ), view : model -> view, subscriptions : model -> Platform.Sub.Sub msg }) -> Page shared route effect view model msg }

Prefixing any of the four functions above with protected will guarantee that the page has access to a user. Here's an example with sandbox:

-- before
Page.sandbox
    { init = init
    , update = update
    , view = view
    }

-- after
Page.protected.sandbox
    (\user ->
        { init = init
        , update = update
        , view = view
        }
    )

-- other functions have same API
init : Model
update : Msg -> Model -> Model
view : Model -> View Msg

For generated code


type alias Bundle params model msg shared effect pagesModel pagesMsg pagesView =
{ init : params -> shared -> Url -> Browser.Navigation.Key -> ( pagesModel
, effect )
, update : params -> msg -> model -> shared -> Url -> Browser.Navigation.Key -> ( pagesModel
, effect )
, view : params -> model -> shared -> Url -> Browser.Navigation.Key -> pagesView
, subscriptions : params -> model -> shared -> Url -> Browser.Navigation.Key -> Platform.Sub.Sub pagesMsg 
}

A convenient function for use within generated code. Makes it easy to handle init, update, view, and subscriptions for each page!

bundle : { redirecting : { model : pagesModel, view : pagesView }, toRoute : Url -> route, toUrl : route -> String, fromCmd : Platform.Cmd.Cmd any -> pagesEffect, mapEffect : effect -> pagesEffect, mapView : view -> pagesView, page : shared -> Request route params -> Page shared route effect view model msg, toModel : params -> model -> pagesModel, toMsg : msg -> pagesMsg } -> Bundle params model msg shared pagesEffect pagesModel pagesMsg pagesView

This function is used by the generated code to connect your pages together.

It's big, spooky, and makes writing elm-spa pages really nice!