/example-elm-searchable-dropdown

Project for Elm-lang workshop

Primary LanguageElmBSD 3-Clause "New" or "Revised" LicenseBSD-3-Clause

Elm searchable dropdown workshop project

This is a project used as an intro to Elm workshop. It builds a filterable dropdown similar to Select2. It takes a lot of small steps and doesn't make assumptions of existing Elm knowledge.

I wrote the dropdown in ~30 minutes with the expectation that a workshop could go through it in 90-120 minutes.

Each commit is a step with a detailed commit message explaining what was done and why. For ease of trying things out, I've linked the full source for each commit as an Ellie along with the accompanying commit message on this README. Unfortunately, GitHub doesn't allow iframe embedding in markdown docs.

1 - Initialize Project

2 - Start with "hello world"

Just display some static text on the page. Boring

3 - Display a static list of fruit

Display an unordered HTML list with each element in the list fruit.

The helper functions from Html take two arguments: a list of attributes and a list of children. Since List.map returns a list, we don't need to wrap it in brackets.

4 - Extract view function

Remove the logic from main and into a view function that handles turning a list of values into an Html list.

5 - Add concept of open/closed

Create a model that contains both the list of values and the idea of being open or closed. The view is rendered differently depending on whether it is open or closed.

The closed view is static so it doesn't require any arguments.

Note: There is a bug in the commit where we're passing an argument to the function viewClosed when it takes no arguments. This is fixed in the Ellie embed. It also gets fixed in the source in a later commit.

6 - Add Model type alias

Using the record type is awkward so alias it as Model so we can use it in signatures easily.

Note that this is different than the State type which is not an alias but instead is its own thing.

7 - Introduce the Elm architecture

The main function is no longer static but instead uses Html.program. The Html.program function takes in record with three vallues: an initial model, a view function, and an update function. Note that functions can be passed as arguments to other functions.

The update function takes in an event and the current model and returns a new model. In this simplest implementation, we're ignoring the event and always returning the current model. Because the view only changes if the model changes, we still effectively have a static app.

8 - React to open and close events

We add a Msg type that defines two events OpenSelect and CloseSelect. The update handles both of these by correctly changing the model. The view gets re-rendered whenever the model changes.

We emit the events from the views in response to a click event handler.

Note that the type signatures for the view functions changed from Html a to Html Maybe. That's because the views are now emitting Msg events.

9 - Add the concept of a "selected" value

The whole point of this is to be able to select values. Since it's possible for no values to be selected, we wrap it in Maybe.

The view gets trickier because there are now 4 ways to render the dropdown:

Open vs Closed each with the appropriate click handler and displaying the selected text or the help tip if nothing is selected.

To accomplish this, we extract a dropdownHead function that deals with selected vs no selection. Since we already know whether the dropdown is open or not, we just pass in the Msg type we want for the click handler.

Because we're now handling whether an item is selected or not, the closed view is no longer static.

10 - Set selected item when clicked

This sets our selected item in response to a Msg sent when the user clicks on an item. This Msg is more complex than previous ones because it wraps a value.

This means we need to give it a value in the click handler. When reacting to the event, we unwrap the value as part of the case statement, just like we did for the Maybe.

11 - Close the dropdown when item is selected

We already have an event that handles an item selection, now we just need to change the model's state to "closed".

12 - Filter values based on query

Only display values in the dropdown that match the given query (case insensitive). Since all the original values are kept, we can re-filter them by a different query at any time.

13 - Set search query as user types

When the user types in the text box, we set the query value on the model, thus filtering the dropdown.

Note that the new Msg value SearchInput takes a paremeter, we don't give it one in onInput. While onClick takes a Msg as its argument, onInput takes a function String -> Msg as its argument.

SearchInput is such a function when not given a value.

onInput does this because we don't know the value ahead of time. Instead, it will put the whatever value the user has typed inside the Msg.

14 - Clear the filter when item is selected

We introduced a bug in the previous commit occurred when:

  1. The results were filtered
  2. A value was selected (which auto-closes the dropdown)
  3. The user re-opens the dropdown
  4. The results are still filtered from last time

We fix this by setting the query to empty string whenever a selection is made, thus clearing the filter for next time.

15 - Introduce Html.program

This is a more complex program that allows interaction with the outside world with commands and subscriptions. Following the compiler errors, we update signatures and return values to match what is required from Html.program.

16 - Focus search input when opening dropdown

This uses a combination of Dom.focus and Task.attempt to create a Cmd that will focus the DOM node with the given id. Since all Cmds trigger a Msg when they are done but we just want to fire and forget, we create a Noop Msg to deal with the situation.

17 - Add styles with an index.html

We want to style the dropdown so we embed the Elm app on an HTML page that has a <style> element on the body. This means we can no longer just use elm reactor. We must now compile the app using elm-make

License

This project is distributed under the BSD 3-clause license

About thoughtbot

thoughtbot

This example project and walkthrough was developed for a workshop given at thoughtbot.

We love open source software! See our other projects or hire us to design, develop, and grow your product.