LoginSignup
6
2

More than 5 years have passed since last update.

Portを通したElm Record と JavaScript Object の互換性

Last updated at Posted at 2018-04-14

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にちゃんと変更されていますね。

image.png

 以下のような流れで、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のコードを見てください。

Main.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のコードは以下の通りです。

index.html
<!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だけを受け取るように書くことができます。

index.html
#
        app.ports.elm2js.subscribe(function( {name} ) {
         console.log("name="+name);
#

 またElmからJavaScriptへ、そのまま変換無しでvalue(?)を渡す例は、次の過去記事を参照してください。ElmとJavaScriptの会話(Port) - Qiita

今回は以上です。

6
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
2