Autocomplete widget in Elm

Code Examples

Autocomplete (or Autosuggest) fields seems simple but if you consider all possible interaction (with mouse, with keyboards, width other items on the page) they can get quite complicated.

In Elm there is elm-autocomplete that is a generic library, very powerful but also not simple to implement. Even in its simpler implementation, it requires more than 300 lines of boilerplate code.

This is a first attempt to write a simple wrapper around elm-autocomplete that allow to create one or more autocompleting input fields easily.

First install Autocomplete with

elm-package install thebritican/elm-autocomplete

Then import Autocomplete and the wrapper:

import Autocomplete
import AutocompleteWrapper

In the model and the init add the all the fields that we need. In this example there are three of them:

type alias Model =
{ field1 : AutocompleteWrapper.Model
, field2 : AutocompleteWrapper.Model
, field3 : AutocompleteWrapper.Model
init : ( Model, Cmd Msg )
init =
( { field1 = AutocompleteWrapper.init "id1" "State" menuItems1
, field2 = AutocompleteWrapper.init "id2" "Language" menuItems2
, field3 = AutocompleteWrapper.init "id3" "Color" menuItems3
, Cmd.none

AutocompleteWrapper.init has three parameters

  • the first is an ID that is used both as ID in the html element that wrap the input box and used internally by Elm to differentiate the different instances of the fields
  • the second parameter is the list of menu items in the form of a list of strings. For example:
menuItems3 : List AutocompleteWrapper.MenuItem
menuItems3 =
[ "Blue"
, "Yellow"
, "Red"
, "Green"

Now let’s add a new message in our messages list. This is the message that will intercept all the interaction with the autocomplete fields

type Msg
= OriginMessage String AutocompleteWrapper.Msg

The update now need to take care of this message. This is the largest part of the wrapper:

update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
msgConverter : Msg -> ( String, AutocompleteWrapper.Msg )
msgConverter msg =
case msg of
OriginMessage fieldId autocomMsg ->
( fieldId, autocomMsg )
case msg of
OriginMessage fieldId autocomMsg ->
if fieldId == model.field1.fieldId then
( newModel, newMsg ) =
AutocompleteWrapper.update autocomMsg
model.field1 OriginMessage msgConverter
{ model | field1 = newModel } ! [ newMsg ]
else if fieldId == model.field2.fieldId then
( newModel, newMsg ) =
AutocompleteWrapper.update autocomMsg
model.field2 OriginMessage msgConverter
{ model | field2 = newModel } ! [ newMsg ]
else if fieldId == model.field3.fieldId then
( newModel, newMsg ) =
AutocompleteWrapper.update autocomMsg
model.field3 OriginMessage msgConverter
{ model | field3 = newModel } ! [ newMsg ]
model ! []

There is a block for each field. You notice that we have three similar blocks because in this example we are implementing three fields.

The autocomplete module require subscription because it need to detects keyboards and mouse events. In this configuration we need to repeat the subscription for each field:

subscriptions : Model -> Sub Msg
subscriptions model =
[ (OriginMessage model.field1.fieldId <<
AutocompleteWrapper.SetAutoState) Autocomplete.subscription
, (OriginMessage model.field2.fieldId <<
AutocompleteWrapper.SetAutoState) Autocomplete.subscription
, (OriginMessage model.field3.fieldId <<
AutocompleteWrapper.SetAutoState) Autocomplete.subscription

For the view, this is all you need:

view : Model -> Html Msg
view model =
div [ class "form-container" ]
[ div
[ AutocompleteWrapper.view model.field1 OriginMessage
, AutocompleteWrapper.view model.field2 OriginMessage
, AutocompleteWrapper.view model.field3 OriginMessage
] ]

Add some style and it is done!

The changes in the model during the interaction

Here you can see how history of the model is changing during the interaction with the fields.

This is my fist attempt of using this library and there will be better way to implement.

One thing that is not pleasing me is the triple subscription to the KeyDown event that fire anytime a key is pressed (see the console log before)

Chain of messages while the user is interacting with the page

I was quickly testing the autocomplete feature of mdgriffith/style-elements, this is an example (very WIP):

If you want to go deeper in understanding elm-autocomplete I suggest you the vision of this video

Thank you for reading.