Elm Language
エルム建築
サーチ…
前書き
アプリケーションを構造化するための推奨される方法は、「Elm Architecture」と呼ばれています。
最も簡単なプログラムの構成要素はmodel更新されることがありますすべてのデータ、union型格納したレコードMsgプログラムがそのデータを更新方法を定義して、機能のupdateモデルとなりますMsg 、新たなモデルを返し、関数viewモデルを取り、ページに表示されるHTMLを返します。関数がMsg返すMsgに、Elmランタイムはそれを使用してページを更新します。
初心者向けプログラム
HTMLは主に学習目的のためにbeginnerProgram持っています。
beginnerProgramは、サブスクリプションまたは実行中のコマンドを処理できません。
DOMイベントからのユーザー入力のみを処理できます。
modelをレンダリングするためのviewと状態の変更を処理するupdate関数が必要です。
例
この最小限のbeginnerProgram例を考えてみましょう。
この例のmodelは、単一のInt値で構成されています。
update関数には、 modelに格納されているIntをインクリメントするブランチが1つしかありません。
viewはモデルをレンダリングし、クリックDOMイベントを添付します。
初期化とビルドでサンプルをビルドする方法を参照してください
import Html exposing (Html, button, text)
import Html exposing (beginnerProgram)
import Html.Events exposing (onClick)
main : Program Never
main =
beginnerProgram { model = 0, view = view, update = update }
-- UPDATE
type Msg
= Increment
update : Msg -> Int -> Int
update msg model =
case msg of
Increment ->
model + 1
-- VIEW
view : Int -> Html Msg
view model =
button [ onClick Increment ] [ text ("Increment: " ++ (toString model)) ]
プログラム
あなたのアプリケーションが初期化のための外部データを必要としないとき、 programは良い選択です。
サブスクリプションとコマンドを処理することができるため、HTTP通信やJavaScriptを使用したinteropなど、I / Oを処理する機会が増えます。
初期状態は、モデルと共に起動コマンドを返すために必要です。
programの初期化には、 model 、 view 、およびupdateとともにsubscriptionsを提供する必要がありsubscriptions 。
型定義を参照してください:
program :
{ init : ( model, Cmd msg )
, update : msg -> model -> ( model, Cmd msg )
, subscriptions : model -> Sub msg
, view : model -> Html msg
}
-> Program Never
例
最も簡単な方法を説明すると、 サブスクリプションを使用する方法は、JavaScriptを使用した単純なポート通信をセットアップすることです。
初期化し、 HTMLに組み込み / 埋め込みでサンプルをビルドする方法を参照してください
port module Main exposing (..)
import Html exposing (Html, text)
import Html exposing (program)
main : Program Never
main =
program
{ init = init
, view = view
, update = update
, subscriptions = subscriptions
}
port input : (Int -> msg) -> Sub msg
-- MODEL
type alias Model =
Int
init : ( Model, Cmd msg )
init =
( 0, Cmd.none )
-- UPDATE
type Msg = Incoming Int
update : Msg -> Model -> ( Model, Cmd msg )
update msg model =
case msg of
Incoming x ->
( x, Cmd.none )
-- SUBSCRIPTIONS
subscriptions : Model -> Sub Msg
subscriptions model =
input Incoming
-- VIEW
view : Model -> Html msg
view model =
text (toString model)
<!DOCTYPE html>
<html>
<head>
<script src='elm.js'></script>
</head>
<body>
<div id='app'></div>
<script>var app = Elm.Main.embed(document.getElementById('app'));</script>
<button onclick='app.ports.input.send(1);'>send</button>
</body>
</html>
フラグ付きプログラム
programWithFlagsは、 programとの違いが1つだけあります。
JavaScriptからの初期化時にデータを受け入れることができます:
var root = document.body;
var user = { id: 1, name: "Bob" };
var app = Elm.Main.embed( root, user );
JavaScriptから渡されたデータはFlagsと呼ばれます。
この例では、JavaScriptオブジェクトをユーザー情報とともにElmに渡していますが、フラグの型エイリアスを指定することをお勧めします。
type alias Flags =
{ id: Int
, name: String
}
フラグはinit関数に渡され、初期状態を生成します。
init : Flags -> ( Model, Cmd Msg )
init flags =
let
{ id, name } =
flags
in
( Model id name, Cmd.none )
型シグネチャとの違いがわかるかもしれません:
programWithFlags :
{ init : flags -> ( model, Cmd msg ) -- init now accepts flags
, update : msg -> model -> ( model, Cmd msg )
, subscriptions : model -> Sub msg
, view : model -> Html msg
}
-> Program flags
初期化コードはほとんど同じですが、これはinit関数だけが異なるためです。
main =
programWithFlags
{ init = init
, update = update
, view = view
, subscriptions = subscriptions
}
親子通信の片方向
例は、コンポーネントの構成と、親から子への一方向のメッセージの受け渡しを示しています。
コンポーネントの構成は、 Html.App.mapメッセージのタグ付けに依存しHtml.App.map
例
初期化とビルドでサンプルをビルドする方法を参照してください
module Main exposing (..)
import Html exposing (text, div, button, Html)
import Html.Events exposing (onClick)
import Html.App exposing (beginnerProgram)
main =
beginnerProgram
{ view = view
, model = init
, update = update
}
{- In v0.18.0 HTML.App was collapsed into HTML
Use Html.map instead of Html.App.map
-}
view : Model -> Html Msg
view model =
div []
[ Html.App.map FirstCounterMsg (counterView model.firstCounter)
, Html.App.map SecondCounterMsg (counterView model.secondCounter)
, button [ onClick ResetAll ] [ text "Reset counters" ]
]
type alias Model =
{ firstCounter : CounterModel
, secondCounter : CounterModel
}
init : Model
init =
{ firstCounter = 0
, secondCounter = 0
}
type Msg
= FirstCounterMsg CounterMsg
| SecondCounterMsg CounterMsg
| ResetAll
update : Msg -> Model -> Model
update msg model =
case msg of
FirstCounterMsg childMsg ->
{ model | firstCounter = counterUpdate childMsg model.firstCounter }
SecondCounterMsg childMsg ->
{ model | secondCounter = counterUpdate childMsg model.secondCounter }
ResetAll ->
{ model
| firstCounter = counterUpdate Reset model.firstCounter
, secondCounter = counterUpdate Reset model.secondCounter
}
type alias CounterModel =
Int
counterView : CounterModel -> Html CounterMsg
counterView model =
div []
[ button [ onClick Decrement ] [ text "-" ]
, text (toString model)
, button [ onClick Increment ] [ text "+" ]
]
type CounterMsg
= Increment
| Decrement
| Reset
counterUpdate : CounterMsg -> CounterModel -> CounterModel
counterUpdate msg model =
case msg of
Increment ->
model + 1
Decrement ->
model - 1
Reset ->
0
Html.App.mapによるメッセージタグ付け
コンポーネントは独自のメッセージを定義し、放出されたDOMイベントの後に送信されます。 親子通信からのCounterMsg
type CounterMsg
= Increment
| Decrement
| Reset
このコンポーネントのビューはCounterMsgタイプのメッセージを送信するため、ビュータイプシグネチャはHtml CounterMsgです。
親コンポーネントのビュー内でcounterViewを再利用できるようにするには、すべてのCounterMsgメッセージを親のMsg渡す必要があります。
この手法をメッセージタグ付けといいます 。
親コンポーネントは、子メッセージを渡すためのメッセージを定義する必要があります。
type Msg
= FirstCounterMsg CounterMsg
| SecondCounterMsg CounterMsg
| ResetAll
FirstCounterMsg Incrementはタグ付きメッセージです。
counterViewにタグ付きメッセージを送信させるには、 Html.App.map関数を使用する必要があります。
Html.map FirstCounterMsg (counterView model.firstCounter)
HTML.Appパッケージは 、 v0.18.0 HTMLパッケージにv0.18.0れv0.18.0
counterViewにタグ付きメッセージを送信させるには、 Html.map関数を使用する必要があります。
Html.map FirstCounterMsg (counterView model.firstCounter)
これは型シグネチャHtml CounterMsg -> Html Msg変更するので、親ビュー内でカウンタを使用し、親の更新関数で状態更新を処理することができます。