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"]
?
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"]
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With