JavaScript
Elm

elm 0.19 で JSONを使ってjsと通信したメモ


概要

前回は入力と表示を同じファイルにしていたが、分けてみる。


ファイル



  • 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)



参考

ElmとJavaScriptの会話(Port)

elm/json

result