What's the idiomatic way to wrap generic inner content in a model?

Let’s take, as an example, an expandable “component” similar to the <details> HTML element. It may wrap anything, including itself.

The model needs three things:

  • a label;
  • an isExpanded boolean flag;
  • some generic inner content.

The update function would either handle the message or forward it to the inner content, and the view function would render itself and the inner content, too. To do that, they need to know the view and update functions for the inner content—and probably its message type, too, though I’m not solid on that part.

What’s the idiomatic way to do this in Elm?

Furthermore, could it be done without encoding the type of the inner content in the signature of the parent expandable? For instance, an expandable element that contains another expandable element that contains a button might have the type signature of Expandable Expandable Html msg. If the data to be displayed using this “component” is dynamic and not known until runtime (as is the case with my application), this approach wouldn’t work.

Here’s the idiomatic way, I would say: https://ellie-app.com/rLnvFHWfWdNa1

Make a function:

expandable :
    { label : Html msg
    , content : Html msg
    , isExpanded : Bool
    , onToggle : Bool -> msg
    }
    -> Html msg
expandable { label, content, isExpanded, onToggle } =
    Html.div []
        [ Html.button [ Html.Events.onClick (onToggle (not isExpanded)) ]
            [ label ]
        , if isExpanded then
            content

          else
            Html.text ""
        ]

The function:

  • Takes a label, as you mentioned (though it does not need to be in the model).
  • Takes content to show when expanded – but it doesn’t care about it in any way.
  • Takes whether or not the thing is expanded or not – that’s the only state we have that changes over time.
  • Takes a function where you can plug in a message for when you toggle the expander. This way, the function does not need to know anything about exactly which expander in your app it is, that’s up to the caller.
1 Like

I think I can find a good use for <details> too! I’m not sure if it helps at all, but you can simply add open or closed for your isExpanded boolean. I’m sure @lydell would know a better way to do that then me!

<details open>
  <summary>System Requirements</summary>
  <p>
    Requires a computer running an operating system. The computer must have some
    memory and ideally some kind of long-term storage. An input device as well
    as some form of output device is recommended.
  </p>
</details>

Would you even need an onToggle button?

If you’re using a default HTML <details> then you don’t need to control it manually. For example

For example...

This is an HTML native accordion!

Yeah, <details> works great without any setup!

If you need to control it using code – like automatically opening or closing it in certain situations – I’ve never had any luck using it, though.

Either way, I interpreted the thread to not be about a <details>-like “component” specifically, but rather about how to wrapping generic content in general (just using an expandable thing as it is a small example).

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.