agocorona / MFlow

(Haskell) Web application server with stateful, type safe user interactions and widget combinators
http://mflowdemo.herokuapp.com
Other
100 stars 12 forks source link

AJAX - multiple buttons/choices #49

Closed BartAdv closed 10 years ago

BartAdv commented 10 years ago

Hello,

Sorry for using issues for questions (what could be better place, anyway?), but I've got troubles finding best way to handle following scenario.

Say I have a record which contains an array, and I want to have form in which I can add the elements to the array, but also any time I can just hit some 'Create' button and finish the formlet.

Snippet:

{-# LANGUAGE DeriveDataTypeable, OverloadedStrings #-}
module Main where

import MFlow.Wai.Blaze.Html.All hiding(head)
import qualified MFlow.Wai.Blaze.Html.All as El

data Foo = MkFoo { name :: String, nums :: [Int] } 
           deriving Show

foo = do
  MkFoo <$> (wlabel "str" $ getString Nothing)
        <*> (wlabel "nums" $ getNums [])
        <++ br

getNums is = do
  b <- getBool True "Add Int" "Done"
       <** submitButton "ok" -- nevermind this, it could be autosubmitting
  case b of
    True -> do
      i <- getInt Nothing
        <** submitButton "add"
      getNums $ i:is
    False -> return is

test = page $ do
  autoRefresh $ do
    f <- foo
    p << (show f) ++> wlink () ""

main :: IO ()
main = do
  runNavigation "" $ transientNav test

Using above code I can add the numbers, but every time I need to make a choice - whether I want to add more, or I want to finish (using ugly Yes/No select, but that's not the point).

Ideally, I would just want getNums widget to loop infinitely, but also some Create button available at foo so that I can just use it to obtain foo value.

agocorona commented 10 years ago

Bartosz: I´m looking at it. For sure there is a way to do that

agocorona commented 10 years ago

Hi Bartosz:

It can be done by just adding the create button:

              test = page $ do
                  autoRefresh $ do
                     f <- foo  <** submitButton "create"
                     p << (show f) ++> wlink () ""

So you also don´t need the ugly dropdown. It works because the button forces the submit of the current data. and <\ return the content of foo and discard the value of submitButton (that is "create")

instead of

    wlink () ""

use empty or noWidget

BartAdv commented 10 years ago

Ahh, that's nice, but doesn't it perform the same submit as the "add" button does? That is, it still want to submit the int, and either fails to validate, or just process another loop (unless I did the getNums loop wrong, which might be the case as I've also run into strange issue with mixing input values (submitButton returned value got fed into text input)

foo = do
  MkFoo <$> (wlabel "str" $ getString Nothing)
        <*> (wlabel "nums" $ getNums [])
        <++ br

getNums is = do
  i <- getInt Nothing
    <** submitButton "add"
  getNums $ i:is

test = page $ do
  autoRefresh $ do
    f <- foo
         <** submitButton "create"
    p << (show f) ++> wlink () ""

On Sat, Aug 2, 2014 at 5:15 PM, agocorona notifications@github.com wrote:

Hi Bartosz:

It can be done by just adding the create button:

test = page $ do autoRefresh $ do f <- foo <* submitButton "create"* p << (show f) ++> wlink () ""

So you also don´t need the ugly dropdown

2014-08-01 23:25 GMT+02:00 Alberto G. Corona agocorona@gmail.com:

Bartosz: I´m looking at it. For sure there is a way to do that

2014-08-01 13:11 GMT+02:00 Bartosz notifications@github.com:

Hello,

Sorry for using issues for questions (what could be better place, anyway?), but I've got troubles finding best way to handle following scenario.

Say I have a record which contains an array, and I want to have form in which I can add the elements to the array, but also any time I can just hit some 'Create' button and finish the formlet.

Snippet:

{-# LANGUAGE DeriveDataTypeable, OverloadedStrings #-}module Main where import MFlow.Wai.Blaze.Html.All hiding(head)import qualified MFlow.Wai.Blaze.Html.All as El data Foo = MkFoo { name :: String, nums :: [Int] } deriving Show foo = do MkFoo <$> (wlabel "str" $ getString Nothing)

<_> (wlabel "nums" $ getNums []) <++ br getNums is = do b <- getBool True "Add Int" "Done" <_\* submitButton "ok" -- nevermind this, it could be autosubmitting case b of True -> do i <- getInt Nothing <*\* submitButton "add" getNums $ i:is False -> return is test = page $ do autoRefresh $ do f <- foo p << (show f) ++> wlink () "" main :: IO ()main = do runNavigation "" $ transientNav test Using above code I can add the numbers, but every time I need to make a choice - whether I want to add more, or I want to finish (using ugly Yes/No select, but that's not the point). Ideally, I would just want getNums widget to loop infinitely, but also some Create button available at foo so that I can just use it to obtain foo value. — Reply to this email directly or view it on GitHub https://github.com/agocorona/MFlow/issues/49.

Alberto.

Alberto.

— Reply to this email directly or view it on GitHub https://github.com/agocorona/MFlow/issues/49#issuecomment-50965372.

agocorona commented 10 years ago

Yes use pageFlow when in a monadic expression. Each branch of an if must have a pageFlow. This is in orde to give diffrent identifier sets to each branch:

For example:

do ...... if condtion then pageFlow "op1" $ ...... else pageFlow "op2" $ .....

2014-08-02 20:56 GMT+02:00 Bartosz notifications@github.com:

Ahh, that's nice, I've run into strange issue with mixing input values (text input value becomes the value of the submit button):

foo = do
MkFoo <$> (wlabel "str" $ getString Nothing)
<*> (wlabel "nums" $ getNums [])
<++ br

getNums is = do
i <- getInt Nothing
<** submitButton "add"
getNums $ i:is

test = page $ do
autoRefresh $ do
f <- foo
<** submitButton "create"
p << (show f) ++> wlink () ""

On Sat, Aug 2, 2014 at 5:15 PM, agocorona notifications@github.com wrote:

Hi Bartosz:

It can be done by just adding the create button:

test = page $ do autoRefresh $ do f <- foo <* submitButton "create"* p << (show f) ++> wlink () ""

So you also don´t need the ugly dropdown

2014-08-01 23:25 GMT+02:00 Alberto G. Corona agocorona@gmail.com:

Bartosz: I´m looking at it. For sure there is a way to do that

2014-08-01 13:11 GMT+02:00 Bartosz notifications@github.com:

Hello,

Sorry for using issues for questions (what could be better place, anyway?), but I've got troubles finding best way to handle following scenario.

Say I have a record which contains an array, and I want to have form in which I can add the elements to the array, but also any time I can just hit some 'Create' button and finish the formlet.

Snippet:

{-# LANGUAGE DeriveDataTypeable, OverloadedStrings #-}module Main where import MFlow.Wai.Blaze.Html.All hiding(head)import qualified MFlow.Wai.Blaze.Html.All as El data Foo = MkFoo { name :: String, nums :: [Int] } deriving Show foo = do MkFoo <$> (wlabel "str" $ getString Nothing)

<_> (wlabel "nums" $ getNums []) <++ br getNums is = do b <- getBool True "Add Int" "Done" <_\* submitButton "ok" -- nevermind this, it could be autosubmitting case b of True -> do i <- getInt Nothing <*\* submitButton "add" getNums $ i:is False -> return is test = page $ do autoRefresh $ do f <- foo p << (show f) ++> wlink () "" main :: IO ()main = do runNavigation "" $ transientNav test Using above code I can add the numbers, but every time I need to make a choice - whether I want to add more, or I want to finish (using ugly Yes/No select, but that's not the point). Ideally, I would just want getNums widget to loop infinitely, but also some Create button available at foo so that I can just use it to obtain foo value. — Reply to this email directly or view it on GitHub https://github.com/agocorona/MFlow/issues/49.

Alberto.

Alberto.

— Reply to this email directly or view it on GitHub https://github.com/agocorona/MFlow/issues/49#issuecomment-50965372.

— Reply to this email directly or view it on GitHub https://github.com/agocorona/MFlow/issues/49#issuecomment-50971239.

Alberto.

BartAdv commented 10 years ago

But there is no condition here...basically, what I'm experiencing now (I've edited above comment later, after you've received mail notification) is just that it's unable to differentiate whether the submit comes from the getInt or whether it's that final submitButton "create".

agocorona commented 10 years ago

But you have a case statement with two branches.

The problem is that the code that check if it must add a new integer and the code that expect a button pressed expect the same identifier unless we add a pageFlow to one of them (or both) to add a different prefix string to each identifer.

2014-08-02 22:11 GMT+02:00 Bartosz notifications@github.com:

But there is no condition here...basically, what I'm experiencing now (I've edited above comment later, after you've received mail notification) is just that it's unable to differentiate whether the submit comes from the getInt or whether it's that final submitButton "create".

— Reply to this email directly or view it on GitHub https://github.com/agocorona/MFlow/issues/49#issuecomment-50973065.

Alberto.

BartAdv commented 10 years ago

Aye, but I've removed the case statement - there is no 'add more/finish' choice now (it worked OK with that choice anyway, it was just too cumbersome)

agocorona commented 10 years ago

Do you have still the problem?. If so, add a pageFlow to submitButton:

r <- yourProcess <\ pageFlow "submit" (submitButton "create")

2014-08-02 23:14 GMT+02:00 Bartosz notifications@github.com:

Aye, but I've removed the case statement - there is no 'add more/finish' choice now (it worked OK with that choice anyway, it was just too cumbersome)

— Reply to this email directly or view it on GitHub https://github.com/agocorona/MFlow/issues/49#issuecomment-50974541.

Alberto.

BartAdv commented 10 years ago

I think we've made a mistake with trying to remove the boolean check (the only condition) from that loop. What did we get? An infinite loop, and no amount of tricks will make this work. Even without knowing the details of how is View monad evaluated, the code should be written and read as normal, and it is so (which is good), but it makes certain things tricky.

agocorona commented 10 years ago

Ah. Still it is necessary a button "one more int" . But it in not necessary the double option of the dropdown El 03/08/2014 16:28, "Bartosz" notifications@github.com escribió:

I think we've made a mistake with trying to remove the boolean check (the only condition) from that loop. What did we get? An infinite loop, and no amount of tricks will make this work (even without knowing the details of how is View monad evaluated, the code should be written and read as normal, and it is so (which is good), but it makes certain things tricky.

— Reply to this email directly or view it on GitHub https://github.com/agocorona/MFlow/issues/49#issuecomment-50991834.

BartAdv commented 10 years ago

Well, not really, I somehow am unable to do this with just 'one more int' button, my mflow-foo is not that strong:)

foo = do
  MkFoo <$> (wlabel "str" $ getString Nothing)
        <*> (wlabel "nums" $ getNums [])
        <++ br

getNums is = pageFlow "nums" $ do
  _ <- submitButton "add"
  i <- getInt Nothing
       <** submitButton "ok"
  getNums $ i:is

test = page $ do
  autoRefresh $ do
    f <- foo
         <** pageFlow "create" (submitButton "create")
    p << (show f) ++> noWidget
agocorona commented 10 years ago

Thanks and sorry.

This is a bug. I have been touching pageFlow without properly testing it. Now it is fixed, and your last code works. The problem is that the push buttons are not sent to the server, except the one that is pressed. That´s right. But pageFlow, besides adding a prefix for every form identifier, has to remember the parameters of previous iterations, so that all the history of button pressed can be re-read by the widget, so the loop can progress upto reading all the sent (new+older) parameters (That is a sort of parameter parsing). A widget is a writer+ a parameter parser.

So pageFlow should remember the pressed buttons and now it does so.

I have used this code to check it:

     getNums :: [Int] -> View Html IO [Int]
     getNums is =  do
          submitButton "add"
          i <- getInt Nothing <** submitButton "ok"
          getNums    $ i:is

    main = runNavigation "" . transientNav $ page $ pageFlow "nums" $ getNums [] >> return ()
BartAdv commented 10 years ago

Thanks, however, the original problem is still not solved this way - but I think it's due to the unidiomatic approach:

test = page $ do
  f <- MkFoo <$> (wlabel "str" $ getString Nothing)
             <*> (wlabel "nums" $ getNums [])
       <** submitButton "create"
  p << show f ++> noWidget

getNums is = do
  submitButton "add"
  i <- getInt Nothing <** submitButton "ok"
  getNums $ i:is

The submitButton "add" is just the same input as getInt, so the loop seems to be still infinite and it doesn't let the flow to go through the "create" (if pressed).

agocorona commented 10 years ago

But that is not a bug! As you say there is a infinite loop. Try with this:

test = page $ do
  f <- MkFoo <$> (wlabel "str" $ getString Nothing)
             <*> (wlabel "nums" $ getNums [])
  p << show f ++> noWidget

getNums is = do
  r <- submitButton "add" <|>submitButton  "create"
  if r == "create" 
    then return is
    else do
       i <- getInt Nothing <** submitButton "ok"
      getNums $ i:is
agocorona commented 10 years ago

To hide the older buttons in each iteration:


getNums is = 
  (submitButton "add" <|> submitButton "create")
  wcallback (\r ->if r == "create" 
    then return is
    else do
         i <- br ++> getInt Nothing <** submitButton "ok"
        getNums $ i:is)
BartAdv commented 10 years ago

Oh man, somehow I've forgotten submitButton produces value as well, whoops. This works quite nicely.

BartAdv commented 10 years ago

May I have one more riddle for you? How would you add the ability to remove the entry after it been added?

agocorona commented 10 years ago

it may be with javascript. Look at how delete is done in the grid example. It is a matter to find the parent to be erased

2014-08-06 17:38 GMT+02:00 Bartosz notifications@github.com:

May I have one more riddle for you? How would you add the ability to remove the entry after it been added?

— Reply to this email directly or view it on GitHub https://github.com/agocorona/MFlow/issues/49#issuecomment-51352373.

Alberto.

BartAdv commented 10 years ago

yeah, i thought the same, and I've tried it, but they are restored after post. Grid uses manyOf which you fixed to handle DOM alteration.

On 8/6/14, agocorona notifications@github.com wrote:

it may be with javascript. Look at how delete is done in the grid example. It is a matter to find the parent to be erased

2014-08-06 17:38 GMT+02:00 Bartosz notifications@github.com:

May I have one more riddle for you? How would you add the ability to remove the entry after it been added?

— Reply to this email directly or view it on GitHub https://github.com/agocorona/MFlow/issues/49#issuecomment-51352373.

Alberto.


Reply to this email directly or view it on GitHub: https://github.com/agocorona/MFlow/issues/49#issuecomment-51361972

agocorona commented 10 years ago

Ah, ok, it is not so simple.

2014-08-06 19:56 GMT+02:00 Bartosz notifications@github.com:

yeah, i thought the same, and I've tried it, but they are restored after post. Grid uses manyOf which you fixed to handle DOM alteration.

On 8/6/14, agocorona notifications@github.com wrote:

it may be with javascript. Look at how delete is done in the grid example. It is a matter to find the parent to be erased

2014-08-06 17:38 GMT+02:00 Bartosz notifications@github.com:

May I have one more riddle for you? How would you add the ability to remove the entry after it been added?

— Reply to this email directly or view it on GitHub https://github.com/agocorona/MFlow/issues/49#issuecomment-51352373.

Alberto.


Reply to this email directly or view it on GitHub: https://github.com/agocorona/MFlow/issues/49#issuecomment-51361972

— Reply to this email directly or view it on GitHub https://github.com/agocorona/MFlow/issues/49#issuecomment-51371811.

Alberto.

agocorona commented 10 years ago

Check this:

  getNums is = 
    (submitButton "delete" <|> submitButton "add" <|> submitButton "create")
    `wcallback` (\r ->case r of
       "create" ->   return is
       "delete"  ->  getNum $ tail is
       _            ->  do
                  i <- br ++> getInt Nothing <** submitButton "ok"
                  getNums $ i:is)