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

How to loop through a Repeating Section

1562434992
Mike W.
Pro
Sheet Author
I am not a programmer nor am I a coder and do not even know if I am asking this with the correct terminology. I am in need of code to ‘Loop’ through a Repeating Section that will execute some code for each row for the HTML portion of the sheet. I am helping to maintain the GURPS character sheet and have made some enhancements for the next PULL. Name of the Repeating Section is: repeating_item The code I needed to execute for this is: setAttrs({ repeating_item_weighttotal: Number(Math.round(item.repeating_item_count * item.repeating_item_weight * item.repeating_item_carried * 1000) / 1000).toFixed(3), repeating_item_costtotal: Number(Math.round(item.repeating_item_count * item.repeating_item_cost * 100) / 100).toFixed(2) }); I will be placing this in the: on('sheet:opened', function() – section of code. I have looked through the forum here and even researched online but there does not seem to be an easy way to do this and what example code I have found is way over my head.  Any help here would be greatly appreciated.
1562436793

Edited 1562437597
GiGs
Pro
Sheet Author
API Scripter
This is definitely achievable. One issue you will have is you are creating the total inside the repeating section. If you are totalling the sections weight and cost, you';ll need those two attributes outside the repeating section. So, lets you create two attributes.  total_item_cost and  total_item_weight . You could use the repeatingSum function. Place that in the script block, then create this sheet worker: on('change:repeating_item remove:repeating_item', function() { repeatingSum("total_item_cost", "item", ["count", "cost"]); repeatingSum("total_item_weight", "item", ["count", "weight","carried"]); }); the first line will look in a repeating section called repeating_item, then on each row, look for count and cost and multiply them together, then store the total of all rows in an attribute called total_item_cost. The next line will do the same, but multiply together count, weight, and carried. You have some rounding to create a specific set of decimal numbers. I should add that feature to the script, but in the meantime, if its important, you could use thee attribute totals created above as intermediate attributes, and create a second pair where the rounding is done. I will be placing this in the: on('sheet:opened', function() – section of code. You dont want to do that. There's no need, since sheet workers are capable of updating the totals on-the-fly as items within the section are changed. I seem to remember something about  sheet:opened  and repeating sections not working together well, but I may be misremembering.
1562446372
Mike W.
Pro
Sheet Author
I am doing the on('sheet:opened" to cover all existing items. Otherwise the user has to change them annually one at a time. This way all existing items in inventory will be updated to the new format. Here is an example: These are existing inventory items where I want the format changes. Noticed the grayed columns where to totals are kept, the rounding formula drops the trailing zeros. I want to keep them so I change the formula. Unfortunately the only way for that to take effect for existing items  is to rerun the formula. Thus the user must manually check the 'On' box on/off or off/om, then the new format is applied. I want to do this automatically for the user so when the sheet opens, it runs the formal thus applying the new format. Make sense?
1562446999
GiGs
Pro
Sheet Author
API Scripter
I'm not completely following: Are you saying you have a single on(sheet:opened) worker that updates a whole bunch of stuff across the sheet, and also have separate sheet workers that update those things? If so, you definitely shouldnt be doing it that way. It might be helpful to see your sheet:opened  worker. These are existing inventory items where I want the format changes. Noticed the grayed columns where to totals are kept, the rounding formula drops the trailing zeros. I want to keep them so I change the formula . Unfortunately the only way for that to take effect for  existing items  is to rerun the formula. Thus the user must manually check the 'On' box on/off or off/om, then the new format is applied. I want to do this automatically for the user so when the sheet opens, it runs the formal thus applying the new format. I dont know what you mean by the bit I've bolded above. Are you saying you have a format you can apply to this section, that the user can switch somewhere else in the sheet? If so, yes, you should be able to update the existing values in place, and do it live without needing to open or close the sheet. Seeing the code that does the format change, and what the formats are would be helpful. By the way, those greyed out attributes might  be needed or might not (I can't say without seeing exactly how its handled), but if needed can be made hidden - the user doesnt need to see them. It's safe to say I'm not completely following the process here :) but it seems to me that there's room for streamlining and optimising.
1562453655

Edited 1562528036
GiGs
Pro
Sheet Author
API Scripter
I skimmed the character sheet on github, and have a better idea of what you're after. I assumed earlier you wanted to total up the repeating section, but you just want something to update fields within the repeating section. So ignore my repeatingSum suggestion. You already have this code on('change:repeating_item:count change:repeating_item:cost change:repeating_item:carried change:repeating_item:weight', function (e) { getAttrs(['repeating_item_count', 'repeating_item_cost', 'repeating_item_weight', 'repeating_item_carried'], function (item) { setAttrs({ repeating_item_weighttotal: Math.round(item.repeating_item_count * item.repeating_item_weight * item.repeating_item_carried * 1000) / 1000, repeating_item_costtotal: Math.round(item.repeating_item_count * item.repeating_item_cost * 100) / 100 }); }); }); With a tweak to this, and a second sheet worker to handle in-place mass changes to the second sheet, you can do what you need without needing to use sheet:opened.   I need to know about the attribute that handles the formatting. Is it a checkbox? what values does it have?  To illustrate how to change the above, let's say it is a checkbox named "inventory_fixed_digits" and has a value of 0 or 1 (0 = not fixed, 1 = fixed digits). on('change:repeating_item:count change:repeating_item:cost change:repeating_item:carried change:repeating_item:weight', function (e) { getAttrs(['repeating_item_count', 'repeating_item_cost', 'repeating_item_weight', 'repeating_item_carried', 'inventory_fixed_digits'], function (item) { const fixed = +item.inventory_fixed_digits || 0; let weight = (+item.repeating_item_count||0) * (+item.repeating_item_weight||0) * (+item.repeating_item_carried||0);         let cost = (+item.repeating_item_count||0) * (+item.repeating_item_cost||0); weight = fixed ? Number(Math.round(weight * 1000)/1000).toFixed(3) : Math.round(weight * 1000)/1000; cost = fixed ? Number(Math.round(cost * 100)/100).toFixed(2) : Math.round(cost * 100)/100; setAttrs({ repeating_item_weighttotal: weight, repeating_item_costtotal: cost }); }); }); Now whenever item changes, the weight or cost is calculated and takes into account the current format. So you just need a way to update all at once when the inventory_fixed_digits attribute is changed. on('change:inventory_fixed_digits',function(e) { // getSectionIDs gives an array of all the row ids. getSectionIDs('item', function (ids) { if (ids.length === 0) { return; } // need to get an array of all the attributes used in the repeating_items calculations, for getAttrs const fieldArray = []; ids.forEach(id => fieldArray.push( `repeating_item_${id}_count`, `repeating_item_${id}_cost`, `repeating_item_${id}_weight`, `repeating_item_${id}_carried` )); getAttrs(fieldArray.concat(['inventory_fixed_digits']), function (item) { const fixed = +item.inventory_fixed_digits; const items = {}; // initialise an object to hold the total weights and costs. ids.forEach(id => { // for each row of the table, calculate the total weight and cost let weight = (+item[`repeating_item_${id}_count`]||0) * (+item[`repeating_item_${id}_weight`]||0) * (+item[`repeating_item_${id}_carried`]||0); let cost = (+item[`repeating_item_${id}_count`]||0) * (+item[`repeating_item_${id}_cost`]||0); // save the totals, in the correct format (if fixed = 1 use the first forumila, if 0, the second) items[`repeating_item_${id}_weighttotal`] = fixed ? Number(Math.round(weight * 1000)/1000).toFixed(3) : Math.round(weight * 1000)/1000; items[`repeating_item_${id}_costtotal`] = fixed ? Number(Math.round(cost * 100)/100).toFixed(2) : Math.round(cost * 100)/100; }); setAttrs(items); }); }); }); Obviously I havent tested this, so there might be typos, but the process is sound. With this approach, the correct format is always used. Even if you dont use this approach, the last sheet worker shows you how to loop through a repeating section and make changes on each row, which was the original question. Note that I wrapped the numbers in the total formulas like this for a reason: (+item.repeating_item_count||0) * (+item.repeating_item_cost||0); If the cell on the sheet is empty, it can cause the calculation to fail and break the entire sheet worker till you enter a proper value. This way, each value is always treated as a number, so if one row has an empty cell (or even text but i assume your sheet doesnt allow that), the sheet worker still returns a value for the other rows or cells.
1562538593
Mike W.
Pro
Sheet Author
Worked perfectly. I am so grateful to you GiGs.