3
$\begingroup$

I have an Association of string keys and numerical values, which is read from a configuration file. I would like to allow users to drag sliders and adjust the numerical values according to their preferences and re-run computations. However, the formfunc in FormPage does not get the new values upon submission. The computations only ever receive the default values read from the configuration file.

The following MWE illustrates the problem:

RightComposition[
    (*The line below simulates the result of reading the configuration file*)
    CharacterRange["a", "h"] &, AssociationMap[RandomInteger[{1, 64}] &],
    (*The functions below generate the FormPage with sliders*)
    KeyValueMap[With[{s = #1, t = #2},
        Rule[s, Rule[
            Association[{
                "Interpreter" -> Restricted["Real", {0, 100}],
                "Control" -> (Slider[t, {0, 100, 0.5}, Appearance -> "Labeled"] &)
            }],
            t
        ]]
    ] &],
    FormPage[#, Identity] &
][]

Note that dragging around the sliders does not change the values that are submitted. The resulting Association is always the same as the initial one.

What am I doing incorrectly?

Partial workaround

Just in case someone else has encountered this problem and is about to waste a lot of time trying to figure out a solution: the only bad approximation I could construct was to change the KeyValueMap above to the following:

KeyValueMap[(#1 -> Table[i, {i, 0, 100, 0.5}] -> #2) &]

In order words, I have had to remove sliders and use drop-downs instead.

$\endgroup$
  • $\begingroup$ Using the wolfram cloud $\endgroup$ – M.R. Jun 20 '18 at 2:48
  • $\begingroup$ Unfortunately, it's not working in the desktop either. My problem pertains to the desktop. $\endgroup$ – Shredderroy Jun 20 '18 at 4:31
  • $\begingroup$ Do you plan to use your Forms on Cloud? Because if you are interested in desktop interfaces, I'd not use them. $\endgroup$ – Kuba Jun 21 '18 at 6:42
1
$\begingroup$

You need to leave a slot for a variable controlled by the Slider. See ref/FormFunction / Scope / Controls / the last example.

In your case it will be

"Control" -> (Slider[#, {0, 100, 0.5}, Appearance -> "Labeled"] &)

It works but is awfully slow. Forms main goal is to be deployed and I find its desktop interface limited.

If the final goal is to deploy it to cloud, don't test on desktop, deploy and test there. Those are different front ends and different things may work/not work there.


Edit

I wanted the initial positions of the sliders to be in the default positions initially.

See documentation for FormFunction, you can provide default values with "Default" key value. Otoh I think what you are after is "Input".

Table[
   key -> <|
     "Interpreter" -> Restricted["Real", {0, 100}]
   , "Control" -> (Slider[#, {0, 100, 0.5}, Appearance -> "Labeled"] &)
   , "Input" :> RandomInteger[{1, 64}]
   |>
 , {key, CharacterRange["a", "h"]}
 ] // 
   FormPage[#, Identity] & // 
     CloudDeploy[#, Permissions -> "Public"] &

You can change :> to -> if you don't want new initial values each time.


Desktop notes

If you want a desktop interface, read about DynamicModule and related things here or in documentation. And try to utilize them creating custom interfaces. The drawback is that validation won't be there out of the box but you will have a fine grained control over the UI.

Here is your form with custom code:

DynamicModule[ {form ,  result}
, Column[{
    Dynamic[Refresh[Grid[ 
      {#, LabeledSlider[Dynamic[form[#]], {0, 100, .5}]}& /@ Keys[form] 
    ], None ]]
  , DefaultButton["OK", result = Identity @ form
    , Method->"Queued", ImageSize -> CurrentValue["DefaultButtonSize"]
    ]    
  , Dynamic[result, TrackedSymbols:>{result}]
  }]
, UnsavedVariables :> {form,  result}  
, Initialization :> (
    form = Association @ Table[key->RandomInteger[{1,64}],{key, CharacterRange["a","h"]}];

    result = ""
  )  
]

It works way better but it does not scale well as the whole form is kept in one symbol form. You can try to play with DynamicObjects` package, Scaling Dynamic widgets up, and do something like:

DynamicModule[ {form ,  result, keys = CharacterRange["a","h"]}
, FrontEndModule[
    Column[{
      Grid[ 
        {#, LabeledSlider[Dynamic[FrontEndSymbol["form", #]], {0, 100, .5}]}& /@ keys 
      ]
    , Dynamic[FrontEndSymbol["form", "a"]] 
    , DefaultButton["OK", result = Identity @ form[], Method->"Queued", ImageSize -> CurrentValue["DefaultButtonSize"]]    
    , Dynamic[result, TrackedSymbols:>{result}]
    }]  
  , Initialization :> (
      Do[
        With[{key = key}
        , FrontEndSymbol["form", key] = RandomInteger[{1,64}]
        ]
      , {key, keys}
      ];
      result = "";
      form[]:= Association[# -> FrontEndSymbol["form", #] & /@ keys]
    )  
  ]
]
|improve this answer|||||
$\endgroup$
  • $\begingroup$ Thank you very much for the general advice. I will certainly keep it in mind. Unfortunately, I do have to deploy to the cloud eventually--but until next week I can continue to prove the concepts from my desktop. I did actually have the slot in Control, but I noticed that that always showed all the sliders on the left. I wanted the initial positions of the sliders to be in the default positions initially. $\endgroup$ – Shredderroy Jun 21 '18 at 18:59
  • $\begingroup$ @Shredderroy see my edit. $\endgroup$ – Kuba Jun 22 '18 at 5:56

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.