リムでELMポートを書く- 0.3


これは、前の記事への更新は、2010年の破壊的変化に続きますres-elm 結合.要するにinit 関数が壊れたinit and initWithOptions ELMの初期化フラグとELMのWebアプリケーションを許可する.
最近、NPMパッケージと呼ばれるパッケージを公開しましたres-elm そして、プロジェクトのカップルで生産にそれを入れてください.それは簡単にReadMeによって文書化されます、しかし、私はそれが完全なポストに値すると思います.このポストは、どのようにElmを使用してELM 0.19プロジェクトの両方のポートを設定する方法を歩いていきます.

目標:ポートを通してRScriptとELMの間の共有制御


最終製品は、最小限に再現性があり、理解しやすいものであり、必ずしも有用ではない.この場合、私は、この非常に小さな図書館の特徴を示す最高のページが非常に小さいウェブアプリであると思います.あなたはそのようなアプリを見つけることができますin this live demo .
つのテキストボックスを再生する瞬間を取る.最初の1つはrescriptlandに住んでいるが、その入力イベントでは、rescriptは、elmアプリにその内容を送信します.2番目はelmlandに住んでいますが、その入力イベントでは、入力を別のポートを通してリスクリプトスクリプトに送ります.結果は常に一致する2つのテキストボックスです.
通常、私はエルムのアプリの外に住んでいるテキストボックスを持っていることはありません-私はエルムに全体のビューの制御を与えるだろうが、それはアプリケーションが代わりにindexeddbリポジトリのようなものを持っていることを想像しやすいですChicago area COVID-19 tracker , いくつかのJSONデータへのHTTP呼び出し.

ELMセットアップ


基本的なELMプロジェクトを書く方法についての詳細な説明は、この種のポストについての範囲外ですが、完全性のためにここにいくつかのELMコードが欲しいです.
つの基本的なメッセージから始めましょうSendString and UpdateString これは、アプリケーションの内外に情報の流れの2つの方向を表します.
MSGエルム
module Msg exposing (..)

type Msg = SendString String | UpdateString String
ELMポートに慣れているなら、ELMポートのJSONエンコード/デコードに慣れているはずです.これは私が実証しようとしているものの範囲外であるので、ここのストリングはうまくいくでしょう、しかし、JSONを安全に解析することは最高の実行です、そして、あなたは複雑なデータ型のためにそれを必要とします.
私も、このELMアプリの2つのポートが欲しい、再びこのelmアプリのデータの双方向フローを表します.
ポート.エルム
port module Ports exposing (..)

port toReScript : String -> Cmd msg
port toElm : (String -> msg) -> Sub msg
そして今、フクロウの残りの部分を描画します.
メイン.エルム
module Main exposing (main)

import Browser
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (..)
import Http
import Json.Decode
import Models exposing (Model)
import Msg exposing (..)
import Ports

main : Program () Model Msg
main = Browser.element
       { init = init
       , subscriptions = subscriptions
       , update = update
       , view = view
       }

------------------------
init : () -> (Model, Cmd Msg)
init _ = ( Models.init
         , Cmd.none
         )


subscriptions : Model -> Sub Msg
subscriptions model =
    Sub.batch [ Ports.toElm UpdateString --subscribe to incoming string
              ]

update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
    case msg of
        SendString str -> { model | str = str }
                          |> \m -> ( m, Ports.toReScript m.str ) --call outgoing port
        UpdateString val -> { model | str = val }
                       |> \m -> (m, Cmd.none)

view : Model -> Html Msg
view model =
    div [ class "elm-parent" ]
        [ h2 [ class "h2" ] [ text "Controlled by Elm" ]
        , input [ placeholder "enter some text"
                , type_ "text"
                , onInput SendString
                , value model.str
                ] []
        ]
再び、私はこれのあらゆるインチを通過するつもりでありません--私はちょうど参考のためにここにそれを望みます.ご覧のように、メッセージはupdate 関数とonInput イベントは、着信ポートはsubscriptions .

プロジェクトのセットアップ


次に、新しいrescriptプロジェクトを初期化してくださいres-elm そして、bs-dependencies .
最後にインデックスを開きます.resファイルを開き、モジュールを公開します.
open Elm;

完全に


次に、ポートを囲む論理を定義します.これらの関数からポートを作成します.
このコードを詳細に説明するのはこのポストのスコープ外です.基本的に、私がしていることは、入力の値を取得したり設定したりするのに必要な基本的なDOM機能のバインディングを定義することですtarget JavaScriptからevent .
/* setup: simple JS dom interop */
@val @scope("document")
external getElementById: string => Dom.element = "getElementById"

@get external getValue: Dom.element => string = "value"
@set external setValue: (Dom.element, string) => unit = "value"

@set external setOnInput: (Dom.element, Dom.event => unit) => unit = "oninput"

@get external getTarget: Dom.event => Dom.element = "target"

/* get input element */
let inputReScript: Dom.element = getElementById("input-rescript")

レコードをフィールドとして宣言する


ELMアプリを初期化すると、各フィールドは私たちのELMアプリのポートを表すレコードの形式で型パラメータが必要です.The res-elm パッケージは2つのタイプが含まれますElm.sendable<'t> and Elm.subscribable<'t> 我々はelmアプリに情報を送信し、それから情報を購読することができます.
このアプリはちょうど2つのポートで単純なケースですが、私はこのタイプのモジュールを定義する自由を取るつもりです.
module Ports = {
  type t = {
    toElm: Elm.sendable<string>,
    toReScript: Elm.subscribable<string>
  };
};

ELMアプリへのリファレンスを取得する


今我々は我々のタイプを持って、我々は我々のアプリを得ることができます.これは、JavaScriptのELM(v 0.19)ポートを書いている誰にも身近に見えるべきです.The init 関数は一つのフィールドを持つレコードをとるnode 活字Dom.element .
/* get app */

let app: Elm.app<Ports.t> =
  Elm.Main.init({ node: Some(getElementById("elm-target")),
                  flags: None
                });
結果はElm.app それは私たちのポートへのアクセスを与えるので、それらを使用しましょう.

イベントの配線


これはたくさんのように見えます、しかし、我々がしているすべてはDom.element 名前inputReScript と設定oninput aの関数へのイベントDom.event .
The app 早めにメンバーが来たports と同じようにElm パッケージはsend バインドするsend event.target.value , JavaScriptのように.
inputReScript 
  -> setOnInput(event => app.ports.toElm
                           -> Elm.send(event -> getTarget -> getValue));

この次のいずれかを少し簡単に従うことです.ここでは、私はsubscribe 値の設定inputReScript 我々のELMアプリがポートを通して値を送るときはいつでも.
app.ports.toReScript -> Elm.subscribe(str => setValue(inputReScript, str));
インデックスを取得するためにコンパイルします.ビーエス.js

HTMLマークアップにまとめます


今、すべてのそれを残して私たちのHTMLマークアップで一緒にすべてを置くことです.
  ...
  <div class="div-rescript-demo">
    <h2 class="h2">Controlled by ReScript</h2>
    <input class="input" id="input-rescript"
           placeholder="enter some text" type="text" />
  </div>
  <div id="elm-target"></div>
</div><!--end container div-->
<script src="scripts/elm/index.js"></script>
<script src="scripts/rescript/src/Index.bs.js" type="module"></script>
これは私たちのアプリを期待しているすべてを与えます:1“入力スクリプト”テキストボックス、2“elmターゲット”div、3)スクリプトへの参照.
それは我々のプロジェクトを終了!再び、完成した例を見つけることができますon my demo site , and full source here . あなたが質問をするならば私に知らせてください!