Search code examples
javascriptelmffi

Pass event handler from elm to JS through port


In JS we have a function like this:

const handleClick = event => {
  event.preventDefault();
  // some more code
}

<button onClick={this.handleClick}>Add to bag</button>

My Elm code:

main.elm

port addToBag : Value -> Cmd msg

type Msg
  = ClickBag Item

update msg model =
  case msg of
   ClickBag item ->
      let
        data =
          Encode.object
            [ ("price", Encode.string item.price)
            , ("sku", Encode.string item.sku)
            ]
      in
        ( model, addToBag data )

view model =
  button [onClick ClickBag model.item] [text "Add to bag"]

index.html

<div id="elm"></div>

<script type="text/javascript">

  const addToBag = (params, btnSelector = null) => {
    console.log('json data', params)
  }

  const ElmApp = Elm.Main.init(document.getElementById('elm'))

  ElmApp.ports.addToBag.subscribe(addToBag)

</script>

For now, I can get the params value but don't know how to pass the event handler(like event in JS code) from Elm file to JS through port(pass that value to btnSelector), so I can use that value for the legacy code. Anyone can help me?

Thanks a lot!


Solution

  • You can extract the full event object as a Value and pass that through the port.

    port toJS : Value -> Cmd msg
    
    onClickWithValue : (Value -> msg) -> Attribute msg 
    onClickWithValue toMsg =
        on "click" (Json.map toMsg Json.value)
    

    and in update have something like

    update : Msg -> Model -> ( Model, Cmd Msg )
    update msg model =
        case msg of
            SomeTag value ->
                ( { model | count = model.count + 1 }, toJS value )
    

    on a side note, if the values you pass to JS are basic values (Bool, Int, Float, String, Maybe, Json.Decode.Value and List, Array, tuples or records of the previous types) , you can just pass them as a record. So, in your case, you can have:

    type alias Item = 
        { price: String 
        , sku: String 
        }
    
    port addToBag : (Item, Value) -> Cmd msg
    
    type Msg
      = ClickBag Item Value
    
    view model =
      button [onClickWithValue (ClickBag model.item)] [text "Add to bag"]
    
    update msg model =
      case msg of
       ClickBag item value ->
            ( model, addToBag ( item, value ) )