Roll20 uses cookies to improve your experience on our site. Cookies enable you to enjoy certain features, social sharing functionality, and tailor message and display ads to your interests on our site and others. They also help us understand how our site is being used. By continuing to use our site, you consent to our use of cookies. Update your cookie preferences .
×

Action button inside a repeating section. Is it possible ?

1602664490

Edited 1602692603
I need to use "type=action" button to increase / decrease a hidden Input in a repeating section, and I fail to get my script to work. I read on wiki that an action buttons should not have underscores in their names. Except that, to reference it, I have to use "repeating_id_buttonname" format. Is it the cause of my troubles ? If it is the case, is there a way around it or should I find another way ? Thx for any help, Kord Here is an example of one of my repeating sections : <fieldset class="repeating_vigspecs"> <!-- Skills Vigueur --> // removed lines non related to my problem (roll buttons that works) <div class="right"> <input type="hidden" name="attr_vigspec" class="smallnumber shortnumber" min = "0" value="9"/> <!-- hidden value to upgrade --> <button type="action" name="act_vigspecinc" class="incdec">+</button> <!-- action button +1, does not work --> <button type="action" name="act_vigspecdec" class="incdec">-</button> <!-- action button -1, does not work --> <input type="number" name="attr_vigspecdice" class="nonumber shortnumber" readonly="true" />D+ <!-- Calculated value updated by worker script, works fine--> <input type="number" name="attr_vigspecpip" class="nonumber shortnumber" readonly="true" /> <!-- Calculated value updated by worker script, works fine --> </div> </fieldset> Here is the code handling the buttons : const increase = (attribute, max = 99) => { // increase attribute by 1 up to max (99 if unspecified) value getAttrs([attribute], function (values) { var update = {}; const score = parseInt(values[attribute], 10)||0; // extrait la valeur de l'attribut, en décimal if (score < max) {update[attribute] = score + 1;} // augmente de 1, si la valeur max n'est pas atteinte (99 par défaut) else {update[attribute] = max;} // sinon force à la valeur max, ce qui rattrape un overshoot setAttrs(update); }); }; const decrease = (attribute, min = 0) => { // decrease attribute by 1 up to min (0 if unspecified) value getAttrs([attribute], function (values) { var update = {}; const score = parseInt(values[attribute], 10)||0; // extrait la valeur de l'attribut, en décimal if (score > min) {update[attribute] = score - 1;} // réduit de 1, si la valeur min n'est pas atteinte (0 par defaut) else {update[attribute] = min;} // sinon force à la valeur min, ce qui rattrape un undershoot setAttrs(update); }); }; const skills = { // Object collecting arrays of skills, indexed by stat       vig: ['athl','lutt','pui','res'],        agi: ['acro','melee','disc','equi','esq'],     coord: ['arm','arti','attel','lanc','larc'],     esp: ['comm','cult','ling','meca','med','ori'],     per: ['arts','debr','jeu','obs','pist','surv'], aura: ['blu','cmd','crg','degu','pss','sed'], }; const maxstat = 30; const minstat = 3; const maxskill = 24; const maxspec = 24; const minspec = 9 /* Handling action buttons for stats ans skills - WORKS FINE */ Object.keys(skills).forEach(stat => {                            // convert the skills into an array (of stats), and loop through them on(`clicked:${stat}inc`, function () {                    // sur clic des boutons "+": act_viginc, act_agiinc, etc... increase(stat, maxstat); }); on(`clicked:${stat}dec`, function () {                    // sur clic des boutons "-": act_vigdec, act_agidec, etc... decrease(stat, minstat) }); }); Object.keys(skills).forEach(stat => { // convert the skills into an array (of stats), and loop through them const skills_handled = skills[stat]; // get the array of skills inside each master stat skills_handled.forEach(skill => { on(`clicked:${skill}inc`, function () { // sur clic des boutons "+": act_athlinc, act_luttinc, etc... increase(skill, maxskill); }); on(`clicked:${skill}dec`, function () { // sur clic des boutons "-": act_athldec, act_luttdec, etc... decrease(skill); }); }); }); /* Handling action buttons in repeating section - DOES NOT WORK */ Object.keys(skills).forEach(stat => { // convert the skills into an array (of stats), and loop through them getSectionIDs('repeating_' + stat + 'specs', function (idarray) { const specs_handled = []; for (let i = 0; i < idarray.length; i++) { specs_handled.push('repeating_' + stat + 'specs_' + idarray[i] + '_' + stat + 'spec'); // met les valeurs des repeating specs existants avec l'id } specs_handled.forEach(spec => { on(`clicked:${spec}inc`, function () { // sur clic des boutons "+": repeating_[stat]specs_[id]_[stat]specinc, etc... increase(spec, maxspec); }); on(`clicked:${spec}dec`, function () { // sur clic des boutons "-": repeating_[stat]specs_[id]_[stat]specdec, etc... decrease(spec, minspec); }); }); }); });
1602664979

Edited 1602665054
GiGs
Pro
Sheet Author
API Scripter
For the buttons inside a repeating section, you need to use the : syntax on the onchanged line: This section is failing: on(`clicked:${spec}inc`, function () // sur clic des boutons "+": repeating_[stat]specs_[id]_[stat]specinc, etc... increase(spec, maxspec); }); because for the on() line, the attribute needs to be in this form: on('clicked:repeating_[stat]specs:[stat]specinc' So you need to restructure your functions to handle that. You want to put the on(clicked) event before getSectionIDs function.
1602667011

Edited 1602667830
>> on('clicked:repeating_[stat]specs:[stat]specinc'    (Edit : sorry, bad copy/paste) I don't grasp it. If I do that, a click will change all the lines in the repeating section ? How do I point to the exact line (and spec) I want to change if don't get the section Ids ? >> You want to put the on(clicked) event before getSectionIDs function. Is it mandatory ?
1602669385

Edited 1602669455
GiGs
Pro
Sheet Author
API Scripter
on(clicked) wont recognise the full attribute name in this format: repeating_section_-4576974_attribute_name. You can do on('clicked:repeating_section") or  on('clicked:repeating_section:attribute_name") , but you cant have a name with the row id in there. If you want the row id, you can get it using eventInfo (described in the wiki). There's an eventInfo.sourceAttribute which will show the full name of the attribute and row that triggered it. You can get the rowid doing something like let rowid = eventInfo.sourceAttribute.split("_")[2]; But if you structure that code appropriately, you dont need to get the row id. Roll20 will supply it behind the scenes if your build the worker properly - but this means you cant use the same function for your repeating and non-repeating attributes. For example, here's a simple version of the worker that will handle the + button. I havent tested this, but it shows the syntax. With this approach you dont need to supply a row id - since the action button is on the same row as the attribute it is affecting, roll20 "knows" which row to use, as long as you use this syntax. Object.keys(skills).forEach(stat => {  // convert the skills into an array (of stats), and loop through them     on(`clicked:repeating_${stat}specs:${stat}specinc`, function () {                                            getAttrs([`repeating_${stat}specs_${stat}spec`], values => {             let score = parseInt(values[`repeating_${stat}specs_${stat}spec`]) || 0;             score =+ 1;             setAttrs({                 [`repeating_${stat}specs_${stat}spec`]: score             });         });     }); }); You'd obviously need to tweak this to handle your max checking. But the principle is there for you.
Okay, many thanks, it is clearer now. I missed the critical info on the way "on:clicked" was working. I will try to adapt your code snippet for my need, and post the result here.
1602669873
GiGs
Pro
Sheet Author
API Scripter
Great! Good luck :)
1602686688

Edited 1602692454
Okay, thanks again, it worked. The simplest code in fact, was to reuse  my increase and decrease functions, with the syntax you suggested. It worked with a call like this : increase(`repeating_${stat}specs_${stat}spec`, maxspec); here is the full implementation, for those interested, works fine : const increase = (attribute, max = 99) => { // increase attribute by 1 up to max (99 if unspecified) value getAttrs([attribute], function (values) { let score = parseInt(values[attribute], 10) || 0; // extrait la valeur de l'attribut, en décimal if (score < max) {score += 1;} // augmente de 1, si la valeur max n'est pas atteinte (99 par défaut) else {score = max;} // sinon force à la valeur max, ce qui rattrape un overshoot setAttrs({[attribute]:score}); }); }; const decrease = (attribute, min = 0) => { // decrease attribute by 1 up to min (0 if unspecified) value getAttrs([attribute], function (values) { let score = parseInt(values[attribute], 10) || 0; // extrait la valeur de l'attribut, en décimal if (score > min) {score += -1;} // réduit de 1, si la valeur min n'est pas atteinte (0 par defaut) else {score = min;} // sinon force à la valeur min, ce qui rattrape un undershoot setAttrs({[attribute]:score}); }); }; const skills = { // Object describing arrays of skills codes , indexed by stat code, used further in the code to manipulate attribute values vig: ['athl','lutt','pui','res'], agi: ['acro','melee','disc','equi','esq'], coord: ['arm','arti','attel','lanc','larc'], esp: ['comm','cult','ling','meca','med','ori'], per: ['arts','debr','jeu','obs','pist','surv'],         aura: ['blu','cmd','crg','degu','pss','sed'], }; const maxstat = 30; const minstat = 3; const maxskill = 24; const minskill = 0; const maxspec = 24; const minspec = 9; /* Handling action buttons for stats ans skills */ Object.keys(skills).forEach(stat => { // convert the skills into an array (of stats), and loop through them on(`clicked:${stat}inc`, function () { increase(stat, maxstat); }); on(`clicked:${stat}dec`, function () { decrease(stat, minstat) }); }); Object.keys(skills).forEach(stat => { // convert the skills into an array (of stats), and loop through them const skills_handled = skills[stat]; // get the array of skills inside each master stat         skills_handled.forEach(skill => { on(`clicked:${skill}inc`, function () { increase(skill, maxskill); }); on(`clicked:${skill}dec`, function () { decrease(skill, minskill); }); }); }); /* Handling action buttons in repeating section */ Object.keys(skills).forEach(stat => { on(`clicked:repeating_${stat}specs:${stat}specinc`, function () { increase(`repeating_${stat}specs_${stat}spec`, maxspec); }); on(`clicked:repeating_${stat}specs:${stat}specdec`, function () { decrease(`repeating_${stat}specs_${stat}spec`, minspec); }); }); }); here is a sample of HTML where these scripts apply: A stat : <!-- insert your rolls buttons here --> <div class="standard right"> <input type="hidden" name="attr_vig" value="3"/> <!-- hidden --> <button type="action" name="act_viginc" class="incdec">+</button>              <!-- Increases vig by 1 when clicked --> <button type="action" name="act_vigdec" class="incdec">-</button>              <!-- Decreases vig by 1 when clicked -->  <input type="number" name="attr_vigdice" class="nonumber" readonly="True"/>D+ <!-- These 2 lines display Dice and Pip values (D6 system) calculated from attr_vig by a script--> <input type="number" name="attr_vigpip" class="nonumber" readonly="True"/> </div> A skill : <!-- insert your rolls buttons here --> <div class="standard right"> <input type="hidden" name="attr_athl" value="0"/> <!-- hidden --> <button type="action" name="act_athlinc" class="incdec">+</button> <button type="action" name="act_athldec" class="incdec">-</button> <input type="number" name="attr_athldice" class="nonumber shortnumber" readonly="true" />D+ <input type="number" name="attr_athlpip" class="nonumber shortnumber" readonly="true" /> </div> Note that the stat and skill names should correspond to the values that are inserted in the skills objet within the script. Here 'vig' is the key to this 'athl' skill. And finally the specs, which are based on stat and skill, but for presentation clarity are displayed within a unique repeating section for each stat. A select input records the name (use same code as in the skills object for value) of the skill the spec is based on. <fieldset class="repeating_vigspecs"> <!-- Skills Vigueur -->     <-- Rolls here -->        <select class="skilltext" name="attr_vigspectype" style="width:80px;">         <option value="athl" selected="selected">Athletisme</option> <option value="lutt">Lutte</option> <option value="pui">Puissance</option> <option value="res">Résistance</option>     </select>     <input class="skilltext" type="text" name="attr_vigspecname"/>     <div class="right"> <input type="hidden" name="attr_vigspec" min = "0" value="9"/> <!-- hidden --> <button type="action" name="act_vigspecinc" class="incdec">+</button> <button type="action" name="act_vigspecdec" class="incdec">-</button> <input type="number" name="attr_vigspecdice" class="nonumber shortnumber" readonly="true" />D+ <input type="number" name="attr_vigspecpip" class="nonumber shortnumber" readonly="true" />     </div> </fieldset>
1602697002
GiGs
Pro
Sheet Author
API Scripter
Kordolius said: Okay, thanks again, it worked. The simplest code in fact, was to reuse  my increase and decrease functions, with the syntax you suggested. It worked with a call like this : increase(`repeating_${stat}specs_${stat}spec`, maxspec); Nice work! Glad you got it working :)