haskellyesodyesod-forms

How to build a pre-selected option for a selectField in Yesod?


(This was asked before but it has no answers).

I have a list of countries in a database:

share [mkPersist sqlSettings] [persistLowerCase|
Country
  name Text
  UniqueCountryName name
  deriving Show
|]

And I can show a form to select one of them:

countries = do
  rows <- runDB $ selectList [] [Asc CountryName]
  optionsPairs $ map (\ r -> (countryName $ entityVal r, entityKey r)) rows

surveyForm :: Html -> MForm Handler (FormResult SurveyAnswerSet, Widget)
surveyForm extra = do
  (countryR, countryV) <- mreq (selectField countries) "" Nothing

I know that I should replace the Nothing in the last line with the desired default value but I still don't get how to do it. Looking at the mreq and the optionsPairs signatures my thought was that in this case I should provide a Maybe Option with the default country but my attempts have raised so many type errors that probably I am quite far from the right answer.

The Yesod book has an example using seems more simple than what I tried to achieve so I am not sure how to extrapolate it.

By the way I am getting the default country from the database so I don't need to hardcode its internal id:

defaultCountry = do
  row <- runDB $ getBy $ UniqueCountryName $ countryName "United States"
  (countryName $ entityVal row, entityKey row)

When I pass it as an argument to mreq I get the following errors:

Couldn't match type ‘(,) Text’ with ‘HandlerFor site’ Expected type: HandlerFor site (Key record) Actual type: (Text, Key record)

That's on the last line of the of the defaultContry function ((countryName $ entityVal row, entityKey row)). I understand that I should take the Key record from the pair and return it in a HandlerFor site but at the same time I also get:

Couldn't match expected type ‘Maybe (Key Country)’ with actual type ‘HandlerFor site0 (Key record0)’

In the (countryR, countryV) <- mreq (selectField countries) "" defaultCountry line. I interprete this as "you are passing me a HandlerFor site0 (Key record0) but I only accept Maybe (Key Country) which seems in conflict with the first error...

In the (countryName $ entityVal row, entityKey row) line I also see:

Couldn't match expected type ‘Entity Country’ with actual type ‘Maybe (Entity Country)’

in the row argument. I understand that I should extract the Entity Country from the Maybe but if I pattern match and pass just the Entity Country (i. e.: Just (Entity countryId country) -> (countryName $ entityVal (Entity countryId country), entityKey (Entity countryId country)) I still get the first error.

Thanks in advance.


Solution

  • Alright, this looks like a few type errors. The first

    Couldn't match type ‘(,) Text’ with ‘HandlerFor site’ Expected type: HandlerFor site (Key record) Actual type: (Text, Key record)

    comes from not using return in your do notation to lift the value to the monad:

    defaultCountry :: HandlerFor site (Text, Key record)
    defaultCountry = do
      row <- runDB $ getBy $ UniqueCountryName $ countryName "United States"
      return (countryName $ entityVal row, entityKey row)
    

    The error on row

    Couldn't match expected type ‘Entity Country’ with actual type ‘Maybe (Entity Country)’

    is because it's a Maybe, so let's correct that

    defaultCountry :: HandlerFor site (Text, Key record)
    defaultCountry = do
      Just row <- runDB $ getBy $ UniqueCountryName $ countryName "United States"
      return (countryName $ entityVal row, entityKey row)
    

    Now that defaultCountry is a good monad, you should be able to use it in your other code. But be careful of that third error

    Couldn't match expected type ‘Maybe (Key Country)’ with actual type ‘HandlerFor site0 (Key record0)’

    You need to unwrap the value from the HandlerFor monad, and rewrap it in a Maybe

    surveyForm :: Html -> MForm Handler (FormResult SurveyAnswerSet, Widget)
    surveyForm extra = do
      (defaultName, defaultKey) <- defaultCountry -- (defaultName, defaultKey) :: (Text, Key record)
      (countryR, countryV) <- mreq (selectField countries) "" (Just defaultKey)