List with Generic Filter

Hi All,

I’m pretty new to Elm and have created a simple function for displaying a list of things alongside a search box. When the user types into the search box, the list is then filtered according to some passed in function.

The function itself is below and I’ve posted a quick example of using it on ellie.

I was just wondering if this looks okay or if I’m doing something crazy wrong. The type signature is pretty gnarly so makes me think there’s something better I could be doing - I tried hiding some of the complexity in the FilterConfg object but I’m not sure if that’s the correct approach.

Any feedback would be much appreciated :smile:

viewFilter : FilterConfig msg -> (a -> ( String, Html msg )) -> (String -> a -> Bool) -> List a -> List (Html msg)
viewFilter config viewListItem matches charlist =
    [ Html.div [ Attr.class "filter" ]
        [ Html.label [ Attr.for config.inputID ] [ Html.text <| config.inputLabel ++ ":" ]
        , Html.input
            [ Attr.placeholder config.inputPlaceholder
            , Attr.id config.inputID
            , Attr.value config.inputValue
            , Attr.type_ "text"
            , Attr.autocomplete False
            , Events.onInput config.inputEvent
            ]
            []
        ]
    , Html.Keyed.ol [] <|
        List.map viewListItem <|
            List.filter (matches config.inputValue) charlist
    ]

type alias FilterConfig msg =
    { inputID : String
    , inputLabel : String
    , inputPlaceholder : String
    , inputValue : String
    , inputEvent : String -> msg
    }

It looks good! :star:

The type annotation might look less intimidating if you move viewListItem and matches into FilterConfig as well.

Since you render the results in an <ol>, each child needs to be a <li>. With this setup, each caller needs to be careful to implement viewListItem so that it renders a <li>. If you want to guarantee correct markup, you could have viewFilter render the <li> always.

1 Like

Thanks lydell!

I think I’ll add the matches filter to the config so that the function just takes a configuration, a way to display an item, and the list of items. It feels like there’s a fuzzy line between supplying everything in separate arguments vs. just giving a function a mega object.

Good point about the <li>. I did it this way so it was easy to add attributes from the calling function. I suppose a way to do that would be to change the viewListItem to return the Html content and attributes separately and then viewFilter can split these out (along with the key) and build the <li> itself. Something like this?

type alias FilterConfig msg a =
    { ...
    , filterFn : String -> a -> Bool
    }

type alias FilterItem msg =
    { key : String
    , attrs : List (Html.Attribute msg)
    , content : List (Html msg)
    }


viewFilter : FilterConfig msg a -> (a -> FilterItem msg) -> List a -> List (Html msg)
viewFilter config viewListItem charlist =
    [ Html.div [ Attr.class "filter" ]
      ...
    , Html.Keyed.ol [] <|
        List.map toKeyedLi <|
        List.map viewListItem <|
        List.filter (config.filterFn config.inputValue) charlist
    ]

toKeyedLi: FilterItem msg -> (String, Html msg)
toKeyedLi filteritem =
    (filteritem.key, Html.li filteritem.attrs filteritem.content)

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