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 .
×

Unable to access values in a repeating section

I have a repeating section for skills in my character sheet and need to calculate the final value to be rolled for the skill by adding it's rating and an attribute.  Following code is intended to trigger a sheet worker which will update a hidden attribute with the value of the attribute to be used in a roll query. defining the repeating section: <span> <fieldset class="repeating_skill"> <input type="text" name="attr_skillname" value="Name">: <input type="number" name="attr_skillrating" value="0"> <br> <span class = "upd"> Attribute: &nbsp <select name = "attr_skill_atr_dropdown"> <option value = "agi">Agility</option> <option value = "body">Body</option> <option value = "cha">Charisma</option> <option value = "int">Intuition</option> <option value = "log">Logic</option> <option value = "mag">Magic</option> <option value = "rea">Reaction</option> <option value = "str">Strength</option> <option value = "wil">Willpower</option> </select> &nbsp <input type="hidden" name="attr_skill_atr" value="0"> <button type="roll" value="/roll 1d20 + @{Bluff}" name="roll_skill"></button> </span> <br> </fieldset> </span>  Script to set the values (sections are commented out as part of debugging.  As written, this is still throwing a "value is not defined" error when the getAttrs() function is called) on("change:repeating_skill:skill_atr_dropdown", function() { let atr_value = 0; getAttrs(["repeating_skill_skill_atr_dropdown"], function(val1) { console.log(values.repeating_skill_skill_atr_dropdown); /* switch(values.repeating_skill_skill_atr_dropdown) { case 'agi': getAttrs(["agil_tot"], function(val1) { atr_value = parseInt(val2.agil_tot); }); break; case 'body': getAttrs(["body_tot"], function(val1) { }); break; case 'cha': getAttrs(["cha_tot"], function(val1) { }); break; case 'int': getAttrs(["int_tot"], function(val1) { }); break; case 'log': getAttrs(["log_tot"], function(val1) { }); break; case 'mag': getAttrs(["magic_tot"], function(val1) { }); break; case 'rea': getAttrs(["reac_tot"], function(val1) { }); break; case 'str': getAttrs(["str_tot"], function(val1) { }); break; case 'wil': getAttrs(["will_tot"], function(val1) { }); break; } */ }); /* setAttrs( { Repeating_skills_skill_atr: atr_value }); */ });
1677377647

Edited 1677378890
Oosh
Sheet Author
API Scripter
You've got a bunch of problems in there, the first is that the parameter name you're supplying to the callback function is val1 , but you're trying to access it with values. As the error message accurately reports, you do not have a variable called values defined anywhere in that scope (the interpreter very rarely lies about such things). You can name the parameter anything you like, but the declared name in parentheses must be the same as the name you're using to call the values inside the function. Secondly you've got nested getAttrs which will slow the sheet down - you don't actually need the first one, as the event itself will contain the updated value of the attribute you're watching. Thirdly, your setAttrs must be within the getAttrs callback to be sure it executes after the getAttrs value is returned. These are asynchronous functions, the only way to guarantee the order of execution is to put your code inside the callback function (well.... there's proper asynchronous code, but that's another story). Finally, you'll make your life easier if you're systematic with your attribute names & values. Personally, I wouldn't be abbreviating them at all since any IDE has autocomplete for variable names, and it makes your code harder to read. Either way - you have difference there between 'agi' and 'agil_tot' and 'wil' and 'will_tot' which means you have to do things like write lengthy switch statements instead of a single template literal line. So, if you were to change your <select> element so the values all properly match the _tot attributes, you could do this: on('change:repeating_skill:skill_atr_dropdown', (event) => { // The event object from the change will contain the new value, no need for the first getAttrs const targetAttribute = `${event.newValue}_tot`; // Get the live value of the attribute getAttrs([targetAttribute], (attributeValues) => { // May as well throw a log in there to make sure it's grabbing the right attribute console.log(`${targetAttribute} value: ${attributeValues[targetAttribute]}`); setAttrs({ repeating_skills_skill_atr: attributeValues[targetAttribute] }); }); }); Again, you will need to change your <option> values in the <select> element to perfectly match the "xxx_tot" attribute prefixes for this approach to work - but that's a habit you should definitely get in to. Systematic and consistent attribute names and values will make these kinds of operations much easier to code. That targetAttribute assignment line alone saves 20+ lines of switch block code. I'd also generally avoid writing your event handler inline, and would instead declare the function in the top scope of the sheet and call it when registering the event. This way, you have an explicitly named function which can be called from other parts of the sheet if it's needed somewhere else. If you leave the code inline in the event handler, you can't access that logic from anywhere else: /** * Repeating section functions */ const updateRepeatingSkillAttribute = (event) => { const targetAttribute = `${event.newValue}_tot`; getAttrs([targetAttribute], (attributeValues) => { console.log(`${targetAttribute} value: ${attributeValues[targetAttribute]}`); setAttrs({ repeating_skills_skill_atr: attributeValues[targetAttribute] }); }); } /** * Register event handlers */ on('change:repeating_skill:skill_atr_dropdown', updateRepeatingSkillAttribute); An added bonus is you can keep your functions grouped in a logical way - essential for a sheetworker-heavy script if you want your code to be at all understandable when you come back to it many months later... A small note on that last line - when you provide a declared function to a callback in Javascript (as we're doing with updateRepeatingSkillAttribute ), the interpreter will implicitly pass all parameters to that function if you leave the parentheses off altogether. So the above is the same as either of these: // standard function expression on('change:repeating_skill:skill_atr_dropdown', function(event) { updateRepeatingSkillAttribute(event); }); // arrow function expression on('change:repeating_skill:skill_atr_dropdown', (event) => updateRepeatingSkillAttribute(event)); Edit - I've also used arrow function expressions rather than standard function expressions. Although it's not something you need to worry about at this stage, arrow functions do not change the event handler's scope, which can be very important when you're dealing with Classes and the 'this' keyword.
1677426299

Edited 1677426371
GiGs
Pro
Sheet Author
API Scripter
Great advice, Oosh - and a good example of how you can streamline some workers using eventInfo. I'd add there's a reason this wasn't working: getAttrs(["repeating_skill_skill_atr_dropdown"] Every row of a repeating section has a row id, like -34768vcghrteb (not an actual id), so the actual line fragment would be: getAttrs(["repeating_skill_-34768vcghrteb_skill_atr_dropdown"] There are times you don't need the row id, but getAttrs is not one of them - you must supply the row id and usually get it using the getSectonIDs function. But Oosh's script might let you avoid that entirely. (I can't remember if setAttrs needs the row Id (it might) - if so you can get that from eventInfo too.)
1677473972

Edited 1677512422
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
I would add that the situations where you don't need the row ID are so specific, that I'd just recommend not using the no ID syntax. Using that syntax will force you to write 2 versions of every script you make for a repeating section; one that uses the no ID syntax, and one that either grabs the repeating row info from the event or looks up all the section ids.