docs for humans that like not being confused

This commit is contained in:
Ryan Haskell-Glatz 2019-11-11 17:50:57 -08:00
parent 8a502b89f3
commit 5319cb1847
5 changed files with 470 additions and 244 deletions

View File

@ -14,12 +14,12 @@ import Element exposing (Element)
import Global import Global
type alias Page flags model msg layoutModel layoutMsg appMsg = type alias Page params model msg layoutModel layoutMsg appMsg =
App.Types.Page flags model msg (Element msg) layoutModel layoutMsg (Element layoutMsg) Global.Model Global.Msg appMsg (Element appMsg) App.Types.Page params model msg (Element msg) layoutModel layoutMsg (Element layoutMsg) Global.Model Global.Msg appMsg (Element appMsg)
type alias Recipe flags model msg layoutModel layoutMsg appMsg = type alias Recipe params model msg layoutModel layoutMsg appMsg =
App.Types.Recipe flags model msg layoutModel layoutMsg (Element layoutMsg) Global.Model Global.Msg appMsg (Element appMsg) App.Types.Recipe params model msg layoutModel layoutMsg (Element layoutMsg) Global.Model Global.Msg appMsg (Element appMsg)
type alias Init model msg = type alias Init model msg =

View File

@ -11,18 +11,22 @@ module App exposing
`App.create` replaces [Browser.application](https://package.elm-lang.org/packages/elm/browser/latest/Browser#application) `App.create` replaces [Browser.application](https://package.elm-lang.org/packages/elm/browser/latest/Browser#application)
as the entrypoint to your app. as the entrypoint to your app.
import App module Main exposing (main)
import Generated.Pages as Pages
import Generated.Route as Route
import Global
import App
import Element
import Global
import Pages
import Routes
main : App.Program Global.Flags Global.Model Global.Msg Pages.Model Pages.Msg
main = main =
App.create App.create
{ ui = App.usingHtml { ui = App.usingHtml
, routing = , routing =
{ routes = Route.routes { routes = Routes.parsers
, toPath = Route.toPath , toPath = Routes.toPath
, notFound = Route.NotFound () , notFound = Routes.routes.notFound
} }
, global = , global =
{ init = Global.init { init = Global.init
@ -35,23 +39,23 @@ as the entrypoint to your app.
@docs Program, create @docs Program, create
# Supports more than elm/html # using elm-ui?
If you're a fan of [mdgriffith/elm-ui](https://package.elm-lang.org/packages/mdgriffith/elm-ui/latest/), If you're a big fan of [mdgriffith/elm-ui](https://package.elm-lang.org/packages/mdgriffith/elm-ui/latest/) (or not-so-big-fan of CSS),
it's important to support using `Element msg` instead of `Html msg` for your pages and components. this package supports using `Element msg` instead of `Html msg` for your pages and components.
Let `App.create` know about this by passing in your own `Options` like these: Providing `App.create` with these `ui` options will do the trick!
import Element import Element
-- other imports
App.create main =
{ ui = App.create
{ toHtml = Element.layout [] { ui =
, map = Element.map { toHtml = Element.layout []
, map = Element.map
}
, -- ...
} }
, -- ... the rest of your app
}
@docs usingHtml @docs usingHtml
@ -78,7 +82,11 @@ type alias Program flags globalModel globalMsg layoutModel layoutMsg =
{-| Pass this in when calling `App.create` {-| Pass this in when calling `App.create`
( It will work if your view returns the standard `Html msg` ) main =
App.create
{ ui = App.usingHtml
, -- ...
}
-} -}
usingHtml : usingHtml :
@ -86,7 +94,7 @@ usingHtml :
(layoutMsg -> Msg globalMsg layoutMsg) (layoutMsg -> Msg globalMsg layoutMsg)
-> Html layoutMsg -> Html layoutMsg
-> Html (Msg globalMsg layoutMsg) -> Html (Msg globalMsg layoutMsg)
, toHtml : uiMsg -> uiMsg , toHtml : ui_msg -> ui_msg
} }
usingHtml = usingHtml =
{ toHtml = identity { toHtml = identity
@ -104,8 +112,8 @@ usingHtml =
-} -}
create : create :
{ ui : { ui :
{ toHtml : uiMsg -> Html (Msg globalMsg layoutMsg) { toHtml : ui_msg -> Html (Msg globalMsg layoutMsg)
, map : (layoutMsg -> Msg globalMsg layoutMsg) -> uiLayoutMsg -> uiMsg , map : (layoutMsg -> Msg globalMsg layoutMsg) -> ui_layoutMsg -> ui_msg
} }
, routing : , routing :
{ routes : List (Parser (route -> route) route) { routes : List (Parser (route -> route) route)
@ -126,7 +134,7 @@ create :
-> ( globalModel, Cmd globalMsg, Cmd (Msg globalMsg layoutMsg) ) -> ( globalModel, Cmd globalMsg, Cmd (Msg globalMsg layoutMsg) )
, subscriptions : globalModel -> Sub globalMsg , subscriptions : globalModel -> Sub globalMsg
} }
, page : Page.Page route layoutModel layoutMsg uiLayoutMsg layoutModel layoutMsg uiLayoutMsg globalModel globalMsg (Msg globalMsg layoutMsg) uiMsg , page : Page.Page route layoutModel layoutMsg ui_layoutMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg (Msg globalMsg layoutMsg) ui_msg
} }
-> Program flags globalModel globalMsg layoutModel layoutMsg -> Program flags globalModel globalMsg layoutModel layoutMsg
create config = create config =
@ -358,10 +366,10 @@ navigate toPath url route =
subscriptions : subscriptions :
{ map : (layoutMsg -> Msg globalMsg layoutMsg) -> uiLayoutMsg -> uiMsg { map : (layoutMsg -> Msg globalMsg layoutMsg) -> ui_layoutMsg -> ui_msg
, bundle : , bundle :
layoutModel layoutModel
-> Page.Bundle layoutMsg uiLayoutMsg globalModel globalMsg (Msg globalMsg layoutMsg) uiMsg -> Page.Bundle layoutMsg ui_layoutMsg globalModel globalMsg (Msg globalMsg layoutMsg) ui_msg
, global : globalModel -> Sub globalMsg , global : globalModel -> Sub globalMsg
} }
-> Model flags globalModel layoutModel -> Model flags globalModel layoutModel
@ -385,11 +393,11 @@ subscriptions config model =
view : view :
{ map : (layoutMsg -> Msg globalMsg layoutMsg) -> uiLayoutMsg -> uiMsg { map : (layoutMsg -> Msg globalMsg layoutMsg) -> ui_layoutMsg -> ui_msg
, toHtml : uiMsg -> Html (Msg globalMsg layoutMsg) , toHtml : ui_msg -> Html (Msg globalMsg layoutMsg)
, bundle : , bundle :
layoutModel layoutModel
-> Page.Bundle layoutMsg uiLayoutMsg globalModel globalMsg (Msg globalMsg layoutMsg) uiMsg -> Page.Bundle layoutMsg ui_layoutMsg globalModel globalMsg (Msg globalMsg layoutMsg) ui_msg
} }
-> Model flags globalModel layoutModel -> Model flags globalModel layoutModel
-> Browser.Document (Msg globalMsg layoutMsg) -> Browser.Document (Msg globalMsg layoutMsg)

View File

@ -10,76 +10,47 @@ module App.Page exposing
{-| Each page can be as simple or complex as you need: {-| Each page can be as simple or complex as you need:
1. [Static](#static) - for rendering a simple view 1. [Static](#static) - a page without state
2. [Sandbox](#sandbox) - for maintaining state, without any side-effects 2. [Sandbox](#sandbox) - a page without side-effects
3. [Element](#element) - for maintaining state, **with** side-effects 3. [Element](#element) - a page _with_ side-effects
4. [Component](#component) - for a full-blown page, that can view and update global state 4. [Component](#component) - a page that can change the global state
## Static ## what's that `always` for?
For rendering a simple view. You may notice the examples below use `always`. This is to **opt-out** each
function from reading the global model.
page = If you need access to `Global.Model` in your `title`, `init`, `update`, `view`, or
Page.static `subscriptions` functions, just remove the always.
{ title = title
, view = view **It is recommended to include this to keep your pages as simple as possible!**
}
# static
@docs static @docs static
## Sandbox # sandbox
For maintaining state, without any side-effects.
page =
Page.sandbox
{ title = title
, init = init
, update = update
, view = view
}
@docs sandbox @docs sandbox
## Element # element
For maintaining state, **with** side-effects.
page =
Page.element
{ title = title
, init = init
, update = update
, view = view
, subscriptions = subscriptions
}
@docs element @docs element
## Component # 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
}
@docs component, send @docs component, send
# Composing pages together # composing pages together
The rest of this module contains types and functions that The rest of this module contains types and functions that
can be generated with the [cli companion tool](https://github.com/ryannhg/elm-spa/tree/master/cli) can be generated with the [cli companion tool](https://github.com/ryannhg/elm-spa/tree/master/cli)
@ -88,7 +59,7 @@ If you're typing this stuff manually, you might need to know what
these are for! these are for!
## Layout ## layout
A page that is comprimised of smaller pages, that is A page that is comprimised of smaller pages, that is
able to share a common layout (maybe a something like a sidebar!) able to share a common layout (maybe a something like a sidebar!)
@ -107,7 +78,7 @@ able to share a common layout (maybe a something like a sidebar!)
@docs layout @docs layout
## Recipe ## recipe
Implementing the `init`, `update` and `bundle` functions is much easier Implementing the `init`, `update` and `bundle` functions is much easier
when you turn a `Page` type into `Recipe`. when you turn a `Page` type into `Recipe`.
@ -120,16 +91,18 @@ A `Recipe` contains a record waiting for page specific data.
- `bundle` (`view`/`subscriptions`) : just needs a `model` - `bundle` (`view`/`subscriptions`) : just needs a `model`
@docs recipe
### What's a "bundle"?
## what's a "bundle"?
We can "bundle" the `view` and `subscriptions` functions together, We can "bundle" the `view` and `subscriptions` functions together,
because they both only depend on the current `model`. That's why this because they both only need the current `model`.
API exposes `bundle` instead of making you type this:
-- BEFORE So _instead_ of typing out these:
view model =
case model_ of view bigModel =
case bigModel of
FooModel model -> FooModel model ->
foo.view model foo.view model
@ -139,8 +112,8 @@ API exposes `bundle` instead of making you type this:
BazModel model -> BazModel model ->
baz.view model baz.view model
subscriptions model = subscriptions bigModel =
case model_ of case bigModel of
FooModel model -> FooModel model ->
foo.subscriptions model foo.subscriptions model
@ -150,9 +123,10 @@ API exposes `bundle` instead of making you type this:
BazModel model -> BazModel model ->
baz.subscriptions model baz.subscriptions model
-- AFTER You only need **one** case expression: (woohoo, less boilerplate!)
bundle model =
case model_ of bundle bigModel =
case bigModel of
FooModel model -> FooModel model ->
foo.bundle model foo.bundle model
@ -162,12 +136,8 @@ API exposes `bundle` instead of making you type this:
BazModel model -> BazModel model ->
baz.bundle model baz.bundle model
(Woohoo, less case expressions to type out!)
@docs recipe ## update helpers
## Update helper
@docs keep @docs keep
@ -177,47 +147,62 @@ import Internals.Page exposing (..)
import Internals.Utils as Utils import Internals.Utils as Utils
type alias Page pageRoute pageModel pageMsg uiPageMsg layoutModel layoutMsg uiLayoutMsg globalModel globalMsg msg uiMsg = type alias Page pageParams pageModel pageMsg ui_pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg =
Internals.Page.Page pageRoute pageModel pageMsg uiPageMsg layoutModel layoutMsg uiLayoutMsg globalModel globalMsg msg uiMsg Internals.Page.Page pageParams pageModel pageMsg ui_pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg
{-| Turns a page and some upgrade information into a recipe, {-| Turns a page and some upgrade information into a recipe,
for use in a layout's `init`, `update`, and `bundle` functions! for use in a layout's `init`, `update`, and `bundle` functions!
Page.recipe Homepage.page import Utils.Spa as Spa
{ toModel = HomepageModel
, toMsg = HomepageMsg recipes : Recipes msg
, map = Element.map -- ( if using elm-ui ) recipes =
{ top =
Spa.recipe
{ page = Top.page
, toModel = TopModel
, toMsg = TopMsg
}
, counter =
Spa.recipe
{ page = Counter.page
, toModel = CounterModel
, toMsg = CounterMsg
}
-- ...
} }
-} -}
recipe : recipe :
{ page : Page pageRoute pageModel pageMsg uiPageMsg layoutModel layoutMsg uiLayoutMsg globalModel globalMsg msg uiMsg { page : Page pageParams pageModel pageMsg ui_pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg
, toModel : pageModel -> layoutModel , toModel : pageModel -> layoutModel
, toMsg : pageMsg -> layoutMsg , toMsg : pageMsg -> layoutMsg
, map : (pageMsg -> layoutMsg) -> uiPageMsg -> uiLayoutMsg , map : (pageMsg -> layoutMsg) -> ui_pageMsg -> ui_layoutMsg
} }
-> Recipe pageRoute pageModel pageMsg layoutModel layoutMsg uiLayoutMsg globalModel globalMsg msg uiMsg -> Recipe pageParams pageModel pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg
recipe = recipe =
Internals.Page.upgrade Internals.Page.upgrade
{-| In the event that our `case` expression in `update` receives a `msg` that doesn't {-| 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. match up with it's `model`, we use `keep` to leave the page as-is.
update msg_ model_ = update : Msg -> Model -> Spa.Update Model Msg
case ( msg_, model_ ) of update bigMsg bigModel =
( FooMsg msg, FooModel model ) -> case ( bigMsg, bigModel ) of
foo.update msg model ( TopMsg msg, TopModel model ) ->
top.update msg model
( BarMsg msg, BarModel model ) -> ( CounterMsg msg, CounterModel model ) ->
bar.update msg model counter.update msg model
( BazMsg msg, BazModel model ) -> ( NotFoundMsg msg, NotFoundModel model ) ->
baz.update msg model notFound.update msg model
_ -> _ ->
Page.keep model_ Page.keep bigModel
-} -}
keep : keep :
@ -227,17 +212,31 @@ keep model =
always ( model, Cmd.none, Cmd.none ) always ( model, Cmd.none, Cmd.none )
{-|
-- STATIC
{-| Create an `static` page from a record. [Here's an example](https://github.com/ryannhg/elm-spa/examples/html/src/Pages/Index.elm) ## an example
page =
Page.static
{ title = always title
, view = always view
}
title : String
title =
"Example"
view : Html Never
view =
h1 [ class "title" ] [ text "Example" ]
-} -}
static : static :
{ title : { global : globalModel } -> String { title : { global : globalModel } -> String
, view : globalModel -> uiPageMsg , view : globalModel -> ui_pageMsg
} }
-> Page pageRoute () Never uiPageMsg layoutModel layoutMsg uiLayoutMsg globalModel globalMsg msg uiMsg -> Page pageParams () Never ui_pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg
static page = static page =
Page Page
(\{ toModel, toMsg, map } -> (\{ toModel, toMsg, map } ->
@ -257,21 +256,65 @@ static page =
-- SANDBOX -- SANDBOX
{-| Create an `sandbox` page from a record. [Here's an example](https://github.com/ryannhg/elm-spa/examples/html/src/Pages/Counter.elm) {-|
## an example
page =
Page.sandbox
{ title = always title
, init = always init
, update = always update
, view = always view
}
title : String
title =
"Counter"
type alias Model =
Int
init : Model
init =
0
type Msg
= Increment
| Decrement
update : Msg -> Model -> Model
update msg model =
case msg of
Increment ->
model + 1
Decrement ->
model - 1
view : Model -> Html Msg
view model =
div []
[ button [ Events.onClick Increment ] [ text "+" ]
, text (String.fromInt model)
, button [ Events.onClick Decrement ] [ text "-" ]
]
-} -}
sandbox : sandbox :
{ title : { global : globalModel, model : pageModel } -> String { title : { global : globalModel, model : pageModel } -> String
, init : globalModel -> pageRoute -> pageModel , init : globalModel -> pageParams -> pageModel
, update : globalModel -> pageMsg -> pageModel -> pageModel , update : globalModel -> pageMsg -> pageModel -> pageModel
, view : globalModel -> pageModel -> uiPageMsg , view : globalModel -> pageModel -> ui_pageMsg
} }
-> Page pageRoute pageModel pageMsg uiPageMsg layoutModel layoutMsg uiLayoutMsg globalModel globalMsg msg uiMsg -> Page pageParams pageModel pageMsg ui_pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg
sandbox page = sandbox page =
Page Page
(\{ toModel, toMsg, map } -> (\{ toModel, toMsg, map } ->
{ init = { init =
\pageRoute context -> \pageParams context ->
( toModel (page.init context.global pageRoute) ( toModel (page.init context.global pageParams)
, Cmd.none , Cmd.none
, Cmd.none , Cmd.none
) )
@ -295,22 +338,55 @@ sandbox page =
-- ELEMENT -- ELEMENT
{-| Create an `element` page from a record. [Here's an example](https://github.com/ryannhg/elm-spa/examples/html/src/Pages/Random.elm) {-|
## an example
page =
Page.element
{ title = always title
, init = always init
, update = always update
, subscriptions = always subscriptions
, view = always view
}
title : String
title =
"Cat Gifs"
init : ( Model, Cmd.none )
init =
-- ...
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
-- ...
subscriptions : Model -> Sub Msg
subscriptions model =
-- ...
view : Model -> Html Msg
view model =
-- ...
-} -}
element : element :
{ title : { global : globalModel, model : pageModel } -> String { title : { global : globalModel, model : pageModel } -> String
, init : globalModel -> pageRoute -> ( pageModel, Cmd pageMsg ) , init : globalModel -> pageParams -> ( pageModel, Cmd pageMsg )
, update : globalModel -> pageMsg -> pageModel -> ( pageModel, Cmd pageMsg ) , update : globalModel -> pageMsg -> pageModel -> ( pageModel, Cmd pageMsg )
, view : globalModel -> pageModel -> uiPageMsg , view : globalModel -> pageModel -> ui_pageMsg
, subscriptions : globalModel -> pageModel -> Sub pageMsg , subscriptions : globalModel -> pageModel -> Sub pageMsg
} }
-> Page pageRoute pageModel pageMsg uiPageMsg layoutModel layoutMsg uiLayoutMsg globalModel globalMsg msg uiMsg -> Page pageParams pageModel pageMsg ui_pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg
element page = element page =
Page Page
(\{ toModel, toMsg, map } -> (\{ toModel, toMsg, map } ->
{ init = { init =
\pageRoute context -> \pageParams context ->
page.init context.global pageRoute page.init context.global pageParams
|> tuple toModel toMsg |> tuple toModel toMsg
, update = , update =
\msg model context -> \msg model context ->
@ -330,22 +406,58 @@ element page =
-- COMPONENT -- COMPONENT
{-| Create an `component` page from a record. [Here's an example](https://github.com/ryannhg/elm-spa/examples/html/src/Pages/SignIn.elm) {-|
## an example
page =
Page.component
{ title = always title
, init = always init
, update = always update
, subscriptions = always subscriptions
-- no always, so `view` gets `Global.Model`
, view = view
}
title : String
title =
"Sign in"
init : Params.SignIn -> ( Model, Cmd Msg, Cmd Global.Msg )
init params =
-- ...
update : Msg -> Model -> ( Model, Cmd Msg, Cmd Global.Msg )
update msg model =
-- ...
subscriptions : Model -> Sub Msg
subscriptions model =
-- ...
view : Global.Model -> Model -> Html Msg
view global model =
case global.user of
SignedIn _ -> viewSignOutForm
SignedOut -> viewSignInForm
-} -}
component : component :
{ title : { global : globalModel, model : pageModel } -> String { title : { global : globalModel, model : pageModel } -> String
, init : globalModel -> pageRoute -> ( pageModel, Cmd pageMsg, Cmd globalMsg ) , init : globalModel -> pageParams -> ( pageModel, Cmd pageMsg, Cmd globalMsg )
, update : globalModel -> pageMsg -> pageModel -> ( pageModel, Cmd pageMsg, Cmd globalMsg ) , update : globalModel -> pageMsg -> pageModel -> ( pageModel, Cmd pageMsg, Cmd globalMsg )
, view : globalModel -> pageModel -> uiPageMsg , view : globalModel -> pageModel -> ui_pageMsg
, subscriptions : globalModel -> pageModel -> Sub pageMsg , subscriptions : globalModel -> pageModel -> Sub pageMsg
} }
-> Page pageRoute pageModel pageMsg uiPageMsg layoutModel layoutMsg uiLayoutMsg globalModel globalMsg msg uiMsg -> Page pageParams pageModel pageMsg ui_pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg
component page = component page =
Page Page
(\{ toModel, toMsg, map } -> (\{ toModel, toMsg, map } ->
{ init = { init =
\pageRoute context -> \pageParams context ->
page.init context.global pageRoute page.init context.global pageParams
|> truple toModel toMsg |> truple toModel toMsg
, update = , update =
\msg model context -> \msg model context ->
@ -363,14 +475,12 @@ component page =
{-| Useful for sending `Global.Msg` from a component. {-| Useful for sending `Global.Msg` from a component.
update : Global.Model -> Msg -> Model -> ( Model, Cmd Msg, Cmd Global.Msg ) init : Params.SignIn -> ( Model, Cmd Msg, Cmd Global.Msg )
update global msg model = init params =
case msg of ( model
SignIn -> , Cmd.none
( model , Page.send (Global.NavigateTo routes.dashboard)
, Cmd.none )
, Page.send Global.SignIn
)
-} -}
send : msg -> Cmd msg send : msg -> Cmd msg
@ -382,39 +492,38 @@ send =
-- LAYOUT -- LAYOUT
{-| These are used by top-level files like `src/Generated/Pages.elm` {-| In practice, we wrap `layout` in `Utils/Spa.elm` so we only have to provide `Html.map` or `Element.map` once)
to compose together pages and layouts.
We'll get a better understanding of `init`, `update`, and `bundle` below! import Utils.Spa as Spa
Page.layout page =
{ map = Html.map Spa.layout
, layout = Layout.view { layout = Layout.view
, pages = , pages =
{ init = init { init = init
, update = update , update = update
, bundle = bundle , bundle = bundle
}
} }
}
-} -}
layout : layout :
{ map : (pageMsg -> msg) -> uiPageMsg -> uiMsg { map : (pageMsg -> msg) -> ui_pageMsg -> ui_msg
, view : , view :
{ page : uiMsg { page : ui_msg
, global : globalModel , global : globalModel
, toMsg : globalMsg -> msg , toMsg : globalMsg -> msg
} }
-> uiMsg -> ui_msg
, recipe : Recipe pageRoute pageModel pageMsg pageModel pageMsg uiPageMsg globalModel globalMsg msg uiMsg , recipe : Recipe pageParams pageModel pageMsg pageModel pageMsg ui_pageMsg globalModel globalMsg msg ui_msg
} }
-> Page pageRoute pageModel pageMsg uiPageMsg layoutModel layoutMsg uiLayoutMsg globalModel globalMsg msg uiMsg -> Page pageParams pageModel pageMsg ui_pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg
layout options = layout options =
Page Page
(\{ toModel, toMsg } -> (\{ toModel, toMsg } ->
{ init = { init =
\pageRoute global -> \pageParams global ->
options.recipe.init pageRoute global options.recipe.init pageParams global
|> truple toModel toMsg |> truple toModel toMsg
, update = , update =
\msg model global -> \msg model global ->
@ -423,7 +532,7 @@ layout options =
, bundle = , bundle =
\model context -> \model context ->
let let
bundle : { title : String, view : uiMsg, subscriptions : Sub msg } bundle : { title : String, view : ui_msg, subscriptions : Sub msg }
bundle = bundle =
options.recipe.bundle options.recipe.bundle
model model

View File

@ -1,118 +1,227 @@
module App.Types exposing module App.Types exposing
( Page, Recipe ( Page
, Init, Update, Bundle , Recipe
, Init
, Update
, Bundle
) )
{-| {-|
## Types so spooky, they got their own module! 👻 ## types so spooky, they got their own module! 👻
This module is all about exposing the types that `ryannhg/elm-app` uses This module is all about exposing the types that `ryannhg/elm-app` uses
under the hood. under the hood.
Because so much of your app is defined outside of this package, we see At a glance, there are a **lot of generic types**.
a **lot of generic types**.
In practice, we can handle this with a single
[`Utils/Spa.elm`](https://github.com/ryannhg/elm-spa/blob/master/example/src/Utils/Spa.elm) file that
makes your types easier to understand!
`elm-spa init` generates that file for you, but I've added examples below if you're
doing things by hand.
### Don't be spooked! # page
In practice, we usually handle this with a `Utils/Page.elm` file that @docs Page
creates less generic `type alias` for use in your app!
module Utils.Spa exposing
( Bundle
, Init
, Page
, Recipe
, Update
, layout
, recipe
)
import App.Page # recipe
import App.Types
import Element exposing (Element)
import Global
type alias Page flags model msg layoutModel layoutMsg appMsg = @docs Recipe
App.Types.Page flags model msg (Element msg) layoutModel layoutMsg (Element layoutMsg) Global.Model Global.Msg appMsg (Element appMsg)
type alias Recipe flags model msg layoutModel layoutMsg appMsg =
App.Types.Recipe flags model msg layoutModel layoutMsg (Element layoutMsg) Global.Model Global.Msg appMsg (Element appMsg)
type alias Init model msg = # init
App.Types.Init model msg Global.Model Global.Msg
type alias Update model msg = @docs Init
App.Types.Update model msg Global.Model Global.Msg
type alias Bundle msg appMsg =
App.Types.Bundle msg (Element msg) Global.Model Global.Msg appMsg (Element appMsg)
layout config = # update
App.Page.layout
{ map = Element.map
, view = config.view
, recipe = config.recipe
}
recipe config = @docs Update
App.Page.recipe
{ map = Element.map
, page = config.page
, toModel = config.toModel
, toMsg = config.toMsg
}
@docs Page, Recipe
@docs Init, Update, Bundle # bundle
@docs Bundle
-} -}
import Internals.Page as Page import Internals.Page as Page
{-| This type alias should be used in all `src/Pages` files. {-|
module Pages.Example exposing
( page ## creating your alias
, -- ...
) **`src/Utils/Spa.elm`**
-- if using mdgriffith/elm-ui
import App.Types
import Element exposing (Element)
type alias Page params model msg layoutModel layoutMsg appMsg =
App.Types.Page params model msg (Element msg) layoutModel layoutMsg (Element layoutMsg) Global.Model Global.Msg appMsg (Element appMsg)
-- if using elm/html
import App.Types
import Html exposing (Html)
type alias Page params model msg layoutModel layoutMsg appMsg =
App.Types.Page params model msg (Html msg) layoutModel layoutMsg (Html layoutMsg) Global.Model Global.Msg appMsg (Html appMsg)
## using your alias
**`src/Pages/Example.elm`**
import Utils.Spa as Spa import Utils.Spa as Spa
page : Spa.Page Params.Example Model Msg model msg appMsg page : Spa.Page Params.Example Model Msg model msg appMsg
page = page =
App.Page.static App.Page.static { ... }
-- ...
-} -}
type alias Page pageRoute pageModel pageMsg uiPageMsg layoutModel layoutMsg uiLayoutMsg globalModel globalMsg msg uiMsg = type alias Page params pageModel pageMsg ui_pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg =
Page.Page pageRoute pageModel pageMsg uiPageMsg layoutModel layoutMsg uiLayoutMsg globalModel globalMsg msg uiMsg Page.Page params pageModel pageMsg ui_pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg
{-| Recipe {-|
## creating your alias
**`src/Utils/Spa.elm`**
-- if using mdgriffith/elm-ui
import App.Types
import Element exposing (Element)
type alias Recipe params model msg layoutModel layoutMsg appMsg =
App.Types.Recipe params model msg layoutModel layoutMsg (Element layoutMsg) Global.Model Global.Msg appMsg (Element appMsg)
-- if using elm/html
import App.Types
import Html exposing (Html)
type alias Recipe params model msg layoutModel layoutMsg appMsg =
App.Types.Recipe params model msg layoutModel layoutMsg (Html layoutMsg) Global.Model Global.Msg appMsg (Html appMsg)
## using your alias
**`.elm-spa/Generated/Pages.elm`**
import Utils.Spa as Spa
type alias Recipes appMsg =
{ top : Spa.Recipe Params.Top Top.Model Top.Msg Model Msg appMsg
, example : Spa.Recipe Params.Example Example.Model Example.Msg Model Msg appMsg
, notFound : Spa.Recipe Params.NotFound NotFound.Model NotFound.Msg Model Msg appMsg
}
-} -}
type alias Recipe pageRoute pageModel pageMsg layoutModel layoutMsg uiLayoutMsg globalModel globalMsg msg uiMsg = type alias Recipe params pageModel pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg =
Page.Recipe pageRoute pageModel pageMsg layoutModel layoutMsg uiLayoutMsg globalModel globalMsg msg uiMsg Page.Recipe params pageModel pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg
{-| Init {-|
## creating your alias
**`src/Utils/Spa.elm`**
type alias Init model msg =
App.Types.Init model msg Global.Model Global.Msg
## using your alias
**`.elm-spa/Generated/Pages.elm`**
import Utils.Spa as Spa
init : Route -> Spa.Init Model Msg
init route_ =
case route_ of
-- ...
-} -}
type alias Init layoutModel layoutMsg globalModel globalMsg = type alias Init layoutModel layoutMsg globalModel globalMsg =
Page.Init layoutModel layoutMsg globalModel globalMsg Page.Init layoutModel layoutMsg globalModel globalMsg
{-| Update {-|
## creating your alias
**`src/Utils/Spa.elm`**
type alias Update model msg =
App.Types.Update model msg Global.Model Global.Msg
## using your alias
**`.elm-spa/Generated/Pages.elm`**
import Utils.Spa as Spa
update : Msg -> Model -> Spa.Update Model Msg
update msg_ model_ =
case ( msg_, model_ ) of
-- ...
-} -}
type alias Update layoutModel layoutMsg globalModel globalMsg = type alias Update layoutModel layoutMsg globalModel globalMsg =
Page.Update layoutModel layoutMsg globalModel globalMsg Page.Update layoutModel layoutMsg globalModel globalMsg
{-| Bundle {-|
## creating your alias
**`src/Utils/Spa.elm`**
-- if using mdgriffith/elm-ui
import App.Types
import Element exposing (Element)
type alias Bundle msg appMsg =
App.Types.Bundle msg (Element msg) Global.Model Global.Msg appMsg (Element appMsg)
-- if using elm/html
import App.Types
import Html exposing (Html)
type alias Bundle msg appMsg =
App.Types.Bundle msg (Html msg) Global.Model Global.Msg appMsg (Html appMsg)
## using your alias
**`.elm-spa/Generated/Pages.elm`**
import Utils.Spa as Spa
bundle : Model -> Spa.Bundle Msg msg
bundle model_ =
case model_ of
-- ...
-} -}
type alias Bundle layoutMsg uiLayoutMsg globalModel globalMsg msg uiMsg = type alias Bundle layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg =
Page.Bundle layoutMsg uiLayoutMsg globalModel globalMsg msg uiMsg Page.Bundle layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg

View File

@ -11,34 +11,34 @@ module Internals.Page exposing
-} -}
type Page pageRoute pageModel pageMsg uiPageMsg layoutModel layoutMsg uiLayoutMsg globalModel globalMsg msg uiMsg type Page pageParams pageModel pageMsg ui_pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg
= Page (Page_ pageRoute pageModel pageMsg uiPageMsg layoutModel layoutMsg uiLayoutMsg globalModel globalMsg msg uiMsg) = Page (Page_ pageParams pageModel pageMsg ui_pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg)
type alias Page_ pageRoute pageModel pageMsg uiPageMsg layoutModel layoutMsg uiLayoutMsg globalModel globalMsg msg uiMsg = type alias Page_ pageParams pageModel pageMsg ui_pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg =
{ toModel : pageModel -> layoutModel { toModel : pageModel -> layoutModel
, toMsg : pageMsg -> layoutMsg , toMsg : pageMsg -> layoutMsg
, map : (pageMsg -> layoutMsg) -> uiPageMsg -> uiLayoutMsg , map : (pageMsg -> layoutMsg) -> ui_pageMsg -> ui_layoutMsg
} }
-> Recipe pageRoute pageModel pageMsg layoutModel layoutMsg uiLayoutMsg globalModel globalMsg msg uiMsg -> Recipe pageParams pageModel pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg
{-| Recipe docs {-| Recipe docs
-} -}
type alias Recipe pageRoute pageModel pageMsg layoutModel layoutMsg uiLayoutMsg globalModel globalMsg msg uiMsg = type alias Recipe pageParams pageModel pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg =
{ init : pageRoute -> Init layoutModel layoutMsg globalModel globalMsg { init : pageParams -> Init layoutModel layoutMsg globalModel globalMsg
, update : pageMsg -> pageModel -> Update layoutModel layoutMsg globalModel globalMsg , update : pageMsg -> pageModel -> Update layoutModel layoutMsg globalModel globalMsg
, bundle : pageModel -> Bundle layoutMsg uiLayoutMsg globalModel globalMsg msg uiMsg , bundle : pageModel -> Bundle layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg
} }
upgrade : upgrade :
{ page : Page pageRoute pageModel pageMsg uiPageMsg layoutModel layoutMsg uiLayoutMsg globalModel globalMsg msg uiMsg { page : Page pageParams pageModel pageMsg ui_pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg
, toModel : pageModel -> layoutModel , toModel : pageModel -> layoutModel
, toMsg : pageMsg -> layoutMsg , toMsg : pageMsg -> layoutMsg
, map : (pageMsg -> layoutMsg) -> uiPageMsg -> uiLayoutMsg , map : (pageMsg -> layoutMsg) -> ui_pageMsg -> ui_layoutMsg
} }
-> Recipe pageRoute pageModel pageMsg layoutModel layoutMsg uiLayoutMsg globalModel globalMsg msg uiMsg -> Recipe pageParams pageModel pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg
upgrade config = upgrade config =
let let
(Page page) = (Page page) =
@ -67,14 +67,14 @@ type alias Update layoutModel layoutMsg globalModel globalMsg =
{-| Bundle docs {-| Bundle docs
-} -}
type alias Bundle layoutMsg uiLayoutMsg globalModel globalMsg msg uiMsg = type alias Bundle layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg =
{ global : globalModel { global : globalModel
, fromGlobalMsg : globalMsg -> msg , fromGlobalMsg : globalMsg -> msg
, fromPageMsg : layoutMsg -> msg , fromPageMsg : layoutMsg -> msg
, map : (layoutMsg -> msg) -> uiLayoutMsg -> uiMsg , map : (layoutMsg -> msg) -> ui_layoutMsg -> ui_msg
} }
-> ->
{ title : String { title : String
, view : uiMsg , view : ui_msg
, subscriptions : Sub msg , subscriptions : Sub msg
} }