问题
Is it possible in Yesod to handle forms that contain a collection of data?
I have a form that the user can add multiple people to, on the frontend it currently looks like this:
{ people.map((person, key) => (
<td>
<input type="hidden" name={ `person[${key}][firstName]` } value={person.firstName} />
<input type="hidden" name={ `person[${key}][lastName]` } value={person.lastName} />
{ person.firstName } { person.lastName }
</td>
)) }
I then want to be able to translate that over to the backend like so:
[Person "Michael" "Snoyman", Person "Ed" "Kmett"]
This list is variable in length, so it could have as many people in the people
value as the user likes. So far I've been unable to work out how to replicate this kind of thing using FormInput
in Yesod.
回答1:
You could create your own FormInput
by defining the unFormInput
function. This function could pull the field names from the form, extract the keys, and then you could use ireq
to promote the relevant fields.
This could look something like
getPeople :: FormInput (your handler type) [People]
getPeople = FormInput $ \m l env fenv ->
(unFormInput (peopleField peopleKeys)) m l env fenv
where
peopleKeys = getPeopleKeys env
getPeopleKeys
This helper function would produce all the key values for the people in your form. They wouldn't need to be valid yet, since field parsing will take care of that later.
getPeopleKeys :: Env -> [Text]
getPeopleKeys env = mapMaybe extractKey (keys env)
where
extractKey :: Text -> Maybe Text
extractKey key = ... -- you could use e.g. regex here
-- to pull the keys out of the field names
-- and return Nothing otherwise
peopleField
This helper produces the FormInput
. It
- takes a list of keys,
- generates a
FormInput
from each one- generates a field for the first name and last name
- turns those fields into
FormInput
s - produces a
FormInput
which combines them into aPerson
- concatenates the
FormInput
s' results into aFormInput ... [Person]
peopleField :: Monad m => RenderMessage (HandlerSite m) FormMessage => [Text] -> FormInput m [Person]
peopleField = mapM personField
where
personField :: Text -> Field ... Person
personField key = (liftA2 (Person)) (personFNameField) (personLNameField)
where
personFNameField = (ireq hiddenField) . fNameField key
personLNameField = (ireq hiddenField) . lNameField key
fNameField key = ... -- this outputs "person[${key}][firstName]"
lNameField key = ... -- etc.
来源:https://stackoverflow.com/questions/52182020/handling-a-collection-of-data-in-a-yesod-form