Separate Outputs

The basic principle is simple.

  1. watch for changes (the event line)
  2. Build the attribute names (the push loop, building the fields array)
  3. Grab the attribute values from the sheet (getAttrs)
  4. Create a variable to hold all the attributes to save (output), and initialise any other variables needed.
  5. Loop (again) through the section rows (ids). A second loop avoids the need to put getAttrs and setAttrs inside a loop, which should be avoided where possible.
  6. In each loop, add the values to output
  7. Finally save the values to the sheet (setAttrs).

This looks like a lot of steps, but its a basic framework you use in pretty much all repeating section sheet workers. Only step 6 really varies from worker to work, and for simple additions is almost identical. You can copy the basic procedure over and over.

on('change:repeating_gear:weight change:repeating_gear:cost', () => {
  getSectionIDs('repeating_gear', ids => {
     const fields = [];
     ids.forEach(id => fields.push(section_name('gear', id, 'weight'), section_name('gear', id, 'cost')));
     getAttrs(fields, values => {
        const output = {};
        output.gear_weight = 0;
        output.gear_cost = 0;
        ids.forEach(id => {
            output.gear_weight += num(v[section_name('gear', id, 'weight')]);
            output.gear_cost += num(v[section_name('gear', id, 'cost')]);
        });
        setAttrs(output);
     });
  });
});
on('change:repeating_weapons:weight change:repeating_weapons:cost', () => {
  getSectionIDs('repeating_weapons', ids => {
     const fields = [];
     ids.forEach(id => fields.push(section_name('weapons', id, 'weight'), section_name('weapons', id, 'cost')));
     getAttrs(fields, values => {
        const output = {};
        output.weapons_weight = 0;
        output.weapons_cost = 0;
        ids.forEach(id => {
            output.weapons_weight += num(v[section_name('weapons', id, 'weight')]);
            output.weapons_cost += num(v[section_name('weapons', id, 'cost')]);
        });
        setAttrs(output);
     });
  });
});
on('change:weapons_weight change:gear_weight', () => {
   getAttrs(['weapons_weight', 'gear_weight'], values => {
      const weapons = num(v.weapons_weight);
      const gear = num(v.gear_weight);
      const total = weapons + gear;
      setAttrs({
         total_weight: total
      });
    });
});
on('change:gear_weight change:gear_cost', () => {
   getAttrs(['gear_weight', 'gear_cost'], values => {
      const weapons = num(v.gear_weight);
      const gear = num(v.gear_cost);
      const total = weapons + gear;
      setAttrs({
         total_weight: total
      });
    });
});Code language: JavaScript (javascript)

In this method, we have each section calculate the totals for that section. Then extra workers watch those totals, and when they change a new total for both sections is updated. This is not an elegant solution, but works perfectly well.

It looks complex, but we have done every step before, and indvidually each step is pretty simple.

Universal Sheet Workers

There’s a lot of duplication here. We can make a more streamlined version, using arrays of names and forEach loops. I have written up this approach on the wiki, where I call it Universal Sheet Workers.

const sections =['weapons', 'gear'];
sections.forEach(section => {
   on(`change:repeating_${section}:weight change:repeating_${section}:cost`, () => {
     getSectionIDs(`repeating_${section}`, ids => {
        const fields = [];
        ids.forEach(id => fields.push(section_name(section, id, 'weight'), section_name(section, id, 'cost')));
        getAttrs(fields, values => {
           const output = {};
           output[`${section}_weight`] = 0;
           output[`${section}_cost`] = 0;
           ids.forEach(id => {
              output[`${section}_weight`] += num(v[section_name(section, id, 'weight')]);
              output[`${section}_cost`] += num(v[section_name(section, id, 'cost')]);
           });
           setAttrs(output);
        });
      });
   });
});

const totals = ['weight', 'cost'];
totals.forEach(total => {
   on(`change:weapons_${total} change:gear_${total}', () => {
      getAttrs(['weapons_${total}', 'gear_${total}'], values => {
         const weapons = num(v[`weapons_${total}`]);
         const gear = num(v[`gear_${total}`]);
         const total = weapons + gear;
         setAttrs({
            total_weight: total
         });
      });
   });
});
Code language: JavaScript (javascript)

Summary

In this method, you create a global attribute for each thing you want to track in each repeating section. If you have 2 values to total up in each section and 2 sections, that needs four global attributes.

Then you have sheet workers to modify those global attributes, and when they change, build a total of the needed global attributes. While the sections are asynchronous and cant easily be tracked, the global attributes are not and you can manipulate tem just like any other attribute.

This does lead to more attributes (oftem hidden inputs) being created just to handle the sections. We’ll see in later posts how to avoid that.

Leave a Reply

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