1
$\begingroup$

I am trying to develop a web form wherein the first page asks users how many cities they would like to compare, and the second page dynamically creates that many input boxes. Once the user inputs the full information on the second page, they submit it and a function generates and displays a map for them. To troubleshoot it I've made functionBuildMap[] just return a generic map:

functionBuildMap := Module[{origin, citylist},
  GeoGraphics[Polygon[GeoVariant[Entity["GeographicRegion", "World"], "SimplifiedArea"]]]
]

I've been trying to follow along with the tutorial located at https://reference.wolfram.com/language/tutorial/AdvancedWebFormCreation.html but am getting nowhere besides bashing my head against the wall.

I've tried implementing the multi-page form technique as such:

FormFunction[firstform,
  FormFunction[secondaryform[#"Destination city count"],
    functionBuildMap[origin, citylist]&]&
]

This technique correctly shows the first form, and after filling out the second form will just return the user to the first form again saying that required fields have not been filled out...

The problem being that while the first form feeds correctly into the second form through the & operator, after the second form is filled out it loops back to the first form saying that it is missing information.

I then tried to use the dynamic forms technique instead:

CloudDeploy @ FormFunction[
  {
    "Destination city count" -> "Integer",
    "Origin City" -> "Airport",
    Table["Destination " <> ToString[i]->"String",{i, TemplateSlot[#"Destination city count"]}]
  },
  functionBuildMap[origin, citylist]&
]

But this form just errors with

Table: Iterator {i, TemplateSlot[#Destination city count]} does not have appropriate bounds.

Yet while this cannot correctly receive input, it will display a map when the use submits their form on the cloud link provided.

Finally, I've also tried dynamic forms with the & operator as such:

CloudDeploy @ FormFunction[
  {
    "Destination city count" -> "Integer",
    "Origin City" -> "Airport",
    Table["Destination " <> ToString[i]->"String", {i, TemplateSlot[#"Destination city count"]}]& (* notice the "&" here this time around*)
  },
  functionBuildMap[origin, citylist]&
]

This only leads to a "$Failed" output on my cloud object page. Additionally it shows this error when I run it:

FormFunction: FormFunction expects a rule, a list of rules, an association or a FormObject as first argument.

$\endgroup$
  • $\begingroup$ Is ref/FormFunction/Scope/MultipageForms what you need? $\endgroup$ – Kuba Dec 4 '18 at 10:04
  • $\begingroup$ That was a desktop docs link but online documentation for FormFunction contains Scope section as well. I am glad that you've solved you problem, consider posting your edit as an answer. $\endgroup$ – Kuba Dec 4 '18 at 20:53
  • $\begingroup$ Don't use pictures, come up with a code sample that is small but works so others can test it. $\endgroup$ – Kuba Dec 4 '18 at 21:08
  • $\begingroup$ @Kuba I've gone through and edited my question to not include pictures, as well as moved my edit to an answer and accepted it. Hopefully all the work will be useful to somebody in the future XD $\endgroup$ – Reedinationer Feb 19 at 23:39
3
$\begingroup$

I've solved my problem! This is the setup that worked for me:

CloudDeploy @ FormFunction[ (* multipage forms on: https://reference.wolfram.com/language/workflow/BuildAMultipageForm.html *)
  { (* open input forms section *)
    { (* this is form number 1 *)
      {"n", "Number of destinations?"} -> "Integer", 
      {"origin", "City of Origin?"} -> "City"
    }, (* close form number 1, and open form input number 2 *)
    Table[{"city" <> ToString[i], "City" <> ToString[i]} -> "City", {i, #n}]& (* dynamically created form number 2 *)
  }, (* Our forms have received all expected inputs, now call our processing function *)
  functionBuildMap[Values[#][[2]], Values[Drop[#, 2]]]&
]

Valuable things I learned:

  1. DO NOT try to build forms with multiple Form[] objects!! I.E. don't try to feed a FormPage into a FormFunction or something similar. It will be a nightmare and you will not accomplish nothing!
  2. DO build them with a single Form[] object (In my case a FormFunction[] did the trick, but FormObject[]'s seem promising for more customization and complex situations.
  3. DO use a := Module[] if you want to use a FormFunction[]. This is how I was able to solve the "feed results into function as inputs" problem. It also makes the finished product look much cleaner (as seen above)
  4. DO troubleshoot form output via Values[#]&. For instance:

    CloudDeploy @ FormFunction[{
      {
        {"n","Number of destinations?"}->"Integer", (* Form 1 *)
        {"origin","City of Origin?"}->"City"
      }, (* End form input section *)
      Table[{"city" <>ToString[i], "City" <> ToString[i]} -> "City", {i, #n}]&  (* Form 2 *)
    },
    Values[#]& (* print all the inputs that would be fed to your function *)
    AppearanceRules-><|
    "Title"->"Wolfram Stuff",
    "Description"->"Do things",
    |>,
    PageTheme-> "Black"
    ] (* close FormFunction *)
    

    Which after I filled it out, just displayed something like

(AppearanceRules*(Values[#1] & ) -> Association["Title" -> "Wolfram Stuff", "Description" -> "Do things", Null])[<|"n" -> 2, "origin" -> Entity["City", {"NewYork", "NewYork", "UnitedStates"}], "city1" -> Entity["City", {"Moscow", "Moscow", "Russia"}], "city2" -> Entity["City", {"London", "GreaterLondon", "UnitedKingdom"}]|>]

Informing me it correctly decided n = 2 cities, what the origin is, what city1 and city2 are! Now the only thing left to do is figure out how to parse that information into your function! In my case the values were parsed as Values[#][[2]], Values[Drop[#, 2]]

$\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.