Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I pass List params in IHP forms?

Tags:

haskell

ihp

I am trying to make a multiple selection part of my form in IHP. Currently trying to solve it with multiple checkboxes about like this in the View.

renderIngredientSelection :: Ingredient -> Html
renderIngredientSelection ingredient = [hsx|
    <li>
        <input name="ingredients" type="checkbox" value={(get #name ingredient)} />{get #name ingredient}
    </li>
    |]

So the browser tool of the network log says it's properly sending a request like this.

barcode=5555555555555&name=Pancake&ingredients=milk&ingredients=egg

But in the controller, the param function will only catch the first ingredients parameter.

Is there any way to catch all these parameters in the controller? I see in the network log, there is created a list of tuples containing all the parameters, including both the ingredients parameters. How could I access this and map it into a list like ["milk", "egg"]?

like image 559
Lars Lillo Ulvestad Avatar asked Sep 13 '20 20:09

Lars Lillo Ulvestad


1 Answers

You can use allParams to access the full request parameters you see in the logs.

For a request like:

barcode=5555555555555&name=Pancake&ingredients=milk&ingredients=egg

We can use the allParams like this:

action MyAction = do
    let ingredients = allParams
    -- ingredients = [("barcode",Just "5555555555555"),("name",Just "Pancake"),("ingredients",Just "milk"),("ingredients",Just "egg")]

We still need to filter this list to only return the ingredients values:

action MyAction = do
    let ingredients = allParams
        |> filter (\(paramName, paramValue) -> paramName == "ingredients")
    -- ingredients = [("ingredients",Just "milk"),("ingredients",Just "egg")]

Now we need to map this key-value-map to only the values. Because the value is a maybe (like Just "milk"), we'll use mapMaybe instead of map. mapMaybe throws away all the Nothing values and unpacks the Just:

action MyAction = do
    let ingredients = allParams
        |> filter (\(paramName, paramValue) -> paramName == "ingredients")
        |> mapMaybe (\(paramName, paramValue) -> paramValue)
    -- ingredients = ["milk", "ingredients"]

We have a [ByteString] now. Most functions we're dealing with expect a Text instead of a ByteString, therefore let's do a conversion using cs (cs is short for convert string):

action MyAction = do
    let ingredients :: [Text] = allParams
        |> filter (\(paramName, paramValue) -> paramName == "ingredients")
        |> mapMaybe (\(paramName, paramValue) -> paramValue)
        |> map cs
    -- ingredients = ["milk", "ingredients"]

Moving it to Application/Controller/Helper.hs

For a nice code structure I'd move this function out to Application/Controller/Helper.hs like this:

module Application.Helper.Controller
( ...
, paramList -- Don't forget the export :)
)

paramList name = allParams
        |> filter (\(paramName, paramValue) -> paramName == name)
        |> mapMaybe (\(paramName, paramValue) -> paramValue)
        |> map cs

And then use it in the controller like this:

action MyAction = do
    let ingredients = paramList "ingredients"

All the functions in Application.Helper.Controller are automatically available in our controllers.

like image 175
Marc Scholten Avatar answered Nov 09 '22 02:11

Marc Scholten