This page covers Elm 0.18
Composing
The parent component
This is the code for the parent component.
module Main exposing (..)
import Html exposing (Html, program)
import Widget
-- MODEL
type alias AppModel =
{ widgetModel : Widget.Model
}
initialModel : AppModel
initialModel =
{ widgetModel = Widget.initialModel
}
init : ( AppModel, Cmd Msg )
init =
( initialModel, Cmd.none )
-- MESSAGES
type Msg
= WidgetMsg Widget.Msg
-- VIEW
view : AppModel -> Html Msg
view model =
Html.div []
[ Html.map WidgetMsg (Widget.view model.widgetModel)
]
-- UPDATE
update : Msg -> AppModel -> ( AppModel, Cmd Msg )
update message model =
case message of
WidgetMsg subMsg ->
let
( updatedWidgetModel, widgetCmd ) =
Widget.update subMsg model.widgetModel
in
( { model | widgetModel = updatedWidgetModel }, Cmd.map WidgetMsg widgetCmd )
-- SUBSCRIPTIONS
subscriptions : AppModel -> Sub Msg
subscriptions model =
Sub.none
-- APP
main : Program Never AppModel Msg
main =
program
{ init = init
, view = view
, update = update
, subscriptions = subscriptions
}
Let's review the important sections of this code.
Model
type alias AppModel =
{ widgetModel : Widget.Model ➊
}
The parent component has its own model. One of the attributes on this model contains the Widget.Model
➊. Note how this parent component doesn't need to know about what Widget.Model
is.
initialModel : AppModel
initialModel =
{ widgetModel = Widget.initialModel ➋
}
When creating the initial application model, we simply call Widget.initialModel
➋ from here.
If you were to have multiple child components, you would do the same for each, for example:
initialModel : AppModel
initialModel =
{ navModel = Nav.initialModel,
, sidebarModel = Sidebar.initialModel,
, widgetModel = Widget.initialModel
}
Or we could have multiple child components of the same type:
initialModel : AppModel
initialModel =
{ widgetModels = [Widget.initialModel]
}
Messages
type Msg
= WidgetMsg Widget.Msg
We use a union type that wraps Widget.Msg
to indicate that a message belongs to that component. This allows our application to route messages to the relevant components (This will become clearer after looking at the update function).
In an application with multiple child components we could have something like:
type Msg
= NavMsg Nav.Msg
| SidebarMsg Sidebar.Msg
| WidgetMsg Widget.Msg
View
view : AppModel -> Html Msg
view model =
Html.div []
[ Html.map➊ WidgetMsg➋ (Widget.view➌ model.widgetModel➍)
]
The main application view
renders the Widget.view
➌. But Widget.view
emits Widget.Msg
so it is incompatible with this view which emits Main.Msg
.
- We use
Html.map
➊ to map emitted messages fromWidget.view
to the type we expect (Msg).Html.map
tags messages coming from the sub view using theWidgetMsg
➋ tag. - We only pass the part of the model that the child component cares about i.e.
model.widgetModel
➍.
Update
update : Msg -> AppModel -> (AppModel, Cmd Msg)
update message model =
case message of
WidgetMsg➊ subMsg➋ ->
let
(updatedWidgetModel, widgetCmd)➍ =
Widget.update➌ subMsg model.widgetModel
in
({ model | widgetModel = updatedWidgetModel }, Cmd.map➎ WidgetMsg widgetCmd)
When a WidgetMsg
➊ is received by update
we delegate the update to the child component. But the child component will only update what it cares about, which is the widgetModel
attribute.
We use pattern matching to extract the subMsg
➋ from WidgetMsg
. This subMsg
will be the type that Widget.update
expects.
Using this subMsg
and model.widgetModel
we call Widget.update
➌. This will return a tuple with an updated widgetModel
and a command.
We use pattern matching again to destructure ➍ the response from Widget.update
.
Finally we need to map the command returned by Widget.update
to the right type. We use Cmd.map
➎ for this and tag the command with WidgetMsg
, similar to what we did in the view.