I've been running my Meep app for a couple years now and logging all my board games game into it. It has worked fine but one thing was constantly annoying me; I couldn't use 1Password with it. Actually, I could, but I needed to enter each field and add space then remove it to trigger the message dispatch, pretty annoying. Then I saw a friend of mine trying to register with its password manager and failed and I thought, alright, enough.
Why is it not working in the first place? That's pretty simple in the end. Elm doesn't expose a onChange
(which handles "change"
) event handler (Why? Go figure...), it only exposes onInput
(which handles "input"
). The 1Password extensoin doesn't fire an "input"
change, it fires a "change"
so the that's why it doesn't make it to Elm.
My first solution was to add a JS event handler that would bind onchange
to a function that would fire to a port and the app would forward the external change to the inside of the app.
I'm sure you thought "Does it needs to be that complicated?" while reading that sentence. It does not, got to The Solution if you wanna head straight to the solution. But you can continue reading and see how hard Elm is trying to make it.
So I try that, I add a Attributes.attribute "onchange" "console.log(event)
just to see if that works. It should right? It does not. Calling Attributes.attribute
will prefix the key of the attribute with data-
if you try to add an attribute that is an event handler (Again, why? Go figure).
In the end I needed to call Elm.Kernel.VirtualDom.attribute "onchange", "console.log(event)"
, to get that working. I was about to work on the port solution until I actually started using my head.
It turns out the solution is far easier and doesn't need any ports. Elm exposes Html.Events.stopPropagationOn
which allows to bind custom event (like "change"
). So I just made sure my Form.input
function had both onInput
and a custom onChange
as event handler and the deal is done. See below for the basic gist of it
module Form exposing (input)
import Html exposing (Html Attribute)
import Html.Events exposing (stopPropagationOn, targetValue)
import Json.Decode
onChange : (String -> msg) -> Attribute msg
onChange tagger =
-- This is pretty much the same code as the `onInput` function with `"change"` instead
stopPropagationOn "change" (Json.Decode.map (\x -> ( x, True )) (Json.Decode.map tagger targetValue))
input = msg -> Html msg
input msg =
Html.input [ onInput msg, onChange msg ] []
No more complicted than that. I know it's simple in the end but I figured it might help some beginners.