Sometimes you want a sheet worker to draw on multiple repeating sections. Say, you have a weapons section and an encumbrance section, and you want to add the weight of all items together without needing to add a weapon to both sections.
You immediately hit a problem. Let’s say you build your code like this:
on('change_repeating_weapons', () => {
getSectionIDs('repeating_weapons, ids => {
// some code to add up the weights and costs
});
on('change:repeating_gear', () => {
getSectionIDs('repeating_gear, ids => {
// some code to add up the weights and costs
});
});
Code language: JavaScript (javascript)
When you calculate the masses of either the weapons or the encumbrance sections, the other is ignored. Worse, when you change the mass of any item in one of the sections, the other sections total is erased.
This is because getSectionIDs is an asynchronous function. Remember how anything not nested inside an asynchronous function is ignored? So two different async functions built like this cannot modify the same attribute.
There are solutions:
- Have each repeating section add its total to a separate input outside the section, then add those inputs together. Those global inputs are not asynchronous so you can add them safely.
- Import the sumRepeating Function: I have written a function that makes this process a lot simpler.
- Nested repeated sections: you know the way getSectionIDs contains getAttrs which also contains setAttrs? You can do this nestiing with multiple repeating sections – just nesat them inside ecah other like a Russian Doll.
- Asynchronous Functions: Javascript has the concept of async functions, and using them you can simply chain them together as if normal functions. You could have getSectionIDs followed by getAttrs which is followed by setAttrs, instead of nesting them inside each other. Unfortunately, the version of JavaScript used in character sheets does not support async functions, so you can’t do that.
- Construct a custom function: you might avoid the nesting of repeating sections by building a custom function, then calling that function as you need it.. You can do this.
Since this is a big topic, I’ll split this into several posts. One for each approach. The rest of those post will describe the things common to all. You can refer back to this post when working through any method.
First, let’s build show the HTML and CSS that will be used by all approaches.
Repeating Section HTML and CSS
This is the code being used for the examples below. It uses the same method I’ve used in previous posts – a dv containing column titles, followed by the repeating section. The main dfferences here are that there is more than one repearing section, and some global attributes outside of the repeating section.
<div class="weapons">
<h4>Weapon</h4>
<h4>Weight</4>
<h4>Cost</h4>
</div>
<fieldset class="repeating_weapons">
<div class="weapons">
<input type="text" name="attr_name" value="" placeholder="NAME">
<input type="number" name="attr_weight" value="0">
<input type="number" name="attr_cost" value="0">
</div>
</fieldset>
<div class="gear">
<h4>Item</h4>
<h4>Weight</4>
<h4>Cost</h4>
</div>
<fieldset class="repeating_gear
<div class="gear">
<input type="text" name="attr_name" value="" placeholder="NAME">
<input type="number" name="attr_weight" value="0">
<input type="number" name="attr_cost" value="0">
</div>
</fieldset>
<div class="totals">
<h3>Weights</h3>
<span>Weapons Total</span>
<input type="number" name="attr_weapons_weight" value="0">
<span>Gear Total</span>
<input type="number" name="attr_gear_weight" value="0">
<span>Overall Total</span>
<input type="number" name="attr_total_weight" value="0">
</div>
<div class="totals">
<h3>Cost</h3>
<span>Weapons Total</span>
<input type="number" name="attr_weapons_cost" value="0">
<span>Gear Total</span>
<input type="number" name="attr_gear_cost" value="0">
<span>Overall Total</span>
<input type="number" name="attr_total_cost" value="0">
</div>
Code language: HTML, XML (xml)
The repeating sections don’t include all the attributes such a section would incude. I’ve included only what we need for this specific example- just image the rest are there (like actiual weapon attack and damage statistics).
The attributes in the repeating sections use the same names. But remember a full attribute name is made up of <section>_<row ID>_<the name shown above>. So all of these names are actually unique.
.weapons {
display: grid;
grid-template-columns: 100px repeat(2, 50px);
column-gap: 5px;
}
.gear {
display: grid;
grid-template-columns: 100px repeat(2, 50px);
column-gap: 5px;
}
.totals {
display: grid;
grid-template-columns: 100px 50px;
column-gap: 5px;
}
.totals <h3> {
column-grid: 1 / -1;
}
Code language: CSS (css)
Notice how often I use CSS Grid for layout. It is so useful. The weapon and gear blocks are identical here, but in a real example they would not be.
That column-grid command at the end means that H£ will take the full width – it wont be forced to fit into one of the columns.
So with the basic framework set up, we can turn to the Sheet Workers. First, though…
Asynchronous Lag
One downside of all but the last approaches here is that they use quite a lot of getAttrs and getSectionIDs calls. These are asynchronous functions and as such, can contribute to sheet lag.
The real problem with asynchronous functions is how they can cascade – change one attribute which then changes others, each of which then change others, and so on. For sections like these, there shouldn’t be many cascades happening, so this shouldn’t cause much if any lag.
Don’t go overboard in trying to cut down on asynchronous functions, a few (even a lot of) such calls won’t make your sheet unusable. But watch out for heavy cascades.
Helper Functions
Near the start of our script block we have these functions that are free to be used by any sheet workers.
<script type="text/worker">
const int = (score fallback = 0) => parseInt(score) || fallback;
const num = (score fallback = 0) => +score || fallback;
const section_name = (section, row, field) =>
`repeating_${section}_${row}_${field}`;
// end of Helper Functions. all of our sheet workers go after this.
</script>
Code language: JavaScript (javascript)
The Int and num functions coerce whatever we supply into a number (either an integer or floating point number). section_name takes three inputs and creates a full section attribute name.
A few such functions make our code easier to write, and maybe easier to read too.
On To The Methods
The next four posts describe how to solve the same basic problem – adding something across these two repeating sections – and show four different ways of approaching this problem.