looping over in the HTML template

1,322 views
Skip to first unread message

Wei James Chen

unread,
May 6, 2021, 7:12:05 AM5/6/21
to oTree help & discussion
Dear all,

I have the following code in my HTML template:
--
<tr>
<td><b>{{p_label_1}}</b></td>

<td><input class="form-check-input" type="radio" id="p1-0" name="p1" value="True"></td>

<td><input class="form-check-input" type="radio" id="p1-1" name="p1" value="False"></td>

</tr>
--
This is 1 of the 9 code block I have, from p1 to p9.
How could I use a loop to replace all 9 blocks with just one?

I am doing this because my label here (like p_label_1) are labels that depend on the value subjects input in the previous screen, so I used a dict of 9 elements like this as my vars_for_template:
--
def vars_for_template(player):
return dict(
p_label_1='XXXX {} '.format(player.S1_Switching + 1),
p_label_2='XXXX {} '.format(player.S1_Switching + 2),
p_label_3='XXXX {} '.format(player.S1_Switching + 3),
p_label_4='XXXX {} '.format(player.S1_Switching + 4),
p_label_5='XXXX {} '.format(player.S1_Switching + 5),
p_label_6='XXXX {} '.format(player.S1_Switching + 6),
p_label_7='XXXX {} '.format(player.S1_Switching + 7),
p_label_8='XXXX {} '.format(player.S1_Switching + 8),
p_label_9='XXXX {} '.format(player.S1_Switching + 9)
)
--

Although I am new to oTree, my experience with zTree, psychtoolbox, etc told me that there must be a way to simplify all these with an elegant solution. 
Please help me~ Thanks a lot~~~

Best,
James


Chris @ oTree

unread,
May 6, 2021, 7:30:12 AM5/6/21
to oTree help & discussion
Rather than passing 9 separate variables to the template, pass a single list whose entries are the numbers and labels,etc.:

[dict(label='XXXX 1', number=1), dict(label='XXXX 2', number=2), ...]

It's very easy to generate a list like that using a for-loop or list comprehension.

but do you need to write the raw HTML? how about using a RadioSelect widget, then do:

{ for field in form }}
<tr>
<th>{{ field.label }}</th>
{{ for choice in field }}
<td>{{ choice.label }} {{ choice }}</td>
{{ endfor }}
</tr>
{{ endfor }}

Wei James Chen

unread,
May 6, 2021, 7:54:35 AM5/6/21
to oTree help & discussion
Hi Chris,

I do use the following code:
--
{ for field in form }}
<tr>
<th>{{ field.label }}</th>
{{ for choice in field }}
<td>{{ choice.label }} {{ choice }}</td>
{{ endfor }}
</tr>
{{ endfor }}
--
On my other screen where I can determine the choice label without using subjects' choice.

In this screen that I am talking about, I think my problem is that the choice label depends on the subject's choice on the previous screen,
so when I write:
--
p1 = make_field('XXX is {} '.format(S1_Switching + 1))
--
where  S1_Switching is the subject's choice in the previous screen, and access p1 in the screen I want I get
--
"<name unknown>" || :param_1
--
On that page.

I did use:
--
def before_next_page(player: Player, timeout_happened):
      player.S1_Switching = S1_Switching(player)
--
to determine the S1_Switching on the previous screen but somehow it is not passed to the template corretly.
Chris @ oTree 在 2021年5月6日 星期四下午7:30:12 [UTC+8] 的信中寫道:

Chris @ oTree

unread,
May 6, 2021, 8:08:06 AM5/6/21
to oTree help & discussion
ok then yes you should pass a list of dicts to the template as shown above. loop over it to make each row.

Wei James Chen

unread,
May 6, 2021, 8:18:21 AM5/6/21
to oTree help & discussion
I tried that, but I am not sure I got it though...

Do these code:
[dict(label='XXXX 1', number=1), dict(label='XXXX 2', number=2), ...] 
pass the correct label to the formfield so that I can access them directly on the page I want?
When I tried to put this on the vars_for_template,
it tells me that I need to submit a dictionary not a list,
should I pass a dict of dict instead?



BTW,
a relating issue is that, when I do 

--
{ for field in form }}
<tr>
<th>{{ field.label }}</th>
{{ for choice in field }}
<td>{{ choice.label }} {{ choice }}</td>
{{ endfor }}
</tr>
{{ endfor }}
--

It seems that the auto-made error message disappears.
The debug mode tells me the following message when some of the radio button were not selected:
--

The form field p10 has errors, but its error message is not being displayed, possibly because you did not include the field in the page. There are 2 ways to fix this:

  1. Include the field with the formfield tag, e.g. {% formfield "p10" %}
  2. If you are not using formfield but are instead writing the raw HTML for the form input, remember to include {{ form.p10.errors }} somewhere in your page's HTML.

The following other field(s) have the same issue:

  • p20
  • p30
  • p40

While debugging, you can display all errors in the form with {{ form.errors }}.

--

Where should I put the {{ form.p10.errors }} in the above table format?


Sorry if this is a very naive question, this is my first week with oTree.


Best,

James 

Chris @ oTree 在 2021年5月6日 星期四下午8:08:06 [UTC+8] 的信中寫道:

Chris @ oTree

unread,
May 6, 2021, 8:46:05 AM5/6/21
to oTree help & discussion
def vars_for_template(player):
  rows =  [dict(label='XXXX 1', number=1), dict(label='XXXX 2', number=2)] 
  return dict(rows=rows)

Template:

{{ for row in rows }}
<tr>
<td><b>{{row.label}}</b></td>
<td><input class="form-check-input" type="radio" name="p{{ row.number }}" value="True"></td>
<td><input class="form-check-input" type="radio" name="p{{ row.number }}" value="False"></td>
</tr>
{{ endfor }}

Wei James Chen

unread,
May 6, 2021, 11:49:46 AM5/6/21
to oTree help & discussion
Dear Chris,

Thanks a lot! This totally saved my day!
Only one minor issue now...

When I use:
--
{{ for field in form }}
<tr>
<th>{{ field.label }}</th>
{{ for choice in field }}
<td>{{ choice }}</td>
{{ endfor }}
</tr>
{{ endfor }}
--
and 
--
{{ for row in rows }}
<tr>
<td><b>{{row.label}}</b></td>
<td><input class="form-check-input" type="radio" name="p{{ row.number }}" value="True"></td>
<td><input class="form-check-input" type="radio" name="p{{ row.number }}" value="False"></td>
</tr>
{{ endfor }}
--

The default error message cannot be properly showed up.
For example, if one of the True/False choices was not chosen, the old {{ formfield }} method would light up the radio button that was not chosen yet.
But if I used the current table method, it will show:
"Please fix the errors in the form." and 
"The form field p10 has errors, but its error message is not being displayed, possibly because you did not include the field in the page. There are 2 ways to fix this:
  1. Include the field with the formfield tag, e.g. {% formfield "p10" %}
  2. If you are not using formfield but are instead writing the raw HTML for the form input, remember to include {{ form.p10.errors }} somewhere in your page's HTML.

The following other field(s) have the same issue:

  • p20
  • p30
  • p40
While debugging, you can display all errors in the form with {{ form.errors }}."

More strangely, in the second table method, when there is an error when submitting the form, it will clear up all the radio buttons that I have chosen.

Is there a way to fix these problems?

Thanks a lot~


Chris @ oTree 在 2021年5月6日 星期四下午8:46:05 [UTC+8] 的信中寫道:

Chris @ oTree

unread,
May 6, 2021, 5:50:35 PM5/6/21
to Wei James Chen, oTree help & discussion
If the radio buttons are required, add “required” to the <input>. 

Sent from my phone

On May 6, 2021, at 11:49 PM, Wei James Chen <thisis....@gmail.com> wrote:

Dear Chris,
--
You received this message because you are subscribed to the Google Groups "oTree help & discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to otree+un...@googlegroups.com.
To view this discussion on the web, visit https://groups.google.com/d/msgid/otree/1479e91d-27f1-4796-bf2e-27a2fffb43b1n%40googlegroups.com.

Yuet

unread,
May 25, 2021, 12:58:18 AM5/25/21
to oTree help & discussion

Dear all,

I have the same problem with James:

When I use {{ choice }} to render my Radio Button, my choice will be remembered after submitting the form with an error message.
However, when I use <input class="form-check-input" type="radio" name="p{{ row.number }}" value="True">, it will clear up all the radio buttons.

I have tried several ways in jQuery, but I didn't get it through.

1. the code below will help me remember my choice, but it will auto-select my Round 1 choice in ALL ROUNDS after Round 1 😂.

$(function () {
$('input[type=radio]').each(function () {
var state = JSON.parse(localStorage.getItem('radio_' + this.id));
if (state)
this.checked = state.checked;
});
});

$("button").click(function () {
$('input[type=radio]').each(function () {
localStorage.setItem(
'radio_' + this.id, JSON.stringify({checked: this.checked})
);
});
});

2. So I try to add sth to clear up the localStorage:

$(window).bind('load', function () {
window.localStorage.clear();
});
$("p").load(function () {
alert("Goodbye!");
});

And this takes me back to my start point (clear up all the radio buttons)...
I tried different selectors and different events, but it either remembers nothing or remembers everything T_T

Sorry for my naive question, actually I don't know how to use jQuery, I just get the code above by google with a little change. 
So could anyone give me some hint? Thanks a lot!!!

Yuet

Yuet

unread,
May 29, 2021, 3:50:53 AM5/29/21
to oTree help & discussion

Hi everyone, sorry if there is any unclear part of my question above.
My question is: when input is customized in the HTML, it will clear up all the choices subjects made after submitting the form with an error message.
I'm here to update my solution (after several days' struggle lol), for anyone who has the same problem with me in the future.

My solution is to:
1. Store the choices in the sessionStorage.
2. Check if the error message exists, if true, then check the radio buttons as the choices stored in the sessionStorage


And here is the code added in the {{block scripts}}

if ($(".otree-form-errors.alert.alert-danger").length > 0) {

$('input[type=radio]').each(function () {
let state = JSON.parse(sessionStorage.getItem('radio_' + this.id));

if (state)
this.checked = state.checked;
});
}

$("button").click(function () {
$('input[type=radio]').each(function () {
sessionStorage.setItem(

'radio_' + this.id, JSON.stringify({checked: this.checked})
);
});
});

Hope it will help someone in the future, cause I was always inspired by the discussions in this group.

Yuet
Reply all
Reply to author
Forward
0 new messages