This example application is the same than the previous example, but with the added functionality of undoing additions of form elements by pressing the back button thanks to the backtracking mechanism of MFlow.
This is the application running
This is the source code
See the description of how the previous example works. The only noticeable change over the previous example is the createForm procedure, which now has a loop.
unlike in the previous example, now createForm contains the page statement, so it is of type
FlowM Html IO [WType]
View Html IO [WType]
Now the getSessionData that retrieves the description and the sequence of the formlet identifiers has been moved out ot the page code. This means that when there is backtracking, that is, when the user press the back button, the procedure recover the previous state. This is explained in this article: controlling backtracking in MFlow. The reason is that only the page code is re-executed when backtracking. Since the session data is not retaken in the code of the page, then it implicitly it is set to the values that it had previously.
createForm :: FlowM Html IO [WType]
createForm n title= do
desc <- getSessionData `onNothing` return 
Seq seq <-getSessionData `onNothing` return (Seq 0)
r <- page $ do
br ++> wlink ("save" :: String) << b "Save the form and continue"
<++ br <> "(when finished)"
content <- liftIO $ readtField (mempty :: Html) (title ++ show n)
liftIO . writetField title $ content
liftIO $ forM_ [1 .. n] $ \n -> writetField (title ++ show n) ("" :: Html) -- delete previous versions
Just <$> getSessionData `onNothing` return 
wdesc <- chooseWidget <++ hr
setSessionData $ mappend desc [wdesc]
content <- liftIO $ readtField mempty (title ++ show n)
fieldview <- generateView wdesc seq
liftIO . writetField (title ++ show (n+1)) $ content <> br <> fieldview
<** divbody <<< wform (edTemplate "edituser" (title ++ show n) (return ()) )
case r of
Just desc -> breturn desc
Nothing -> createForm (n+1) title
The save button (first half of the code in the page) as in the previous example, return the description of the form. Note the breturn statement at the end. That assures that when there is backtracking, the page if createForm will be called back.
But before returning it deletes the unused versions of the tFields. The reason for the need of version is because unlike the description of the form -that is stored in the session data- the tFields are pages segments that are cached and stored in files. So they must be explicitly deleted.
These versions are created in the second half of the page code, which is the menu. Now this second half return the option chosen, while before his result was ignored. In the previous example, all the edition was carried out in the same page. Now the option chosen generates a widget that is added to a new version of the form, that gets a new name by appending (n+1) to the title. Then the description is also updated in the session data. Finally it returns Nothing.
When a option of the menu has been chosen it get out of the page and is detected by the last case statement. ,Then the createForm is called again recursively with n increased to mean that there is a new version. The page is refreshed with the content of this new version and so on.
When the back button is pressed, The browser present the previous page, that contains the rendering of the previous version. When the user press an option, the code detect that this option has a REST link corresponding to a previous page, then page backtrack, but since the previous page executed is the same code with the previous version, then it executes the addition of the new option to the previous version.
The loop end when the save is pressed. Then the flow test the form, as in the case of the previous example.