This page covers Elm 0.18
合成
親コンポーネント
これは親コンポーネントのコードです。
module Main exposing (..)
import Html exposing (Html, program)
import Widget
-- モデル
type alias AppModel =
{ widgetModel : Widget.Model
}
initialModel : AppModel
initialModel =
{ widgetModel = Widget.initialModel
}
init : ( AppModel, Cmd Msg )
init =
( initialModel, Cmd.none )
-- メッセージ
type Msg
= WidgetMsg Widget.Msg
-- VIEW
view : AppModel -> Html Msg
view model =
Html.div []
[ Html.map WidgetMsg (Widget.view model.widgetModel)
]
-- 更新
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 : AppModel -> Sub Msg
subscriptions model =
Sub.none
-- APP
main : Program Never AppModel Msg
main =
program
{ init = init
, view = view
, update = update
, subscriptions = subscriptions
}
このコードの重要なセクションを見てみましょう。
モデル
type alias AppModel =
{ widgetModel : Widget.Model ➊
}
親コンポーネントには独自のモデルがあります。このモデルの属性の1つにWidget.Model➊が含まれています。この親コンポーネントは Widget.Modelの中身を知る必要はないことに注意してください。
initialModel : AppModel
initialModel =
{ widgetModel = Widget.initialModel ➋
}
最初のアプリケーションモデルを作成するときは、単にここから Widget.initialModel➋を呼び出します。
子コンポーネントが複数ある場合は、それぞれの子コンポーネントを同じように実行します。たとえば、次のようになります。
initialModel : AppModel
initialModel =
{ navModel = Nav.initialModel,
, sidebarModel = Sidebar.initialModel,
, widgetModel = Widget.initialModel
}
あるいは、同じタイプの複数の子コンポーネントを持つこともできます。
initialModel : AppModel
initialModel =
{ widgetModels = [Widget.initialModel]
}
メッセージ
type Msg
= WidgetMsg Widget.Msg
メッセージがそのコンポーネントに属していることを示すために Widget.Msgをラップするユニオン型を使用します。これにより、アプリケーションが関連するコンポーネントにメッセージをルーティングできるようになります(これは、update関数を見ればより明確になります)。
複数の子コンポーネントを持つアプリケーションでは、次のようなものがあります。
type Msg
= NavMsg Nav.Msg
| SidebarMsg Sidebar.Msg
| WidgetMsg Widget.Msg
表示
view : AppModel -> Html Msg
view model =
Html.div []
[ Html.map➊ WidgetMsg➋ (Widget.view➌ model.widgetModel➍)
]
メインアプリケーションの viewはWidget.viewをレンダリングします。しかし、 Widget.viewはWidget.Msgを送出するので、 Main.Msgを送出するこのビューと互換性がありません。
Html.map➊を使用して、放出されたメッセージをWidget.viewから期待されるタイプ(Msg)にマッピングします。Html.mapタグはWidgetMsgタグを使ってサブビューから来るメッセージにタグを付けます。- 子コンポーネントが気にするモデルの部分、つまり
model.widgetModelのみを渡します。
更新
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)
WidgetMsg➊がupdateによって受け取られると、子コンポーネントに更新を委譲します。しかし子コンポーネントは自分が担当するwidgetModel属性だけを更新します。
パターンマッチングを使用して WidgetMsgからsubMsg➋を抽出します。この subMsgはWidget.updateが予期する型になります。
この subMsgとmodel.widgetModelを使って、 Widget.update➌を呼び出します。これは更新された widgetModelとコマンドを含むタプルを返します。
Widget.updateからの応答を分解➍するためにパターンマッチングを使います。
最後に、 Widget.updateによって返されたコマンドを正しい型に対応付ける必要があります。このために Cmd.map➎を使い、WidgetMsgでコマンドにタグを付けます。これは、ビューで行ったのと同様です。