[Haskell-beginners] Options for creating a multiple select form via Yesod

David McBride dmcbride at neondsl.com
Tue Jun 14 15:25:08 CEST 2011


I gave a shot at this last night,and didn't quite pull it off.  But I
got pretty far and I'd rather you have my work than try it from
scratch.

The first method won't work because the Field type is used in
validating the get parameters in the mhelper function in
yesod.form.functions.  That means that you have to have a unified type
for field that can do everything.  Maybe there is a more succinct way
with classes, but I just couldn't think of an elegant way to do it
that way.

So what I tried to do was make the fieldParser callback into its own type:

newtype FieldParser msg a = FieldParser (Either
  (Maybe Text -> Either msg (Maybe a))
  (Maybe [Text] -> Either msg (Maybe [a])))

That means a field parser can either take one text and return one
item, or it takes a list and returns a list of items.

Then I went through Yesod.Form.Fields and changed about 12 or 15 references of
{ fieldParse = blank $ \s ->
to
{ fieldParse = FieldParser . Left $ blank $ \s ->

Cool, now when you write your multipleSelectField, you'll set the fieldParser to
FieldParser . Right $ etc...

The very last thing you have to do to fix this is the mhelper
function, which is where I lost steam.  Right now it looks up the name
of the field in the get/post params that were passed in, and then
hands the value to fieldParse.  What it needs to do is check to see
whether Field Parser is left or right, and then pass in the params
slightly differently depending on which it is.  I don't know how the
parameters will end up getting passed into askParams though.  Right
now askParams returns a list of names to value pairs, so hopefully you
will end up with a list of multiple entries for the name of your mutli
select and a different value for each entry, which you need to filter
out and collect into a single list and then run the fieldParser on it.

Hopefully that is not too bad.


On Tue, Jun 14, 2011 at 4:30 AM, Michael Litchard <michael at schmong.org> wrote:
> Well I will try the easier way first, and having accomplished that I
> will look into doing it the better way. If people can call dibs, I'd
> like to.
>
> On Mon, Jun 13, 2011 at 9:47 PM, David McBride <dmcbride at neondsl.com> wrote:
>> After looking at the source, you should be aware that
>>
>> 1) yesod-form has been updated to 2.0,
>> 2) it is a lot easier to understand than 1.x was.
>>
>> The main obstacle I see is that the library uses the Field datatype,
>> that has a fieldParse method of Maybe Text -> Either msg (Maybe a).
>> The problem with that is that a multiple select box should require
>> [Text] or perhaps Maybe [Text] rather than Maybe Text.  It is making
>> the assumption that there can only be one piece of data per field,
>> which holds for everything except multiple selects and multiple radio
>> buttons.
>>
>> So looking at this, it looks like you'd have to add another field type
>> "FieldMulti" to Yesod/Form/Types.hs, which allows for multiple values.
>>  Then add a new version of selectFieldHelper that accepts fieldMultis
>> instead of fields, and then it is trivial to change selectField to be
>> a multi field.
>>
>> Alternatively you could change Field to accept either single or
>> multiple values and change its use everywhere else, which is probably
>> the better answer, but more involved.
>>
>> I don't know if this is the best way to go about it, but it seems like
>> it should work.
>>
>> On Mon, Jun 13, 2011 at 8:11 PM, Michael Litchard <michael at schmong.org> wrote:
>>> Thank you David. I'm trying to figure out step-by-step, exactly how
>>> selectFields binds field values. One thing I'm having trouble with is
>>> visualizing return values.
>>> Beginning with askParams.
>>>
>>> askParams :: Monad m => StateT Ints (ReaderT Env m) Env
>>> askParams = lift askenv <- askParams
>>>
>>>
>>> Here's the example from selectFields
>>> env <- askParams
>>> later on env is used in with the lookup function
>>>
>>> let res = case lookup name env of
>>> seeing as lookup is checking for value of type a in a [(a,b)]
>>> and given the type of askParams
>>> I have no idea what is going on here. I don't see a [(a,b)] in
>>> askParams :: Monad m => StateT Ints (ReaderT Env m) Env.
>>>
>>> So if someone could answer how env <- askParams yields a [(a,b)] for
>>> lookup to use as input, I would appreciate it.
>>>
>>>
>>> On Mon, Jun 13, 2011 at 2:54 PM, David McBride <dmcbride at neondsl.com> wrote:
>>>> The read function is sort of the opposite of the show function.  Take
>>>> a string, give me a value.  reads is like read, however it has some
>>>> traits that read doesn't have.
>>>>
>>>> The problem with read is that if you go: read "asdf" :: Int, it will
>>>> die with an exception, and that is something you don't want in a web
>>>> app.  Also it doesn't tell you what the rest of the string is, so you
>>>> have no real way of finding out what was left of the string after the
>>>> part you wanted to parse.
>>>>
>>>> So there is the reads function that returns [(a,String)] which is a
>>>> list of pairs of the answer a, and the rest of the string String.  As
>>>> a bonus, it returns a list so if it can't parse the string you pass
>>>> it, then it just returns an empty list.  Why didn't it use Maybe you
>>>> ask?  I bet it probably has to do with the function being one of the
>>>> first functions ever written for haskell, long before Maybe existed.
>>>>
>>>> So all it is there is unpack this bytestring into a string, then parse
>>>> it into a value, and please don't blow up if the input is invalid.
>>>>
>>>> On Mon, Jun 13, 2011 at 5:28 PM, Michael Litchard <michael at schmong.org> wrote:
>>>>> I was a bit hasty. I can render a multi-select field easily enough.
>>>>> However, I'm having difficulty following how selectField makes a value
>>>>> from the select field accessible from the handler code calling
>>>>> selectField. Once I figure that out, I can modify multiSelectField
>>>>> accordingly.
>>>>>
>>>>> The goal here being to modify selectField so that a list of field
>>>>> values can be bound .
>>>>>
>>>>> Here's what I have so far:
>>>>> multiSelectField is thus far identical in every way to selectField
>>>>> save for the following change in the Hamlet part.
>>>>>
>>>>> <select multiple="#{theId}" id="#{theId}" name="#{name}">
>>>>>
>>>>> My thinking was that the value bound to multiple was arbitary, and I'd
>>>>> use theId until I figured out something that made more sense.
>>>>>
>>>>> Here's where I am focusing my efforts next
>>>>>
>>>>> http://hpaste.org/47774
>>>>>
>>>>> Specifically
>>>>> (x', _):_ ->
>>>>>                            case lookup x' pairs' of
>>>>>                                Nothing -> FormFailure ["Invalid entry"]
>>>>>                                Just (y, _) -> FormSuccess y
>>>>> I'm thinking this is where selectField binds a value from the select
>>>>> field form. I'm confused by the (x',_):_. At first I thought it meant
>>>>> that just the first pair in a list of pairs is pattern matched
>>>>> against, and the rest discarded. But then I ask myself where the list
>>>>> is coming from. In a select field there would only be one pair, not a
>>>>> list of them. Here's where I get confused. Because if this is not
>>>>> where the values of the select field get bound, I don't know where
>>>>> it's happening.
>>>>>
>>>>> Is my confusion clear enough such that I could get some clarifying
>>>>> feedback? If not, what is unclear?
>>>>>
>>>>> On Sat, Jun 11, 2011 at 11:03 AM, Michael Snoyman <michael at snoyman.com> wrote:
>>>>>> The best way for code contributions in general is to submit a pull
>>>>>> request on Github. If that's a problem, sending a patch via email
>>>>>> works as well (either directly to me or to web-devel).
>>>>>>
>>>>>> Michael
>>>>>>
>>>>>> On Sat, Jun 11, 2011 at 1:14 AM, Michael Litchard <michael at schmong.org> wrote:
>>>>>>> Hey! I just added multiSelectField to the Forms library. I'm only
>>>>>>> getting the first value selected, but I think that's because of how
>>>>>>> I'm using multiSelecrField. I'm going to try to change the client code
>>>>>>> to fix this. I'll let you know how it goes. when I get a
>>>>>>> maybeMultiSelectField added I'll show you what I have. What would be
>>>>>>> the best way to submit this?
>>>>>>>
>>>>>>> On Thu, Jun 9, 2011 at 10:05 PM, Michael Snoyman <michael at snoyman.com> wrote:
>>>>>>>> Hi Michael,
>>>>>>>>
>>>>>>>> There's nothing jQuery or Javascript specific about a multi-select
>>>>>>>> field: it's just a normal select field with a "multiple" attribute. I
>>>>>>>> would recommend taking the selectField code from yesod-form and
>>>>>>>> modifying it to be multi-select. I'll likely do this myself
>>>>>>>> eventually, but it could be a good learning experience in Yesod (and a
>>>>>>>> great introduction to contributing to the framework if you're so
>>>>>>>> inclined).
>>>>>>>>
>>>>>>>> Michael
>>>>>>>>
>>>>>>>> On Thu, Jun 9, 2011 at 8:29 PM, Michael Litchard <michael at schmong.org> wrote:
>>>>>>>>> I'm trying to create a multiple select form, as illustrated on the following:
>>>>>>>>> http://api.jquery.com/selected-selector/
>>>>>>>>>
>>>>>>>>> Here's the options I see possible:
>>>>>>>>>
>>>>>>>>> (1) Write a jQuery widget.
>>>>>>>>> (2) Use plain javascript via Julius
>>>>>>>>> (3) Use the low-level functions in Yesod.Form to write a widget
>>>>>>>>> (4) Use a pre-existing function that does what I need, but am not
>>>>>>>>> aware of this functionality
>>>>>>>>>
>>>>>>>>> (1) has appeal as it looks like something small I can contribute to
>>>>>>>>> the project. It will take me some extra time to figure out the
>>>>>>>>> details. But, I had a look at the other jQuery widgets and they seem
>>>>>>>>> to provide an approachable model to follow.
>>>>>>>>>
>>>>>>>>> (2) This looks like the most straight-forward approach. I'm just
>>>>>>>>> learning javascript so would have to figure out how to capture values
>>>>>>>>> in Haskell from the form.
>>>>>>>>>
>>>>>>>>> (3) This looks like the most difficult way. I don't think I know
>>>>>>>>> enough about the low-level functions in Yesod.Form to be able to
>>>>>>>>> accomplish this in a timely manner.
>>>>>>>>>
>>>>>>>>> (4) This is the best scenario. There's already a way to do this right
>>>>>>>>> now, and I just haven't identified it. If this is the case, I would
>>>>>>>>> appreciate being pointed in the right direction.
>>>>>>>>>
>>>>>>>>> Until informed otherwise, I'm evaluating options 1 and 2. All feedback
>>>>>>>>> welcomed. Thanks to all who made Yesod possible.
>>>>>>>>>
>>>>>>>>> _______________________________________________
>>>>>>>>> Beginners mailing list
>>>>>>>>> Beginners at haskell.org
>>>>>>>>> http://www.haskell.org/mailman/listinfo/beginners
>>>>>>>>>
>>>>>>>>
>>>>>>>
>>>>>>
>>>>>
>>>>> _______________________________________________
>>>>> Beginners mailing list
>>>>> Beginners at haskell.org
>>>>> http://www.haskell.org/mailman/listinfo/beginners
>>>>>
>>>>
>>>> _______________________________________________
>>>> Beginners mailing list
>>>> Beginners at haskell.org
>>>> http://www.haskell.org/mailman/listinfo/beginners
>>>>
>>>
>>
>> _______________________________________________
>> Beginners mailing list
>> Beginners at haskell.org
>> http://www.haskell.org/mailman/listinfo/beginners
>>
>



More information about the Beginners mailing list