#1 by sried
Hi everyone! I have a text input field that I do display through CSS in the beginning. After a certain answer given to a previous question, the text input pops up. However, when you enter something and submit the page, the text is not saved. Here is a MWE: ________________ __init.py: ... class Player(BasePlayer): first_q = models.IntegerField() other_q = models.StringField() class MyPage(Page): form_model = 'player' form_fields = ['first_q'] ## note here that I do not include other_q because it does not necessarily pop up ... ________________ MyPage.html: ... <style> #other_quest { display: none; } </style> <label for="first_q">Please type in some number.</label> <br> <input name="first_q" id="first_q" type="number" oninput="show_other_q()"/> { formfield_errors "first_q"}} <div id="other_quest"> <label for="other_q">Why is this number greater 0?</label> <br> <input name="other_q" id="other_q" type="text"/> </div> <script> function show_inc_other_spec (e) { let first_quest = document.getElementById('first_q').value; if (parseInt(first_quest)>0) { document.getElementById('other_quest').style.display = 'block'; } else { document.getElementById('other_quest').style.display = 'none'; } } </script> The first variable first_q is saved properly. The second one, other_q, is not saved, even when it is displayed and an answer is entered. Do you have any idea what to do with this?
#2 by BonnEconLab
I’m pretty sure that you have to include 'other_q' in your form_fields definition for participants’ input to be recorded in the oTree database: form_fields = ['first_q', 'other_q'] To allow it to be empty, make it “optional”, as described in https://otree.readthedocs.io/en/latest/forms.html#optional-fields: other_q = models.StringField(blank=True)
#3 by BonnEconLab
PS: If you would like to force your participants to provide an answer in the case that the “other_q” input is displayed, you could add the following to the “if” branch of your JavaScript code: document.getElementById('other_q').required = true;
#4 by sried
Hello Holger! Thanks for the solution, that worked very smooth and even allows me to use the {{ formfield 'other_q' }} option. That works smoothly. What does not work, however, is to use the models.StringField(blank=True)-option and then use forminputs.other_q.required=true. Thus, the following code does not prevent the field of other_q to be blank. ... <label for="first_q">Please type in some number.</label> <br> <input name="first_q" id="first_q" type="number" oninput="show_other_q()"/> { formfield_errors "first_q"}} <div id="other_quest"> {{ formfield "other_q" }} {{ formfield_errors "other_q"}} </div> <script> function show_inc_other_spec (e) { let first_quest = document.getElementById('first_q').value; if (parseInt(first_quest)>0) { document.getElementById('other_quest').style.display = 'block'; forminputs.other_q.required = true; } else { document.getElementById('other_quest').style.display = 'none'; } } </script>
#5 by BonnEconLab
The code you posted contains a couple of typos – for instance, a missing “{”, and you renamed the JavaScript function but did not change the “oninput” attribute accordingly. Im my testing, the following works flawlessly: In __init__.py: class Player(BasePlayer): first_q = models.IntegerField() other_q = models.StringField(blank=True) class Test(Page): form_model = 'player' form_fields = ['first_q', 'other_q'] In Test.html: <label for="first_q">Please type in some number.</label> <br> <input name="first_q" id="first_q" type="number" oninput="show_other_q()"/> {{ formfield_errors "first_q"}} <div id="other_quest" style="display: none;"> {{ formfield "other_q" }} {{ formfield_errors "other_q"}} </div> {{ next_button }} <script> function show_other_q() { let first_quest = document.getElementById('first_q').value; if (parseInt(first_quest)>0) { document.getElementById('other_quest').style.display = 'block'; forminputs.other_q.required = true; } else { document.getElementById('other_quest').style.display = 'none'; forminputs.other_q.required = false; } } </script> • If I type in a number > 0 into the first input field, the second input field appears and also has to be filled for the next_button to work. • If I type in a number <= 0, then the second input field is now shown and also not required for the next_button to work.
#6 by sried
Sorry for the typos, I wrote the code from scratch since it is of course only a dummy example of my problem in the real code. Interestingly, when I set up a testapp with your code exactly, the problem still remains. Leaving other_q blank after it pops up due to a number > 0, I can still press the next_button without any error_message. My otree version was updated today to 5.8.4 I don't know where the problem could be. Do you have any other idea? I run otree in a virtual environment using Python 3.9.6 on Windows 10 Enterprise.
#7 by BonnEconLab
Can you send me a ZIP archive of your test app/attach it to a post in this forum?
#8 by sried
Of course, here is the otreezip of the very basic dummy problem. Thanks again for your help! I am very grateful!
#9 by BonnEconLab
Hmmmmmm, maybe there has been a misunderstanding. The code you sent works perfectly on my computer (macOS 12.5, oTree 5.8.5, Python 3.8.2). I have tested it with three browsers: Firefox, Chrome, and Safari. By “it works perfectly,” I mean that I cannot press the “Next” button unless I fill in the “q_other” field. Of course, this does not produce an oTree-generated error message, because the field is never submitted if it is left empty and required is set to true. The only “error” message I get is “Please fill out this field.”
#10 by sried
That really is interesting. I tested it on Firefox, Chrome, and Edge, and it doesn't work. I even updated it to otree 5.9.0 now and it still doesn't work. I can resume to the next page without any issues.
#11 by BonnEconLab
Well, then the solution is obvious: Get a Mac. ;-) Kidding aside, I can only think of one reason: The “forminputs.other_q.required = true;” part of your JavaScript code is not executed by your browser.
#12 by sried
That is probably the only reasonable explanation. The interesting question would now be: What happens on the participants' PCs when this is set up online? It would be a pity if it works for some, but not for all..
#13 by BonnEconLab
I’m really puzzled. I just tested the code that you attached previously on a Windows machine in Firefox, Chrome, and Edge, and none of the three browsers lets me advance to the next page if I leave “Other Q” empty. I have an idea for an alternative, though ...
#14 by BonnEconLab
... and the alternative idea is: Let both fields be mandatory in __init__.py: class Player(BasePlayer): first_q = models.IntegerField() other_q = models.StringField() In Test.html, include the following: {{ block content }} <label for="first_q">Please type in some number.</label> <br> <input name="first_q" id="first_q" type="number" oninput="show_other_q()"/> {{ formfield_errors "first_q"}} <div id="other_quest" style="display: none;"> {{ formfield "other_q" }} </div> <p>{{ next_button }}</p> <script> function show_other_q() { let first_quest = document.getElementById('first_q').value; if (parseInt(first_quest) > 0) { document.getElementById('other_quest').style.display = 'block'; if (document.getElementById('id_other_q').value == "N/A") { document.getElementById('id_other_q').value = ""; }; } else { document.getElementById('other_quest').style.display = 'none'; if (document.getElementById('id_other_q').value == "") { document.getElementById('id_other_q').value = "N/A"; }; } } </script> {{ endblock }} This way, you get an “N/A” entry for “other_q” when the answer to the first question is ≤ 0 (on the first attempt). If the entry for the first question is > 0, the “other_q” input field is initially empty, and the form cannot be submitted if the field is left empty. Does this help?
#15 by Chris_oTree
forminputs is a new feature, so make sure you are updated to the latest otree version (currently 5.9): https://otree.readthedocs.io/en/latest/forms.html#javascript-access-to-form-inputs
#16 by sried
Hi Chris, I did update it yesterday to 5.9.0, but it still did not work. I don't know why, especially since it worked for Holger.