'How to trigger Cmd from view in Elm

I'm new to Elm and I ran into this problem...

We get translations for our page using something like:

case (translate translation.id) of
     Success: -> translation
     Failure: -> translation.id

Where translate just finds translation.id in a dictionary and it may or may not be there. There are no runtime errors because you get a string either way, but we would like to log the missing translation to a rest service logger. But Elm hates side effects in the view that doesn't stem from html events so I'm not sure how to handle this.

Obviously in regular JS you could just crowbar in a fetch inside the failure case block and then return a string afterwards but that doesn't seem to be possible in Elm?

elm


Solution 1:[1]

You need to move the effects part of your code into the update function. In this case I suggest doing it when the translation arrives. There you will want something like

    OnTranslation translation ->
        ( { model | translation = translation } -- attach to model
        , case translate translation.id of
            Ok _ ->
                Cmd.none

            Err err ->
                -- register with the error logger
                logMissingTranslation translation.id
        )

Solution 2:[2]

@Odin Thorsen, use Html.node and Html.Attributes.attribute to reference a custom element in Elm:

-- VIEW


view : Model -> Html.Html ()
view model =
    Html.div []
        [ translation model "key"
        , translation model "junk key"
        ]


translation : Model -> String -> Html.Html ()
translation { translations } id =
    Html.node "translated-text"
        [ Html.Attributes.attribute "translation-id" id
        , Html.Attributes.attribute "translation-text" <| translate translations id
        ]
        []


translate : Dict.Dict String String -> String -> String
translate translations id =
    case Dict.get id translations of
        Just value ->
            value

        Nothing ->
            ""

And in Javascript, use customElements.define to create the custom element:

    customElements.define("translated-text", class extends HTMLElement {
      constructor() { super(); }
      connectedCallback() { }
      attributeChangedCallback() { this.setTextAndLogFailure(); }
      static get observedAttributes() { return ["translation-id", "translation-text"]; }
      
      setTextAndLogFailure() {
        var id = this.getAttribute("translation-id");
        var text = this.getAttribute("translation-text");
        if (text === null) return;
        this.textContent = text;
        if (!text.length) alert("Unkown translation id: " + id);
      }
    });

You'd replace alert("Unkown translation id: " + id); with the logging fetch.


Here is an Ellie with a full solution.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Simon H
Solution 2