A little tip for restricted text input in Elm. I've coded up the general idea in an ellie. This is really a general HTML tip, that I didn't know existed, but for some reason I feel it works pretty well in Elm.
The sitation is that you wish to restrict a text input to one of several possibilities, but there are many possibilities, so a select
element is probably not correct. You may or may not wish to prevent any output not in the list of suggestions. What I didn't know is that you can define a datalist
element that has all the suggestions and the browser will interpret that appropriately provided you set the list
attribute on the input
element. As for invalidating it, you could use a pattern
attribute, but I think in Elm you're probably doing your own invalidation anyway.
So, supposing you have an input, and some list of appropriate inputs - maybe they are dynamic, or a list of countries or something - the key point is to define the datalist
element:
1import Html exposing (Html)
2import Html.Attributes as Attributes
3
4allowableDataList : String -> List String -> Html msg
5allowableDataList id allowableInputs =
6 let
7 makeOption input =
8 Html.option
9 [ Attributes.value input
10 , Attributes.id id
11 ]
12 []
13 in
14 List.map makeOption allowableInputs
15 |> Html.datalist []
Now, you might wish to define this in your main view
function if, for example, it's not often changing and used frequently. But you can always define it when you need it. Of course you have to be careful with the value of the id
. To use this, you only have to set the list
attribute on an input:
1import Html exposing (Html)
2import Html.Attributes as Attributes
3import Html.Events as Events
4
5restrictedInput : String -> (String -> Msg) -> String -> Html Msg
6restrictedInput currentInput onInput allowInputsId =
7 Html.input
8 [ Attributes.value currentInput
9 , Attributes.list allowInputsId
10 , Events.onInput onInput
11 ]
12 []
The key point here is Attributes.list allowInputsId
. As I said above you could define the datalist
in your main function, or you could also define it here very locally, even within the input
works:
1import Html exposing (Html)
2import Html.Attributes as Attributes
3import Html.Events as Events
4
5type alias RestrictedInput =
6 { onInput : String -> Msg
7 , id : String
8 , allowableInputs : List String
9 , currentValue : String
10 }
11
12restrictedInput : RestrictedInput -> Html Msg
13restrictedInput config =
14 let
15 dataListId =
16 String.append config.id "-data-list"
17 in
18 Html.input
19 [ Attributes.value config.currentInput
20 , Attributes.id config.id
21 , Attributes.list dataListId
22 , Events.onInput onInput
23 ]
24 [ allowableDataList dataListId config.allowableInputs ]
Then if you also want to display an error message in the case that the current input is not one of the allowable inputs then you can simply check List.member config.currentInput config.allowableInputs
. Obviously you might wish to augment that with whether the input has been blurred. Anyway I thought this was a nice little tip worth sharing, one of those little not-well-known corners of HTML. As I said before there is an ellie-app for this.