3
$\begingroup$

I want a FormFunction to show a history of inputs, by default an empty list. But once the submit is hit, the form should be reloaded with the history shown on top.

I can not deploy another form on submit.

The last input needs to be transferred via http request body, not in url parameters.

Here is my attempt:

CloudDeploy[
  Delayed @ With[
    { inputList = ImportString[ 
        FromCharacterCode[
          HTTPRequestData[][["BodyBytes"]]/.{}->ToCharacterCode@"[]"
        , "UTF8"
        ]
       ,"RawJSON"
       ]
    }
  , FormFunction[
      { 
        Framed @ inputList
      , "x" -> "Number"
      }
    , HTTPResponse[
        ExportString[Append[inputList, #x],"RawJSON"] (*BODY, it needs to be in the body not url parameters*)
      , <|
          "Headers"->{ "Location" -> First@CloudObject@"temp/recursiveForm" }
        , "StatusCode"->302
        |>
      , CharacterEncoding->None
      ]&
    ]
  ]
, "temp/recursiveForm"
, "Permissions"->"Public"
]

The form's submit function is a manually generated, in order to add body, http redirect response.

It does not work in a sense that the body appears to be empty after redirection.

It could be due to how browsers handle 302 but I am not sure. I am not sure if my approach is wrong, if it is a limitation of FormFunctions or any other reason.

Any feedback appreciated.

$\endgroup$
  • $\begingroup$ What about using cookies instad of inputList ? $\endgroup$ – Rolf Mertig Feb 26 at 15:35
  • $\begingroup$ @rolf no the input can be large. $\endgroup$ – Kuba Feb 26 at 16:31
4
$\begingroup$

The problem in your example is indeed the 302 redirect. The browser will simply ignore the response body and redirect to the Location given in the headers. So there is no JSON data passed to the subsequent call of the form.

If what you ultimately want is a form that takes a list of numbers (or other inputs), maybe RepeatedElement does just what you want? E.g.

FormFunction["field" -> RepeatingElement["Number"], #field &]

creates a form that allows the user to add and remove items. I understand it's a little different from what you described.

Maybe there's a way to create exactly what you want using dynamic forms with TemplateSlot etc. However, I gave it a quick try and couldn't make a "self-referential field" work (a field that would maintain a list of all previous inputs).

A (potentially multi-page) FormPage could be another option.

As a last resort, there's always APIFunction that gives you full low-level control, if FormFunction doesn't do what you want. You could create an API that returns an HTML page with a form that contains the regular user input, plus a hidden <input> field that maintains all previous inputs. When the API is called with a GET request, it would return the initial form state (with an empty list of previous inputs). On POST, it would return an HTML page with an updated list of previous inputs (plus whatever else you want to display to the user). There would be no redirects involved. The API could still use FormObject under the hood (in combination with XMLTemplate) to generate (most of) the <form> HTML code.

Sorry this is not a complete solution yet, but maybe enough of a pointer in the right direction. Let me know if you have further questions.

|improve this answer|||||
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.