Shared
Whether you're sharing layouts or information between pages, the Shared
module is the place to be!
Flags
If you have initial data you want to pass into your Elm application, you should provide it via Flags
.
When you create a project with elm-spa init
, a file will be created at public/main.js
:
// (in public/main.js)
var flags = null
var app = Elm.Main.init({ flags: flags })
The value passed into the flags
needs to match up with the type of Shared.Flags
, for it to be passed into Shared.init
.
Here's an example:
// (in public/main.js)
var flags = { project: "elm-spa", year: 2020 }
-- (in src/Shared.elm)
type alias Flags =
{ project : String
, year : Int
}
Once you get comfortable with flags, I recommend always using Json.Value
from the elm/json package as your Flags:
import Json.Decode as Json
type alias Flags =
Json.Value
type alias InitialData =
{ project : String
, year : Int
}
decoder : Json.Decoder InitialData
decoder =
Json.map2 InitialData
(Json.field "project" Json.string)
(Json.field "year" Json.int)
init : Flags -> Url -> Key -> ( Model, Cmd Msg )
init flags url key =
case Json.decodeValue decoder flags of
Ok initialData -> -- Initialize app
Err reason -> -- Handle failure
This way, you can create a decoder to gracefully handle the JSON being sent into your Elm application.
Learn more about Flags in the official Elm guide.
Model
All data in Shared.Model
will persist across page navigation.
By default, it only contains key
and url
, which are required for the programmatic navigation and reading URL information in your application.
This makes it a great choice for things like logged-in users, dark-mode, or any other data displayed on shared components needed by navbars or footers.
type alias Model =
{ key : Key
, url : Url
, user : Maybe User
}
Here we added a user
field that we can update with the next function!
update
The Shared.update
function is just like a normal update
function in Elm. It takes in messages and returns the latest version of the Model
. In this case, the Model
is the Shared.Model
mentioned above.
type Msg
= SignIn User
| SignOut
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
SignIn user ->
( { model | user = Just user }
, Cmd.none
)
SignOut ->
( { model | user = Nothing }
, Cmd.none
)
This is just an example of using update
with the user
field we added earlier. Let's call those messages from our view.
view
The Shared.view
function is a great place to render things that should persist across page transitions. It comes with more than just a Model
, so you can insert the page
wherever you'd like:
import Components.Navbar as Navbar
import Components.Footer as Footer
view :
{ page : Document msg
, toMsg : Msg -> msg
}
-> Model
-> Document msg
view { page, toMsg } model =
{ title = page.title
, body =
[ Navbar.view
{ user = model.user
, onSignIn = toMsg SignIn
, onSignOut = toMsg SignOut
}
, div [ class "page" ] page.body
, Footer.view
]
}
Using the toMsg
function passed in the first argument, we're able to convert Shared.Msg
to the same msg
that our view
function returns.
If you want components to send Shared.Msg
, make sure to use that function first!
Let's take a look at Components now!