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 .
×
Create a free account

Best way to sheet worker a text string display thingy

1594700550

Edited 1594700623
Hello, Working on a sheet and am very fresh at HTML/CSS so its hard for me to navigate some of the more complicated sheets that some of you have built wonderfully. What I am trying to do is have a couple 'selects' and a 'number input' that would change a text string above it. I would like the 'Agility' to be static but next to it (currently a disabled input box) to change based on the three inputs below it.  Right now I have a sheet worker that changes the 10 based on the 'Base Die', but would like a text string that looks more like the Agility (willing to get rid of the disabled input box if there is a better way!) and adjusts to all three items below.  So it would look like ' Agility D10 + D10 ' in its current state, or ' Agility D10 + D10 + 1 ' if I put a 1 in the 'modifier' box below.  Or if I put a blank in Extra die it would be ' Agility D10 '  I'd prefer it dropping off the blanks instead of like a 'Agility D10 + 0 + 0', if possible. I was messing with sheet workers but it seemed to not like letters and only dealt with numbers.  (the blue checkbox hides the three boxes below). Now a potential hiccup, is that the drop downs say d2/d4/.../d12 etc, but it creates a corresponding 2/4/.../12 for the actual attribute, this is necessary for the stupid macro on the dice roll. Thank you for anyone who can lead me in the right direction!
1594710104

Edited 1594724387
GiGs
Pro
Sheet Author
API Scripter
It would help to see the sheet worker you have, and the html for the section above, or at least the actual attribute names. I cant see where you're going wrong without those. But sheet workers are fine with either letters or numbers. One thing I'm noticing is that disabled agility text box isnt big enough to hold all the text you want to put in it. Secondly, sheet workers cant change disabled inputs so you absolutely must  change that from a disabled input to a readonly input. So for the sake of this example, lets assume those attributes are called agility_base , agility_extra , and agility_modifier . lets also assume the disabled input you change to a readonly is called agility_text . Here's a sheet worker that should do the trick: on('change:agility_base change:agility_extra change:agility_modifier', function(){     getAttrs(['agility_base', 'agility_extra', 'agility_modifier'], function(v){         const base = v.agility_base;         const extra = parseInt(v.agility_extra) || 0;         const mod = parseInt(v.agility_modifier) || 0;         const final = 'd' + base +              (extra ? '+d' + extra: '') +              (mod ? (mod >= 0 ? '+' : '') + mod : '' );         setAttrs({             agility_text: final          });     });  }); How it works: if you're not familiar with const, it's just like var, but only for variables you cannot change once it is declared. Since none of the variables here get changed after they are created, it's efficient to use it. I've assumed both dice are just showing numbers, so d8 will return 8 . That means we have to add the d  to the string ourselves. So, lets look at the complicated section, starting const final . In javascript, you can use + to join strings together. so for example, const final = 'd' + '8' would give a result of d8 . Thats what the first line does. The second line is my favourite javascript operator, the ternary operate. This is a compact if statement, which works like this: var something = (if statement) ? (value if true) : (value if false) in this case we have (extra ? '+d' + extra: '') The bit before the question mark is an if statement - it is basically  if (extra) . In javascript, values of ''  or 0 , for example, are falsy values. That means when you do an if statement on them, they return false. Most other values are truthy , which means they return true.    In this example, I'm assuming the extra die   can have a value of either '' or 0 . In that case the "value if false" section is used, after the colon: that is ''. Otherwise, it adds the die value to the string.  The last line is very similar: (mod ? (mod >= 0 ? '+' : '') + mod : '' ); It's a ternary operator with another ternary operator inside it. So it first checks if mod have a truthy value, and if so, adds it to the die string. But there's a complication here: if the modifier is negative, it works fine. If its positive, like 2 , you need to add a +  before it. Thats what the middle ternary operator does.  So, this should give you a text string like d10 , or d10+d6 , or d10+3 , or d10+d8-2 , whichever is appropriate.
1594719744

Edited 1594724369
GiGs
Pro
Sheet Author
API Scripter
Here's that sheet worker rewritten without the ternary operators. The structure might be easier to understand: on('change:agility_base change:agility_extra change:agility_modifier', function(){     getAttrs(['agility_base', 'agility_extra', 'agility_modifier'], function(v){         const base = v.agility_base;         const extra = parseInt(v.agility_extra) || 0;         const mod = parseInt(v.agility_modifier) ||0;         let final = 'd' + base;         if(extra) {             final += '+d' + extra;         }          if(mod) {             if(mod >= 0) {                 final += '+';             }             final += mod;         }         setAttrs({             agility_text: final          });     });  });
1594719838

Edited 1594720965
Finderski
Pro
Sheet Author
Compendium Curator
You could do this with just CSS, too. I'd need to see the HTML but essentially, you'd have a series of spans and named spans that would show/hide based on the values of the drop down boxes. HTML: <input type="hidden" name="attr_extraDie" class="sheet-extraDieType"> <input type="hidden" name="attr_modifier" class="sheet-showModifier"> Agility <span name="attr_baseDie"></span><span class="sheet-showExtraDieType"> + <span name="attr_extraDie"></span></span><span class="sheet-showModifier"> + <span name="attr_modifier"></span></span><br> Base Die: <select name='attr_baseDie'> <option value="d4" selected>d4</option> <option value="d6">d6</option> <option value="d8">d8</option> <option value="d10">d10</option> <option value="d12">d12</option> <option value="d20">d20</option> </select> Extra Die: <select name='attr_extraDie'> <option value="0" selected></option> <option value="d4">d4</option> <option value="d6">d6</option> <option value="d8">d8</option> <option value="d10">d10</option> <option value="d12">d12</option> <option value="d20">d20</option> </select><br> Modifier: <input type='number' name="attr_modifier" value="0"> CSS: .sheet-showExtraDieType, .sheet-showModifier { display: none; } .sheet-extraDieType:not([value="0"]) ~ .sheet-showExtraDieType, .sheet-showModifier:not([value="0"]) ~ .sheet-showModifier { display: inline; }
1594721268

Edited 1594724346
GiGs
Pro
Sheet Author
API Scripter
And finally, because you likely have a bunch of separate stats, here's a way to handle them all with one sheet worker.  const stats = ['agility', 'strength', 'cunning']; // change these to match your stat names. stats.forEach(stat => {     on(`change:${stat}_base change:${stat}_extra change:${stat}_modifier`, function(){         getAttrs([`${stat}_base`, `${stat}_extra`, `${stat}_modifier`], function(v){             const base = v[`${stat}_base`];             const extra = parseInt(v[`${stat}_extra`]) || 0;             const mod = parseInt(v[`${stat}_modifier`]) || 0;             const final = `d${base}${extra ? `+d${extra}`: ''}${mod ? `${mod >= 0 ? '+' : ''}${mod}` : '' }`;             setAttrs({                 [`${stat}_text`]: final              });         });      }); }); This assumes you have named each stat and the various other attributes in a consistent manner: for example: agility_base, agility_extra, agility_modifier, agility_text . And each other stat would have its name followed by _base, _extra, _modifier , and _text . You just need to change those endings wherever they appear above to match your attributes.  The const final line here does the same as the previous version but uses less familiar syntax.
1594721465
GiGs
Pro
Sheet Author
API Scripter
Finderski said: You could do this with just CSS, too. I'd need to see the HTML but essentially, you'd have a series of spans and named spans that would show/hide based on the values of the drop down boxes. I prefer sheet worker solutions   for EVERYTHING , but that's a clever way to handle it too.
1594721984
Andreas J.
Forum Champion
Sheet Author
Translator
GiGs said: I prefer sheet worker solutions   for EVERYTHING , but that's a clever way to handle it too. I'm not surprised by this whatsoever , with your expertise on creating & working on them...
Thank you all!   I saw an example of that multiple attribute sheet worker and I couldnt quite get, so I will try again.  The 'd' +  was my undoing for trying to get text with the numbers i think.   I will try the sheetworker first and hopefully get it to work!
1594724507
GiGs
Pro
Sheet Author
API Scripter
Jake M. said: Thank you all!   I saw an example of that multiple attribute sheet worker and I couldnt quite get, so I will try again.  The 'd' +  was my undoing for trying to get text with the numbers i think.   I will try the sheetworker first and hopefully get it to work! I just realised the extra line is missing something - I've corrected it in the code posts above. If your 0 entry was a 0 and not "", it wouldnt work properly to hide that die. But by using parseInt to turn it into a number (not a string), it works properly. Andreas J. said: GiGs said: I prefer sheet worker solutions   for EVERYTHING , but that's a clever way to handle it too. I'm not surprised by this whatsoever , with your expertise on creating & working on them... hehe, I figured it wasnt a secret ;)
This worked like a charm!  All six of my attributes worked after updating it accordingly. Eventually I will want to do something similar to like 24 unique repeating sections, is that something that will be doable?  I am not close to that part yet, but imagine it might take 24 chunks of code similar to below but coded for specific repeating sections? GiGs said: And finally, because you likely have a bunch of separate stats, here's a way to handle them all with one sheet worker.  const stats = ['agility', 'strength', 'cunning']; // change these to match your stat names. stats.forEach(stat => {     on(`change:${stat}_base change:${stat}_extra change:${stat}_modifier`, function(){         getAttrs([`${stat}_base`, `${stat}_extra`, `${stat}_modifier`], function(v){             const base = v[`${stat}_base`];             const extra = parseInt(v[`${stat}_extra`]) || 0;             const mod = parseInt(v[`${stat}_modifier`]) || 0;             const final = `d${base}${extra ? `+d${extra}`: ''}${mod ? `${mod >= 0 ? '+' : ''}${mod}` : '' }`;             setAttrs({                 [`${stat}_text`]: final              });         });      }); }); This assumes you have named each stat and the various other attributes in a consistent manner: for example: agility_base, agility_extra, agility_modifier, agility_text . And each other stat would have its name followed by _base, _extra, _modifier , and _text . You just need to change those endings wherever they appear above to match your attributes.  The const final line here does the same as the previous version but uses less familiar syntax.
1594865064
GiGs
Pro
Sheet Author
API Scripter
It should be possible. It depends if the repeating sections are structured similarly enough. What are they for, and what will your sheet workers be doing with them?
They will structured extremely similar to the atttributes Basically there are 24 skills, each skill can get a specialty (this would be the repeating section): The skills I would just follow your sheet worker and adjust for the skills and maybe have 1 additional thing to work with skill_text skill_base skill_base2 skill_extra skill_mod For the specialties it would be similar to the skills specialty_text, _base, _base2, _extra, _mod but those would be repeating under each skill.
1594868963
GiGs
Pro
Sheet Author
API Scripter
The repeating sections are affected only from within the same row of the repeating section, you can use almost identical code for them. If they are affected by the skill they are specialties of, or changes in other stat values, they'll need their own worker - but you'll still be able to use one forEach loop for all of them. It's hard to give code examples without more details of how they work, but it is do-able.
I will definitely post again to the forum when I get to that section! Got to learn some fundamentals on the layout portion now with the CSS grid stuff, but making progress. Thank you for your help so far!
1595104719

Edited 1595104769
Darn I was trying to be clever but kept breaking stuff. I will have 3 more items that will be derived off of the stats below.  Initiative, Endurance, and Resistance.  I was trying to emulate your earlier example (the one without the ${stat} dynamics) to update those accordingly. Basically, I am trying to get these derived attributes to update if a feeder attribute changes like: Agility_base, _extra, _mod or Alertness_base, _extra, _mod or Initiative_extra, _mod Basically, I would like the initiative_base (gray box above) to pull the agility_text + alertness_text.  Then the blue box would be initiative_base + _extra + _mod (which I would append the larger sheet worker GiGs provided to include initiative).  I think my issue (besides not understanding anything) is that I am probably not taking the quickest route there.  I feel like it is probably easier if I could call 'agility_text' and 'alertness_text' instead of using the smaller pieces.  Doing the smaller pieces, it just wouldn't work :( GiGs said: And finally, because you likely have a bunch of separate stats, here's a way to handle them all with one sheet worker.  const stats = ['agility', 'strength', 'cunning']; // change these to match your stat names. stats.forEach(stat => {     on(`change:${stat}_base change:${stat}_extra change:${stat}_modifier`, function(){         getAttrs([`${stat}_base`, `${stat}_extra`, `${stat}_modifier`], function(v){             const base = v[`${stat}_base`];             const extra = parseInt(v[`${stat}_extra`]) || 0;             const mod = parseInt(v[`${stat}_modifier`]) || 0;             const final = `d${base}${extra ? `+d${extra}`: ''}${mod ? `${mod >= 0 ? '+' : ''}${mod}` : '' }`;             setAttrs({                 [`${stat}_text`]: final              });         });      }); }); This assumes you have named each stat and the various other attributes in a consistent manner: for example: agility_base, agility_extra, agility_modifier, agility_text . And each other stat would have its name followed by _base, _extra, _modifier , and _text . You just need to change those endings wherever they appear above to match your attributes.  The const final line here does the same as the previous version but uses less familiar syntax.
1595117524

Edited 1595117554
GiGs
Pro
Sheet Author
API Scripter
Are any of the stats you are trying to use disabled, or have disabled="true" in their inputs? It's much  easier to help if you post the code you've been trying.
So the first one works perfectly fine.  It generates the 6 attributes and kicks out the ${stat}_text.  The second block is my attempt to get the initiative_base to be the agility_text and alertness_text combined. The third block is what I have been butchering left and right trying to figure it out, basically to emulate the first block but specific for the initiative chunk.  I think they are all not playing well with each other and its most likely due to some error that I am causing from using something twice. const stats = ['agility', 'strength', 'vitality', 'alertness', 'intelligence','willpower']; stats.forEach(stat => { on(`change:${stat}_base change:${stat}_extra change:${stat}_mod`, function(){ getAttrs([`${stat}_base`, `${stat}_extra`, `${stat}_mod`], function(v){ const base = v[`${stat}_base`]; const extra = parseInt(v[`${stat}_extra`]) || 0; const mod = parseInt(v[`${stat}_mod`]) || 0; const final = `d${base}${extra ? `+d${extra}`: ''}${mod ? `${mod >= 0 ? '+' : ''}${mod}` : '' }`; setAttrs({ [`${stat}_text`]: final }); }); }); }); on('change:agility_base change:agility_extra change:agility_mod change:alertness_base change:alertness_extra change:alertness_mod change:initiative_extra change:initiative_mod', function(){ getAttrs(['agility_text', 'alertness_text'], function(v){ const agi = v.agility_text; const alertnes = v.alertness_text; let finalinibase = agi + '+' + alertnes setAttrs({ initiative_base: finalinibase }); }); }); on('change:agility_base change:agility_extra change:agility_mod change:alertness_base change:alertness_extra change:alertness_mod change:initiative_extra change:initiative_mod', function(){ getAttrs(['initiative_text', 'initiative_extra','initiative_mod'], function(v){ const initext = parseInt(v.initiative_text) || 0; const iniextra = parseInt(v.initiative_extra) || 0; const inimod = parseInt(v.initiative_mod) || 0; let finalinitext = initext + '+' + iniextra '+' + inimod setAttrs({ initiative_text: finalinitext }); }); });
1595123165
GiGs
Pro
Sheet Author
API Scripter
The main problem would seem to be this line in the last worker: const initext = parseInt(v.initiative_text) || 0; You are calling initiative_text, but I think this should be initiative_base. Likewise in the getAttrs, you are getting the valie of intiative_text, but initiative_base is nowhere to be found. I think you can delete that last worker anyway, and just add initiative to the array at the start: const stats = ['agility', 'strength', 'vitality', 'alertness', 'intelligence','willpower', 'initiative']; Initiative is calculated the same way as all the others, with  base, extra and mod suffix, so the same worker method should work. Then the worker that calculates initiative_base uses agility_text  and alertness_text, so you should only have those in the change and getAttrs lines on('change:agility_text change:alertness_text', function(){ getAttrs(['agility_text', 'alertness_text'], function(v){ const agi = v.agility_text; const alert = v.alertness_text; let finalinibase = agi + '+' + alert setAttrs({ initiative_base: finalinibase }); }); }); In this way you create a cascading effect. For example: Agility mod changes => agility_text is calculated => agility_text is updated => that triggers the initiative macro => initiative base is calculated => that triggers the initiative calculation and initiative_text is calculated. You have to get the on(change) lines set up correctly so these events fire in the way you want them. Which is why you strip out everything in the second worker to just the agility and alertness text attributes. Since there is crossover developing here (agaility + alertness both affecting initiative) it would be more efficient to combine these into a single larger worker, and that would allow putting the agility and alertness dice together in a better way when making the initiative base. But such workers are complicated to build, and this cascading method works fine for smaller sheets without hundreds of attributes.
Thank you for your repeated assistance. I made a few failed attempts to adapt what you told me and I got an issue. Currently i have it working nearly perfectly by just having what you suggested.  The only hiccup is that the initiative_text has a 'd' at the beginning, so it creates a 'ddX + dX' (see below in the blue) I tried basically running a separate line with the corresponding attributes.  I imagine it is a limitation I am not familiar with.  But I am trying to get rid of that first d shown above in blue. const stats = ['agility', 'strength', 'vitality', 'alertness', 'intelligence','willpower','initiative']; stats.forEach(stat => { on(`change:${stat}_base change:${stat}_extra change:${stat}_mod`, function(){ getAttrs([`${stat}_base`, `${stat}_extra`, `${stat}_mod`], function(v){ const base = v[`${stat}_base`]; const extra = parseInt(v[`${stat}_extra`]) || 0; const mod = parseInt(v[`${stat}_mod`]) || 0; const final = `d${base}${extra ? `+d${extra}`: ''}${mod ? `${mod >= 0 ? '+' : ''}${mod}` : '' }`; const derfinal = `${base}${extra ? `+d${extra}`: ''}${mod ? `${mod >= 0 ? '+' : ''}${mod}` : '' }`;             setAttrs({ [`${stat}_text`]: final [`${stat}_dertext`]: derfinal }); }); }); });
1595656258
GiGs
Pro
Sheet Author
API Scripter
I'll have a look and see where that d is coming from later, if I can, but the code above has an error. When you have multiple things in setAttrs, you need to separate them with a comma, like so: setAttrs({ [`${stat}_text`]: final, [`${stat}_dertext`]: derfinal });
1595656266
GiGs
Pro
Sheet Author
API Scripter
I'll have a look and see where that d is coming from later, if I can, but the code above has an error. When you have multiple things in setAttrs, you need to separate them with a comma, like so: setAttrs({ [`${stat}_text`]: final, [`${stat}_dertext`]: derfinal });
1595657454
GiGs
Pro
Sheet Author
API Scripter
I see the issue. Its because initiative is a calculated stat that already has a d at the start, whereas the others are all numbers. So you need to check if the stat already starts with a d. Or check if the stat is initiative. Try this: const stats = ['agility', 'strength', 'vitality', 'alertness', 'intelligence','willpower','initiative']; stats.forEach(stat => { on(`change:${stat}_base change:${stat}_extra change:${stat}_mod`, function(){ getAttrs([`${stat}_base`, `${stat}_extra`, `${stat}_mod`], function(v){ const base = v[`${stat}_base`]; const extra = parseInt(v[`${stat}_extra`]) || 0; const mod = parseInt(v[`${stat}_mod`]) || 0; const final = `${stat === 'initiative' ? '' : 'd'}${base}${extra ? `+d${extra}`: ''}${mod ? `${mod >= 0 ? '+' : ''}${mod}` : '' }`;             setAttrs({ [`${stat}_text`]: final }); }); }); });
Hahahaha! It was the comma!  Thank you so much.  Worked like a charm.  I have 3 of these derived stats (ini, endu, resist), so I tried it the first way and it worked.  Im hoping by the end of this thing I will be able to vaguely understand all the bits and pieces.  Thank you again. const stats = ['agility', 'strength', 'vitality', 'alertness', 'intelligence','willpower','initiative','endurance','resistance']; stats.forEach(stat => { on(`change:${stat}_base change:${stat}_extra change:${stat}_mod`, function(){ getAttrs([`${stat}_base`, `${stat}_extra`, `${stat}_mod`], function(v){ const base = v[`${stat}_base`]; const extra = parseInt(v[`${stat}_extra`]) || 0; const mod = parseInt(v[`${stat}_mod`]) || 0; const final = `d${base}${extra ? `+d${extra}`: ''}${mod ? `${mod >= 0 ? '+' : ''}${mod}` : '' }`; const derfinal = `${base}${extra ? `+d${extra}`: ''}${mod ? `${mod >= 0 ? '+' : ''}${mod}` : '' }`; setAttrs({ [`${stat}_text`]: final, [`${stat}_dertext`]: derfinal }); }); }); }); GiGs said: I'll have a look and see where that d is coming from later, if I can, but the code above has an error. When you have multiple things in setAttrs, you need to separate them with a comma, like so: setAttrs({ [`${stat}_text`]: final, [`${stat}_dertext`]: derfinal });