Portを通したElm Record と JavaScript Object の互換性を試してみました。
一般的にPortを使った場合、ElmとJavaScriptの間で自動的に以下のようなデータ変換が行われます。JavaScript Interop
Boolean – Elm と JSの両方に型は存在します。
String – Elm と JSの両方に型は存在します。
Number – Elm int と float は JS numbersに対応します
List – Elm List はJS arrayに対応します
Array – Elm ArrayはJS arrayに対応します
Tuple – Elm TupleはJS array( fixed-length, mixed-type)に対応します
Record – Elm RecordはJavaScript objectに対応します。
Maybes – Elm の Nothing とJust 42 は、JSのnull と 42 に対応します。
Json – Elm の Json.Encode.Value はJSのJSONに対応します。
今回はElm Recordが本当にJavaScript objectに対応しているのかを検証してみました。以下のようなElmのレコードを、portでJavaScriptに渡した時、ElmレコードはJavaScript objectに変換されます。JavaScript objectで少しの修正を行い、portを通してElmに戻してみます。
type alias Model =
{ name : String
, age : Int
, height : Float
, married : Bool
}
elm2jsとjs2elmという2つのportで以下のような変換が行われます。
elm2js js2elm
Elm record --> JavaScript object --> Elm record
実際の実行結果の画面です。ElmのHTMLでname=大谷、age=23、height=193.6と入力すると、Elmレコードが、JavaScript objectとしてJavaScriptに渡され、name=大谷-san、age=23+1、height=193.6+1.5のJavaScript objectに更新されて、Elmに戻されます。Elmレコードが「大谷-san - 24 - 195.1 - True」と表示されていますね。ちなみにmarriedもJavaScriptでFalseからTrueにちゃんと変更されていますね。
以下のような流れで、Elmから渡されたレコードが、JavaScriptで更新され、Elmに戻されます。
Elm {name="大谷",age=23,height=193.6,married=False} ==>
JavaScript {name:"大谷",age:23,height:193.6,married:False} ==>
JavaScript {name="大谷"+"-san",age=23+1,height=193.6+1.5,married=! False} ==>
JavaScript {name:"大谷-san",age:24,height:195.1,married:True} ==>
Elm {name="大谷",age=24,height=195.1,married=True}
それではElmのコードを見てください。
port module PortsTest exposing (..)
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (onClick, onInput)
-- MAIN
main =
Html.program
{ init = init
, view = view
, update = update
, subscriptions = subscriptions
}
-- MODEL
type alias Model =
{ name : String
, age : Int
, height : Float
, married : Bool
}
-- MSG
type Msg =
AddName String
| AddAge String
| AddHeight String
| ToJs
| FromJs Model
-- INIT
init : (Model, Cmd Msg)
init =
(Model "" -1 -1.0 False , Cmd.none)
-- UPDATE
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
AddName txt ->
( {model | name = txt }, Cmd.none )
AddAge txt ->
( {model | age = getInt(txt) }, Cmd.none )
AddHeight txt ->
( {model | height = getFloat(txt) }, Cmd.none )
ToJs ->
( model, elm2js model )
FromJs newmodel ->
( newmodel, Cmd.none )
getInt txt =
case String.toInt(txt) of
Ok i -> i
_ -> -1
getFloat txt =
case String.toFloat(txt) of
Ok f -> f
_ -> -1.0
-- OUTGOING PORT
port elm2js : Model -> Cmd msg
-- VIEW
view : Model -> Html Msg
view model =
div []
[ input [ placeholder "名前", onInput AddName ] []
, input [ placeholder "年齢", onInput AddAge ] []
, input [ placeholder "身長", onInput AddHeight ] []
, button [ onClick ToJs ] [ text "ToJS" ]
, div [] [ text model.name
, text " - "
, text <| toString model.age
, text " - "
, text <| toString model.height
, text " - "
, text <| toString model.married
]
]
-- INCOMING PORT
port js2elm : (Model -> msg) -> Sub msg
-- SUBSCRIPTIONS
subscriptions : Model -> Sub Msg
subscriptions model =
js2elm FromJs
JavaScriptのコードは以下の通りです。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script type="text/javascript" src="elm.js"></script>
</head>
<body>
</body>
<script type="text/javascript">
function convert(myobject){
return {
name: myobject.name + "-san",
age: myobject.age + 1,
height: myobject.height + 1.5,
married: ! myobject.married
}
}
var app = Elm.PortsTest.fullscreen();
app.ports.elm2js.subscribe(function(myobject) {
console.log("myobject="+JSON.stringify(myobject));
var newobject = convert(myobject);
console.log("newobject="+JSON.stringify(newobject));
app.ports.js2elm.send(newobject);
});
</script>
</html>
必要なパッケージをインストールします。
elm-package install elm-lang/html
コンパイルします。
elm-make Main.elm --output elm.js
サーバを起動します。
elm-reactor -a=www.mypress.jp -p=3030
ちなみにES6のJavaScript objectのデストラクチャリングを使えば、以下のようにnameだけを受け取るように書くことができます。
#
app.ports.elm2js.subscribe(function( {name} ) {
console.log("name="+name);
#
またElmからJavaScriptへ、そのまま変換無しでvalue(?)を渡す例は、次の過去記事を参照してください。ElmとJavaScriptの会話(Port) - Qiita
今回は以上です。