Simple Repeating Sections

In a previous post we talked about how easy it is to work with repeating sections. Now we’ll talk about how hard it is to work with repeating sections…

Let’s say you have a encumbrance list presented as a repeating section, and want to count up the amount carried. That means you have to extract the weight shown in every row of the section. We’ll make the section really simple (we’ll show more complex versions later).

<fieldset class="Repeating_gear">
    <span>Thing:</span>
    <input type="number" name="attr_weight" value="0">
</fieldset>
<span>Total Weight:</span>
<input type="number" name="attr_weight_total" value="0" readonly>Code language: HTML, XML (xml)

So, here is the code for a repeating section where each row has a weight attribute (‘repeating_gear_ID_weight’), and want to add every row’s weight and put the total in the weight_total attribute.

The getSectionIDs funtion will give us an array of row ids. We need to use that to construct am array of full attribute names. We can then pass that to getAttrs, which will grab the values from the character sheet.

Finally we can loop through those attributes, and add their values together.

on('change:repeating_gear', function() {
  getSectionIDs('repeating_gear', function(ids) {
    const gear_weights = [];
    ids.forEach(function(id) {
       gear_weights.push('repeating_gear_' + id + '_weight');
    });
    getAttrs(gear_weights, function(values) {
       let weight_total = 0;
       gear_weights.forEach(function(row) {
          const weight = +values[row] || 0;
          weight_total += weight;
       }); 
       setAttrs({
          weight_total
       });
    });
  });
});Code language: JavaScript (javascript)

There are a few things to comment on here. First here is the exact same worker, rewritten to take advantage of the fat arrow syntax and template literals.

on('change:repeating_gear', () => {
  getSectionIDs('repeating_gear', ids => {
    const gear_weights = [];
    ids.forEach(id => gear_weights.push(`repeating_gear_${id}_weight`));
    getAttrs(gear_weights, values => {
       let weight_total = 0;
       gear_weights.forEach(row => {
          const weight = +values[row] || 0;
          weight_total += weight;
       }); 
       setAttrs({
          weight_total
       });
    });
  });
});Code language: JavaScript (javascript)

Now I’ll go through the code, step by step.

The Event Line

on('change:repeating_gear', () => {Code language: JavaScript (javascript)

Notice how similar this is to normal change: statements. Now, I could have stated the specici attribute here with an extra colon, like this:

on('change:repeating_gear:number', () => {Code language: JavaScript (javascript)

I chose not to, because there is only a single attribute per row, but I’ll show that later.

getSectionIDs

  getSectionIDs('repeating_gear', ids => {
    const gear_weights = [];
    ids.forEach(id => gear_weights.push(`repeating_gear_${id}_weight`));
    getAttrs(gear_weights, values => {Code language: JavaScript (javascript)

getSectionIDs is an asynchronous function, so eveyrthing after it must be nested inside it.

Alos, the documentation says you can do either of these:

  getSectionIDs('repeating_gear', ids => {
  getSectionIDs('gear', ids => {Code language: JavaScript (javascript)

If you leave off the ‘repeating_’ part, it will be added automatically. In my experience, this is not the case. Always include the ‘repeating_ part, regardless of the documentation.

Not also that an array is built here of all the names we want to use in getAttrs. The sheet worker knowns nothing about your character sheet, you must pass all attributes you want to use. We want to use an attribute from every row of the repeating section, so we must pass all of those attributes to getAttrs.

This way, we get all of them, and thanks to getSectionIDs, we don’t need to know anything about the contents of the section. It automatically gives us a row id for every row that exists, and we then build the relevant attribute name. This is why its a loop (forEach in this case) – because we do this once per row.

The Worker Body

       let weight_total = 0;
       gear_weights.forEach(row => {
          const weight = +values[row] || 0;
          weight_total += weight;
       }); 
       setAttrs({
          weight_total
       });Code language: JavaScript (javascript)

Now we have all of the attributes and their values in the valuesobject, and we need to add them up. Here we use an extra loop, and inside the loop, we get the value of the attribute, add it to weight_total, and finally save that back to the character sheet.

Concluding the Worker

Now whenever any weight value is changed, the total weight is updated. Exactly what we want!

Complicating The Situation

Now, imagine we have an encumbrance section with several stats:

  • A weight per item
  • The number of items
  • Which container that weight is stored in

And we want to show the total weight, plus the weight in each container.

To add an extra step, we want to show the money spent. Lets say for convenience we only show silver piece values. So, we want to show the total value as well as the various weights.

You could easily use a similar set up for, say, keeping track of bffs, or which spells or weapons are active.

I’ll shiow how to do that and maybe more in next post, along with some coding improvements.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.