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 .
Advertisement Create a free account Compendium in Game, Join Today

[Sheet author tools] getSetAttrs, or: an alternative API for getAttrs/SetAttrs

1586647373
Jakob
Pro
Sheet Author
API Scripter
Have you ever been annoyed by the fact that getAttrs and setAttrs are asynchronous, but you really don't want to deal with that? Maybe you tried Promises, but they don't work with sheet workers? Well, I can't solve those problems, but I was annoyed by the API of getAttrs and setAttrs, so I made a new one. Since it does not support getSectionIds (yet?), it's not a complete replacement, but maybe someone finds it useful. You can paste this at the start of your sheet workers. class AttributeSetter { constructor(attrs) { this._sourceAttrs = attrs; this._targetAttrs = {}; } setAttr(name, value) { this._sourceAttrs[name] = value; this._targetAttrs[name] = value; } setAttrs(values) { for (let key in values) { this.setAttr(key, values[key]); } } getAttr(name) { return this._sourceAttrs[name]; } finalize(callback) { getAttrs(Object.keys(this._targetAttrs), (values) => { const finalAttrs = {}; for (let key in this._targetAttrs) { if (String(this._targetAttrs[key]) !== values[key]) finalAttrs[key] = String(this._targetAttrs[key]); } setAttrs(finalAttrs, { silent: true }, callback); }); } } const attributeHandler = { get: function (target, name) { return target.getAttr(name); }, set: function (target, name, value) { target.setAttr(name, value); return true; } }; function getSetAttrs(attrs, callback, finalCallback) { getAttrs(attrs, (values) => { const setter = new AttributeSetter(values); callback(new Proxy(setter, attributeHandler), setter); setter.finalize(finalCallback); }); } From then on, you can use the magic getSetAttrs function and a single callback to treat attributes like they are object properties (again, as long as you don't need repeating sections). How does it work? Your callback function (which must be synchronous, so don't use any asynchronous API functions!) will be called with one or two arguments, let's call them attrs  and setter . The first of these, attrs , allows you to set and get attributes like they are object properties. E.g., you can use the following: getSetAttrs(["foo", "bar"], function(attrs) { attrs["baz"] = attrs["foo"] + attrs["bar"]; }); And that's it! The attribute will be set correctly, and if you use it in a follow-up calculation, it will work as expected. The second argument (optional) setter  allows you to set attributes in bulk by calling setter.setAttrs() : getSetAttrs(["foo", "bar"], function(attrs, setter) { setter.setAttrs({ "baz": attrs["foo"] + attrs["bar"], "another_attributes": 23 * (parseInt(attrs["foo"]) || 0) }); }); There is some other magic in place behind the scenes: All the attributes will be set in one operation, which is both faster and less confusing than asynchronous calls to setAttrs spread throughout the code. All attribute values are coerced to String before being set. There are some strange edge cases avoided by doing that (believe me). I personally find it more pleasant to set attributes using object notation. Attributes are not set if they are equal to the values already on the sheet. This prevents you from needlessly creating attributes that clutter the Attributes & Abilities tab. Attributes are always set using 'silent' mode, which prevents further registered callbacks from triggering apart from the explicit finalCallback  argument to getSetAttrs (though you could change that). There is no longer any need to gather everything in one object for a final setAttrs call (depending if you like that, this may be a plus or a minus :D). Any (synchronous) modifications in the callback will be collected and applied at the end, so any way of modifying it (one big object, multiple property assignments, passing setter  or attrs on to subfunctions) is maximally efficient. Maybe someone can use this, I was getting annoyed of multiple levels callbacks, so I wrote this.
1586683985
GiGs
Pro
Sheet Author
API Scripter
Very intriguing. It's nice to see you're still around, too, Jakob. 
1586722437
∇ince
Pro
Sheet Author
@Jakob +1
1587816258
ᐰndreas J.
Forum Champion
Sheet Author
Translator
Return of The Jakob :D