概要
前回は入力と表示を同じファイルにしていたが、分けてみる。
ファイル
-
subscribe
で、 Elm -> javascript -
send
で、javascript -> Elm
app/src/card.js
'use strict';
require("./styles.scss");
const handouts = require('./Card/Main');
const creator = require('./Card/HandoutCreator');
const handoutsApp = handouts.Elm.Main.init({flags: 0, node: document.getElementById('cards')});
const creatorApp = creator.Elm.Main.init({flags: null, node: document.getElementById('cardCreator')});
creatorApp.ports.toJs.subscribe(data => {
const json = JSON.stringify(data);
handoutsApp.ports.fromJs.send(json);
})
-
port module
でportを使うモジュールを明示 -
subscriptions
にメッセージを登録 -
update
でjsonをモデルにデコード
app/src/Card/Main.elm
port module Main exposing (main)
import Browser
import Card.HandoutList as HandoutList
import Card.InputModel
import Html exposing (..)
import Html.Attributes exposing (..)
import Json.Decode
-- In ports
port fromJs : (String -> msg) -> Sub msg
main : Program Int Model Msg
main =
Browser.element
{ init = init
, update = update
, view = view
, subscriptions = \_ -> Sub.batch [ fromJs FromJs ]
}
-- model
type alias Model =
{ inputModel : Card.InputModel.Model
, handoutListModel : HandoutList.Model
}
initialModel : Model
initialModel =
{ inputModel = Card.InputModel.initialModel
, handoutListModel = HandoutList.initialModel
}
init : Int -> ( Model, Cmd Msg )
init flags =
( initialModel, Cmd.none )
type Msg
= HandoutListMsg HandoutList.Msg
| FromJs String
-- update
update : Msg -> Model -> ( Model, Cmd Msg )
update message model =
case message of
HandoutListMsg subMsg ->
let
( updatedHandoutListModel, handoutListCmd ) =
HandoutList.update subMsg model.inputModel.title model.handoutListModel
in
( { model | handoutListModel = updatedHandoutListModel }, Cmd.map HandoutListMsg handoutListCmd )
FromJs json ->
let
r =
Json.Decode.decodeString Card.InputModel.decoder json
-- decodeに成功したら、InputModelを。失敗したら元の値を返す。
im =
case r of
Ok m ->
m
Err _ ->
model.inputModel
in
( { model | inputModel = im }, Cmd.none )
-- subscription
subscriptions : Model -> Sub Msg
subscriptions model =
Sub.none
-- view
view : Model -> Html Msg
view model =
div [ class "container" ]
[ Html.map HandoutListMsg (HandoutList.view model.handoutListModel)
]
- jsonにエンコードしてjavascriptに送信
app/src/Card/HandoutCreator.elm
port module Main exposing (main)
import Browser
import Card.InputModel exposing (Model, encode)
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (onClick, onInput)
import Json.Encode
port toJs : Json.Encode.Value -> Cmd msg
main : Program (Maybe String) Model Msg
main =
Browser.element
{ init = init
, update = update
, view = view
, subscriptions = \_ -> Sub.none
}
init : Maybe String -> ( Model, Cmd Msg )
init flags =
( Card.InputModel.initialModel, Cmd.none )
type Msg
= NoOp
| UpdateInput String
-- update
update : Msg -> Model -> ( Model, Cmd Msg )
update message model =
case message of
NoOp ->
Tuple.pair model Cmd.none
UpdateInput s ->
let
newModel =
{ model | title = s }
in
Tuple.pair newModel (toJs (encode newModel))
-- view
view : Model -> Html Msg
view model =
div []
[ handoutInput model
]
handoutInput : Model -> Html Msg
handoutInput model =
Html.form []
[ label [ attribute "for" "inputTitle" ]
[ text "タイトル"
]
, input [ attribute "type" "text", id "inputTitle", class "browser-default", onInput UpdateInput, value model.title ] []
]
- デコーダーとエンコードの定義
app/src/Card/InputModel.elm
module Card.InputModel exposing (Item, Model, decoder, encode, initialModel)
import Json.Decode as D
import Json.Encode as E
type alias Item =
String
type alias Model =
{ title : Item
}
initialModel : Model
initialModel =
{ title = ""
}
encode : Model -> E.Value
encode m =
E.object
[ ( "title", E.string m.title )
]
decoder : D.Decoder Model
decoder =
D.map Model
(D.field "title" D.string)