Working with menus in elm-ui
Tags: elm, elmui, beginners
Published: 2018-12-31 02:56:17

---
title:  Working with menus in elm-ui
published: true
description: 
tags: elm, elm-ui, beginner
---

Recently had to figure out how to get a modal sort of menu to open and close correctly using the `mdgriffith/elm-ui` package in Elm 0.19. For those unfamiliar, the `elm-ui` package allows you to create front-end interfaces without resorting to CSS or HTML (with occasional exceptions). If you are working in Elm, I highly recommend it. If you aren't working in Elm yet, I would point to `elm-ui` as one of its selling points and a reason to try it out.

Here is the very basic concept in an [ellie](https://ellie-app.com/4jHGYY9PK6Ja1). 
Our simple `model` has two fields: count and menu. It is defined thus:
```
type alias Model =
    { count : Int, menu : Bool }


initialModel : Model
initialModel =
    { count = 0, menu = False }
```

Here is the view code:
```
view : Model -> Html Msg
view model =
    Element.layout [ Element.Background.color (Element.rgb255 30 30 30), padding 10]
    <| column [spacing 10, width <| px 200, Element.Background.color (Element.rgb255 200 30 10)] 
           [ el [ width fill
                , padding 8
                , Element.Font.center
                , Element.Background.color <| 
                      Element.rgb255 120 120 180
                ] <| 
                     Element.text (String.fromInt model.count)
           , el [ centerX
                , Element.Events.onClick OpenMenu
                , Element.below <| myMenu model
                ] <| 
                    Element.text "I'm a menu!"
           ]
```

And finally our `Msgs` and `update` function fill out the Elm Architecture for our demo app:
```
type Msg
    = Increment
    | Decrement
    | OpenMenu
    | CloseMenu


update : Msg -> Model -> Model
update msg model =
    case msg of
        Increment ->
            { model | count = model.count + 1 }

        Decrement ->
            { model | count = model.count - 1 }
            
        OpenMenu -> 
            { model | menu = True }
            
        CloseMenu -> 
            { model | menu = False }
```

If you try the ellie link you'll find that the menu opens to reveal the buttons for incrementing and decrementing the model count. Easy peasy. BUT the menu stays open. The first step to fix that is to change the menu element's `onClick` attribute from an `OpenMenu` action to a `ToggleMenu` action.
So now we have 
```
type Msg
    = Increment
    ...
    | ToggleMenu

update : Msg -> Model -> Model
update msg model =
    case msg of
       ...
        ToggleMenu -> { model | menu = not model.menu }

view : Model -> Html Msg
view model =
    ...
    , el [ centerX
                , Element.Events.onClick ToggleMenu
                , Element.below <| myMenu model
                ] <| 
                    Element.text "I'm a menu!"
    ...
```
Sweet! Now the menu opens and closes when the triggering element is clicked. See the updated version [here](https://ellie-app.com/4jHQRRKK4NXa1). But lots of users will probably expect the menu to close when any other part of the viewport is clicked. Here is the first way I thought of setting that up:
```
view : Model -> Html Msg
view model =
    Element.layout [ Element.Events.onClick CloseMenu
    ...
```
This should mean that clicking anywhere sends the `CloseMenu` `Msg`. See the problem with this approach [here](https://ellie-app.com/4jHVqgY4NZMa1). Try to launch the menu and then open the debugger.

What you see is that clicking the menu sends the `ToggleMenu` `Msg` just like we want, but it is immediately followed by the `CloseMenu` `Msg`. This leads to the menu never opening. Ugggh.

The reason for this is that `onClick` events are propagated from child elements to parent elements. And we can't just use the `ToggleMenu` fix on the `layout` because that would open the menu anytime you click anywhere.

There are almost certainly many ways to get around this, but the way I hit upon was to put an empty element the size of layout behind the content of the layout using the cleverly named function [`Element.behindContent`](https://package.elm-lang.org/packages/mdgriffith/elm-ui/1.1.0/Element#behindContent). Giving this element an `onClick CloseMenu` attribute works because it is not in a parent-child relationship with the menu elements. 

This approach works okay but there's still a couple of glitches. See it in action [here](https://ellie-app.com/4jJ5QMPPTD2a1). The most obvious glitch in terms of the problem of getting the menu to close is that the element displaying the counter is not sending the `CloseMenu` `Msg` when clicked because it is in front and not a child element of the element we just added. [My solution](https://ellie-app.com/4jJgMcr8Gxfa1) was to add the `onClick CloseMenu` attribute to that element. I'd be interested to hear more elegant solutions though!

The second obvious hiccup to me is that the menu closes each time you increment or decrement the count. I think most users would expect the menu to stay open until they were done incrementing/decrementing the count as many times as they wanted. I'll leave the solution to that as an exercise for anyone interested.

Thanks for reading. I hope you found it helpful. If you have questions about Elm or `elm-ui` I highly recommend the [Elm Discourse ](https://discourse.elm-lang.org) and the Elm and `elm-ui` [Slack channels](https://elmlang.slack.com/messages).