using ng-repeat to fill boxes with different numbers, and adding animations

301 views
Skip to first unread message

hale erkan

unread,
Mar 17, 2021, 6:45:39 PM3/17/21
to oTree help & discussion
Hi there, 
I am trying to create a game where after players start they are shown prices in boxes one by one (in every 3 seconds) and when they click "Buy" it means they accept the last price shown and game ends.  I attached a screenshot of the game.  Currently I can only put a single price in the boxes, I tried defining an array of prices but cannot use it as in the ng-repeat.  I defined the prices in the pages.py (in def vars_for_template(self):)  and put the array in config.py page (pictures attached).  when I put {{otree_vars.prices}} after the dollar sign it writes down all the prices in every box and when I use {{otree_vars.prices[column]}} it gives an error. 


There are two more things I am having trouble with: 
-  I want to make the old boxes greyed out every time a new box shows a price.  
-  I want the dots on the lines to show up with the prices, currently I put them on a canvas but I have no idea how to add them the animation so that they can how up with the corresponding price. 


(the variable names are quite different in the code because I am modifying a code where users are trying to collect boxes without finding the bomb, like a stopping game)

thanks!!
hale 


Screen Shot 2021-03-17 at 5.18.58 PM.png
Screen Shot 2021-03-17 at 5.29.21 PM.png
Screen Shot 2021-03-17 at 5.27.52 PM.png
Screen Shot 2021-03-17 at 5.28.32 PM.png

Rok

unread,
Mar 18, 2021, 8:34:55 AM3/18/21
to oTree help & discussion
I was not aware of ng-repeat before, but I took a quick look and I don't think it can do what you need it to do. It seems to me it just generates HTML on page load, it will not update the page (your numbers/prices) in real time every few seconds.

You could do what you need in javascript, here's an example that could help:


I hope I understood what you were trying to achieve correctly.

Good luck,
Rok

Rok

unread,
Mar 18, 2021, 8:45:40 AM3/18/21
to oTree help & discussion
In the last example, sliders were not vertical on firefox. Here's a fix: https://jsfiddle.net/g71fuq3b/

It looks fine on chrome but pretty terrible on firefox, unfortunately.

Rok

hale erkan

unread,
Mar 18, 2021, 1:32:59 PM3/18/21
to oTree help & discussion
Hi Rok, 
This is incredibly helpful!! Thanks a lot! By any chance can I just display one number on each box (like display  1 in the firsts box on the left, display 3 in the second one and so on)?

Thanks
Hale 

Rok

unread,
Mar 18, 2021, 9:52:05 PM3/18/21
to oTree help & discussion
So you would like all the boxes to change their number at the same time?

hale erkan

unread,
Mar 18, 2021, 10:58:22 PM3/18/21
to oTree help & discussion
No no, after the game starts I want the firsts price to be shown in the first box, then 3 seconds later I want the second price to be show up in the second box, then third price on the third box and so on.  And every time a new price is shown I want the previous boxes to be greyed out because those prices are no longer valid.  Player can not pick an old offer,  when she clicks "buy" she will pay the last price shown.

Thanks!!
Hale  

Rok

unread,
Mar 19, 2021, 7:58:47 AM3/19/21
to oTree help & discussion
https://jsfiddle.net/uex80wgo/1/

You should also set your form model and form fields on the page where you display the sliders to 'player' and ['selected_price']. Set a variable selected_price in Player. That's where the chosen price will be saved.

Good luck,
Rok

hale erkan

unread,
Mar 19, 2021, 11:59:10 AM3/19/21
to oTree help & discussion
thanks, this is very helpful!! 

hale erkan

unread,
Mar 21, 2021, 8:26:02 PM3/21/21
to oTree help & discussion
Hi Rok, 
I couldnt display the boxes and the slider to the player. I added selected_price to both the pages.py and model.py. I added the screenshots.  Should I be doing something else? 
Thanks, 
Hale 

Screen Shot 2021-03-21 at 7.07.51 PM.png
Screen Shot 2021-03-21 at 7.13.01 PM.png
Screen Shot 2021-03-21 at 7.13.11 PM.png

Rok

unread,
Mar 21, 2021, 11:55:18 PM3/21/21
to oTree help & discussion
In the first screenshot the debug info is telling you that there is an error, and to add {{ form.selected_price.errors }} somewhere to your HTML to see the error. Do that and post the error here so we can see what's wrong.

Also, I think you forgot to set a value (like "=10") the second line in before_next_page()?

Rok

hale erkan

unread,
Mar 22, 2021, 11:13:25 AM3/22/21
to oTree help & discussion

Hi Rok, 

This is the message I get (attached). 

Also is there any way to make the position of these sliders slightly  different. Like each slider being slightly higher than the previous one ?


Thanks a lot! 
Screen Shot 2021-03-22 at 10.10.09 AM.png

Rok

unread,
Mar 22, 2021, 12:59:01 PM3/22/21
to oTree help & discussion
Hello,

for the offset, here's an updated fiddle. You can set desired offset of every slider in the createinputs call: createInputs(number_of_sliders, offset_in_pixels);


The error message means that there is no value in your input for selected_price when you try to submit the page. That's the hidden input, right? Try to remove the hidden attribute and see if the values are updated in it every few seconds, like in the sliders. If there is a number in it when you try to submit the page, and you still get the error then I'm not sure what it could mean. Perhaps you have another input element on that page with the attribute name="selected_price" that does not have a set value.. ?

Rok

Message has been deleted
Message has been deleted

hale erkan

unread,
Mar 22, 2021, 5:32:48 PM3/22/21
to oTree help & discussion

Hi, 
when I remove the hidden attribute it only shows a box to fill (attached). I am super confused. Normally I was able to display any figure on the page. It doesnt even show a regular error message. Even if there is an issue with the "selected_price", input-container should be displayed? 

I searched for "selected_price" in the entire document, it has one more occurance in the automatically generated document "0013_auto_20210321_1735.py", I attached the screenshot of that document as well. 

thanks 
Screen Shot 2021-03-22 at 4.31.55 PM.png
Screen Shot 2021-03-22 at 12.58.55 PM.png

Rok

unread,
Mar 23, 2021, 10:02:08 AM3/23/21
to oTree help & discussion
Hmm, all the sliders dissapeared too?

Can you just post your entire html code for that page to something like pastebin.com?

Rok

hale erkan

unread,
Mar 23, 2021, 2:17:43 PM3/23/21
to oTree help & discussion
yea, nothing except the button is shown. I copied the html code for the main page and the other parts of the code (pages.py, models.py, config.py, js and css) as well.
https://pastebin.com/yfHFFxyn

Rok

unread,
Mar 23, 2021, 4:17:18 PM3/23/21
to oTree help & discussion
Hmm, everything looks mostly okay except a few things;

You don't seem to be loading your bret.js in decision.html. (no JS = no sliders)

I'd remove the "{{ form.selected_price.errors }}" from the end of bret.js, not sure if it helps there and if it should be there.

Let me know if it's working. If it's not can you look at your browser's JS console.

Rok

hale erkan

unread,
Mar 24, 2021, 1:08:27 PM3/24/21
to oTree help & discussion
Hi Rok, 
That worked! Thank you very much!! 
I have one more question, I tried to make the boxes aligned horizontally and only the sliders getting higher. But they always change position together, is there an easy way to make the boxes aligned and sliders stay as they are? 

Thanks! 
Message has been deleted
Message has been deleted

Rok

unread,
Mar 25, 2021, 1:19:15 PM3/25/21
to oTree help & discussion
Hmmm, Chris, if you read this, do you have any idea why my previous post got deleted? Is it the imgur links?

anyway, to try and recap my previous post, right now we get the vertical displacement by changing the margin-top (how much space the element wants to have between it and the next element in that direction) of the div that contains the input box and the slider. What you now want to do is change the spacing required between the input box and the slider instead. (see picture attached) 

If this (html, javascript) is something you want to learn and get better at, I suggest you try to figure it out yourself, otherwise you can find the updated link below. :)


Best Regards,
Rok
image.png

Chris @ oTree

unread,
Mar 25, 2021, 1:27:36 PM3/25/21
to oTree help & discussion
Hi, it looks like those posts were caught by the spam filter. Anyway I've whitelisted your email address.

Rok

unread,
Mar 25, 2021, 1:34:08 PM3/25/21
to oTree help & discussion
Thank you. :)

hale erkan

unread,
Apr 7, 2021, 11:58:15 PM4/7/21
to oTree help & discussion
Hi Rok, 
I have a few more questions. I am having trouble with buttons. 
-I am trying to add a "Start" button before setInterval is activated. I basically don't want numbers to be shown before player clicks "Start" but it doesnt work.  
- I also want to add a "Next" button which will appear after "Buy" is clicked and it will direct users to the Results page. (currently "Buy" button directs players to Results page). 

I attached the  JS, html and CSS codes below, I tried most of the suggestions on forums but couldnt make it work. 

JS File ###################################################################################
var current_input = 1;

var value_array = ["$20","$21",'$11','$14','$43','$16','$49','$27','$40','$25','$45','$52','$29','$39','$42','$41','$66','$50','$75','$68'];
var value_array2 = [20,21,11,14,43,16,49,27,40,25,45,52,29,39,42,41,66,50,75,68];
var time_interval = 3000;
var time_value=[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21];
var value_array_position = 0;
var selectionnot_made=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];
var selectionmade=[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,];
var periodcounter = 0;

window.onload = function(){
time_period=0;
// Create desired number of inputs, with the desired offset (in pixels)
createInputs(20, 4);

//var startButton = document.getElementById("start");
// startButton.onclick = function() {
myInterval = setInterval(function(){

time2 = time_value[value_array_position];
document.getElementById("demo").innerHTML =time2;
document.getElementById("currentprice").innerHTML =value_array[value_array_position];;
// Disable (grey out) previous input
if(document.getElementById("input-range-"+(current_input-1)) != null) {
document.getElementById("input-box-"+(current_input-1)).disabled = true;
document.getElementById("input-range-"+(current_input-1)).disabled = true;
}
if(document.getElementById("input-range-"+(current_input)) != null){
// For random value
//new_value = Math.round(Math.random() * 50);
// For value from array
new_value = value_array2[value_array_position];
new_valuestr = value_array[value_array_position];
time = time_value[value_array_position];
value_array_position++;
// if(value_array_position >= value_array.length) {
// value_array_position = 0;
// }

// Set new values to elements
document.getElementById("input-range-"+current_input).value = new_value;
document.getElementById("input-box-"+current_input).value = new_valuestr;

// set the new price to the hidden element
document.getElementById("selected_price").value = new_value;
document.getElementById("time_period").value = time;
document.getElementById("selection_made").value = selectionmade[value_array_position];
current_input++;

}
else {
document.getElementById("selection_made").value = selectionnot_made[value_array_position];
document.getElementById("end-button").click();

clearInterval(myInterval);



}
}, time_interval);
};


function createInputs(numInputs, offsetPixels=4, middleSpacing=10){
container = document.getElementById("inputs-container");
verticalOffset = offsetPixels * (numInputs-1) + middleSpacing;
maxrange=40;
minrange=0;
for(i=1; i<=numInputs; i++){

container.innerHTML += '<div class="input-container"><i> t'+i+'</i> <input type="text" class="input-box" id="input-box-' + i + '" style="margin-bottom:' + verticalOffset + 'px;" readonly/> ' + maxrange + ' <br><input type="range" class="input-range" id="input-range-' + i + '" min='+minrange+' max= '+maxrange+' step="0" value=0 sorient="vertical" readonly/>'+minrange+'</div>';
verticalOffset -= offsetPixels;
maxrange=40+2*i;
minrange=2*i;
}
}





HTML File###################################################################################

{% extends "global/Base.html" %}
{% load staticfiles otree %}
{% load i18n %}


{# ****************************************************************************************************************** #}
{# *** STYLES *** #}
{# ****************************************************************************************************************** #}
{% block styles %}
<link href="{% static 'bret/css/bret.css' %}" rel="stylesheet"/>
<link href="{% static 'bret/css/custom.css' %}" rel="stylesheet"/>
<link href="{% static 'bret/css/fontawesome-all.css' %}" rel="stylesheet"/>
<style type="text/css">.ng-cloak{display:none !important;}</style>

<script src="{% static 'bret/js/angular.min.js' %}"></script>
<script src="{% static 'bret/js/bret.js' %}"></script>
{% endblock %}

{# ****************************************************************************************************************** #}
{# *** SCRIPTS *** #}
{# ****************************************************************************************************************** #}
{% block scripts %}
{% endblock %}
{# ****************************************************************************************************************** #}
{# *** TITLE *** #}
{# ****************************************************************************************************************** #}



{% block content %}





<h2>Time Period: <span id="demo", ></span> </h2>
<h2>Current Price: <span id="currentprice", ></span> </h2>
<div id="inputs-container"

> </div>

<input type="hidden" name="selected_price" id="selected_price" />

<input type="hidden" name="time_period" id="time_period" />
<input type="hidden" name="selection_made" id="selection_made" />


<button id="start" onclick="" >start</button>

<button id="next-button" onclick="">Buy</button>
<button id="end-button" onclick="", style="display:none" >Stop</button>


<button id="resutpage-button" onclick="", style="display:none" >Next</button>

<input type='hidden', class="counter" name="time_of_decision" id="p.time_of_decision()" />

{% endblock %}

CSS File ###################################################################################
.input-range {
-webkit-appearance: slider-vertical;
width: 20px;
height: 150px;
border: 0.2px solid #ab2c6c;
border-radius: 1px;
cursor:pointer;
background: transparent;
border-color: transparent;
color: transparent;

}



.input-box {
width: 32px;
margin-bottom: 10px;
text-align: center;
background: #d4e2fc;
opacity=0.2;
}

.input-container {
margin: 10px;
text-align: center;
font-size: 12px;
width: 27px;
pointer-events: none;
display: inline-block;
vertical-align: top;
}








Rok

unread,
Apr 8, 2021, 2:41:34 PM4/8/21
to oTree help & discussion
Hello,

as the docs say, if you want a button that does not submit the form, you have to set its type to type="button". So your Start and Buy buttons should have that set to not progress the page too early.

For starting the timer with the start button, I moved most of the code from window.onstart to a new function that is ran by the start button. For stopping, i made a small function that just runs clearInterval(myInterval); and ended up adding a small while() loop that sets all the remaining inputs disabled too, because it looks nice to me. Nice styling btw, it looks good. :)

I've copied your above code into jsfiddle and updated it with the stuff I mentioned above. I hope that helps.


Best Regards,
Rok

hale erkan

unread,
Apr 8, 2021, 8:00:15 PM4/8/21
to oTree help & discussion
Thank you very much ! 
I actually want to use CSS file  attached below but it messes up all the alignments of the sliders. I guess I can fix it using padding but then I would have to manually fix it for  every other number of boxes. Do you have any suggestions for that?


##########CSS#####
input[type=range]{
-webkit-appearance: none;

}

input[type=range]::-webkit-slider-runnable-track {
-webkit-transform:rotate(270deg);
margin: auto;
-webkit-appearance: none;
position: relative;
overflow: hidden;
height: 10px;
width: 200px;
cursor: pointer;
border-radius: 0
}

::-webkit-slider-runnable-track {
background: #ddd;
}

::-webkit-slider-thumb {
-webkit-appearance: none;
width: 10px; /* 1 */
height: 10px;
background: #ddd;
box-shadow: -10vw 0 0 10vw dodgerblue;
border: 1px solid #999; /* 1 */

}



.input-box {
width: 32px;
margin-bottom: 10px;
text-align: center;
background: #d4e2fc;
opacity=0.2;
}

.input-container {
margin: 10px;
text-align: center;
font-size: 12px;
width: 27px;
pointer-events: none;
display: inline-block;
vertical-align: top;
}

Rok

unread,
Apr 9, 2021, 11:22:34 AM4/9/21
to oTree help & discussion
Your way of rotating the slider does not work on firefox unfortunately. When i was testing this earlier I only managed to get it to work in the way i suggested above. I see the firefox fix got lost somewhere, so here's a version where the sliders are also vertical on firefox:


the styling of vertical sliders is kind of a weird problem. I tested it for a while now and can't seem to get it to work properly in a sensible manner. It would take all kinds of weird css, html and javascript hacks working together to get them vertical, able to be custom styled, properly positioned and working on all browsers. It seems I am either missing something very obvious, or the technology of 2021 is limited to either vertical sliders or custom styled sliders. :) 

I'm thinking at this point we should just give up on sliders and just make two divs (one for the slider bar and one for the slider thumb) styled to look like a slider, and move them accordingly with javascript.

Let me know if you figure it out.

Good luck,
Rok

Rok

unread,
Apr 9, 2021, 2:29:52 PM4/9/21
to oTree help & discussion
Hey again,

the sliders were bothering me and it got kinda personal at this point, so I've remade them completely with just two basic divs that you should be able to style however you want. Now they look exactly the same on all browsers. :)


Best Regards,
Rok

hale erkan

unread,
Apr 22, 2021, 5:07:15 PM4/22/21
to oTree help & discussion
Thanks a lot
 This is incredibly helpful!!!

hale erkan

unread,
Aug 25, 2021, 11:41:47 AM8/25/21
to oTree help & discussion
Hi, 

Is there a way to disable players from refreshing the game and starting over? 

Thanks, 
Hale 

Rok

unread,
Sep 15, 2021, 11:45:12 PM9/15/21
to oTree help & discussion
I think the best way to do that would be to use a live page. 

That way you can store every decision in real time, then if a player refreshes the page, you load the player's previous moves back from the server and continue their game from where they left off.

Best Regards,
Rok

Reply all
Reply to author
Forward
0 new messages