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

David McBride dmcbride at neondsl.com
Tue Jun 14 18:43:36 CEST 2011


I think I'll give it a shot.  Fun problem and something I'll
definitely need eventually.  I'll try and get you a pull request
tonight if all goes well.

On Tue, Jun 14, 2011 at 11:40 AM, Michael Snoyman <michael at snoyman.com> wrote:
> Hi David,
>
> Thank you for the analysis, you're absolutely correct that this is a
> shortcoming in the API of yesod-form as it stands. I had an idea for a
> possibly simple modification to fix the situation: change fieldParse
> to
>
>    [Text] -> Either msg (Maybe a)
>
> We don't really want to support returning singles or doubles from the
> same Field; a multiSelectField will automatically make the "a"
> variable a list. Said another way, the types should be:
>
>    selectField :: [(Text, a)] -> Field xml msg a
>    multiSelectField :: [(Text, a)] -> Field xml msg [a]
>
> Another related change: we don't really need to separate out
> fieldRender from fieldView I believe. Instead, we can have fieldView
> be:
>
>    fieldView :: Text -- ^ ID
>                 -> Text -- ^ name
>                 -> a -- currently, this is another Text
>                 -> Bool -- ^ required?
>                 -> xml
>
> I'm willing to test out these changes myself, but wanted to (1) get
> input from you and (2) see if you or anyone else wanted to take a
> crack at it.
>
> Michael
>
> On Tue, Jun 14, 2011 at 4:25 PM, David McBride <dmcbride at neondsl.com> wrote:
>> 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
>>>>
>>>
>>
>> _______________________________________________
>> Beginners mailing list
>> Beginners at haskell.org
>> http://www.haskell.org/mailman/listinfo/beginners
>>
>



More information about the Beginners mailing list