[Haskell-beginners] Options for creating a multiple select form via Yesod
Michael Litchard
michael at schmong.org
Thu Jun 16 01:02:59 CEST 2011
I took a look at
hello-forms.hs
and this was my clue
myForm = fixType $ runFormGet $ renderDivs $ pure (,,,,,,)
<*> areq boolField "Bool field" Nothing
<*> aopt boolField "Opt bool field" Nothing
<*> areq textField "Text field" Nothing
<*> areq (selectField fruits) "Select field" Nothing
<*> aopt (selectField fruits) "Opt select field" Nothing
<*> aopt intField "Opt int field" Nothing
<*> aopt (radioField fruits) "Opt radio" Nothing
The aopt and areq look new. As far as I can tell, this looks like it
should do what I needed. I'm eager to try it out.
On Wed, Jun 15, 2011 at 3:59 PM, David McBride <dmcbride at neondsl.com> wrote:
> I haven't finished. I was making the changes he suggested, but found
> a problem. I sent him a question but he's probably a busy guy.
>
> I don't see the changes in his github page, where are you getting this
> from? If you have found them, it is just a matter of taking the
> package, cabal configure, cabal install and then it should be
> available for all your coding needs.
>
> On Wed, Jun 15, 2011 at 6:40 PM, Michael Litchard <michael at schmong.org> wrote:
>> David,
>> I checked-out yesod-forms from the repository and see that
>> you've made the changes. Thank you once again. My follow-up question
>> is for anyone who cares to answer. How do I swap the new yesod-forms
>> and the old one from 0.2.0? I don't want to hose
>> my system. This is new ground for me.
>>
>> On Tue, Jun 14, 2011 at 10:56 AM, Michael Litchard <michael at schmong.org> wrote:
>>> Oh sweet! Thanks!
>>>
>>> On Tue, Jun 14, 2011 at 9:43 AM, David McBride <dmcbride at neondsl.com> wrote:
>>>> 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
>>>>>>
>>>>>
>>>>
>>>> _______________________________________________
>>>> Beginners mailing list
>>>> Beginners at haskell.org
>>>> http://www.haskell.org/mailman/listinfo/beginners
>>>>
>>>
>>
>
More information about the Beginners
mailing list