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

[Help] Property names when referring to repeating rows from outside the fieldset

var calcbonus = function(skillname) { var lbonus = [0]; getSectionIDs("repeating_stddevice", function(id) { for (i=0; i<id.length; i++) { getAttrs(["repeating_stddevice_" + id[i] + "_sdevice","repeating_stddevice_" + id[i] + "_sactive"], function(v) { switch (skillname) { case "Ambush": if (v.sactive == "1" && v.sdevice == "StealthSuit_1") { lbonus.push(1); }  default: console.log("============> no active applicable device for " + skillname); } }); } setAttrs({sbonus: _.max(lbonus) }); console.log("sbonus value " + _.max(lbonus)); }); }; I have a repeating row fieldset which holds sbonus and sactive for each row. I am accessing the rows based upon a separate field change (skillname) that is outside the fieldset, and I want to check the repeating rows for values that apply to the skillname. My problem is I cannot identify the property names passed by the gettAttrs function as  the v object. I have tried v.sdevice and v["repeating_stddevice_" + id[i] + "_sdevice"] and they return as undefined. I am probably missing something obvious as I am new to javascript.
1498147599
chris b.
Pro
Sheet Author
API Scripter
can you post the html of your repeating_stdevice fieldset? that looks like it should work if you have : <fieldset class="repeating_stdevice"> <input name="attr_sdevice" ... > <input name="attr_sactive" .... > </fieldset> however sometimes if it is a default value,and it is either a new row, or say I just added sdevice and sactive recently, (but the repeating fieldset existed before and the user's data rows exsited before)  , then they come back as undefined, and I have to use defaults instead. or set them to defaults along with whatever else I'm doing.
<fieldset class="repeating_stddevice">             <input type="radio"  name="attr_sactive" value="0" checked="true"/>             <input type="radio"  name="attr_sactive" value="1"/>             <strong>Device </strong>             <select class="sheet-longBox" type="text" name="attr_sdevice" value="None" />         <option value="None">None</option>             <option value="StealthSuit_1">Ambush</option>         <option value="SpacecraftTech_2">Spacecraft Kit III</option>             </select> </fieldset> Thank you for your response Chris,here is the <fieldset> I am referring to. I thought the input and select lines would mean the variables have a default value. The sactive radio buttons are clunky, when I know more I want to make it a checkbox but for the moment I have two radio buttons to cycle between.
1498194665
chris b.
Pro
Sheet Author
API Scripter
to indicate the default in the select do it like this instead: <option value="None" selected>None</option> oh duh i just noticed something: you need the 'repeating_stdevice_"+id in front of the sactive and sdevice fields when you read them too: change this: if (v.sactive == "1" && v.sdevice == "StealthSuit_1") { to this, also i add the defaults here in case they are undefined: (that is what the ||"0" and ||"None"  is for. So if undefined is on the left, that is considered "false", so it looks at the right side of the OR/|| and then uses the string provided.) if ((v["repeating_stddevice_" + id[i] +"_sactive"]||"0") == "1" && (v["repeating_stddevice_" + id[i] + "_sdevice"]||"None") == "StealthSuit_1") { also im not sure what you are counting but it looks like your max may be wrong. right now it the max will only be 0 or 1. is that right? if you are just looking for 0 or  1 you can put a break; statement after finding the first one. if(_.size(sbo setAttrs({sbonus: _.max(lbonus) });
sactive is the "equipped" switch, so sactive == 0 means not equipped so no bonus at all for that piece of equipment, and sactive == 1 means equipped so take the bonus into consideration. The max bonus may be 0, 1 or 2. I have simplified my example for my question. The rows represent different pieces of equipment, and I want the character to receive only the single highest value applicable to the skill. Having a +1 and +2 item that adds to Ambush means you only get +2 to the roll calculation not +3. I don't want to break at +2 because new developments in game may mean the players get a +3 item. I will apply your suggestion and see how it turns out. Thank you for your feedback.
1498219793

Edited 1498221985
Jakob
Sheet Author
API Scripter
Another important thing: getAttrs is asynchronous. This means that this part: for (i=0; i<id.length; i++) { getAttrs(["repeating_stddevice_" + id[i] + "_sdevice","repeating_stddevice_" + id[i] + "_sactive"], function(v) { switch (skillname) { case "Ambush": if (v.sactive == "1" && v.sdevice == "StealthSuit_1") { lbonus.push(1); }  default: console.log("============> no active applicable device for " + skillname); } }); } setAttrs({sbonus: _.max(lbonus) }); console.log("sbonus value " + _.max(lbonus)); will not work as expected. You do not know when function inside the getAttrs() will be executed, and that can very well (in fact, will probably) happen after  the setAttrs({sbonus: _.max(lbonus) }); line. So any changes to lbonus from within the getAttrs call will not be reflected in the final value of sbonus. You need to rewrite this part in such a way that the final line happens after you have iterated through all the rows. The easiest way is probably to just use one getAttrs() call that gets all row attributes at once, then run through the loop and do the setAttrs   from within that getAttrs().
Thank you Jakob, I had forgotten the two types of functions, I will look into the asynchronous semaphore described in the API: Cookbook.
1498256668
Jakob
Sheet Author
API Scripter
Ghostroo said: Thank you Jakob, I had forgotten the two types of functions, I will look into the asynchronous semaphore described in the API: Cookbook. You can do that, but there's no reason to use a semaphore for a task like this.
1498259677

Edited 1498260140
Ghostroo
Sheet Author
var devicebonus = function(skillname) { var lbonus = [0]; console.log("===> skillname / " + skillname); getSectionIDs("repeating_stddevice", function(id) { for (i=0; i<id.length; i++) { getAttrs(["repeating_stddevice_" + id[i] + "_sdevice","repeating_stddevice_" + id[i] + "_sactive"], function(v) { switch (skillname) { case "Ambush": if ((v["repeating_stddevice_" + id[i] +"_sactive"]||"0") == "1" && (v["repeating_stddevice_" + id[i] + "_sdevice"]||"None") == "Stealthsuit_1") { lbonus.push(1); }  default: console.log("============> no active applicable device for " + skillname); } setAttrs({sbonus: _.max(lbonus) }, function() {}); }); } }); }; I think this change will mean setAttrs is performed from the getAttrs callback function; it means multiple setAttrs but at the end I will have the max value I am looking for. With the wait function parameter for setAttrs, devicebonus() should resolve with the right value. Have I understood correctly?
I will have to rethink how I want to deal with these buffs, my approach has been synchronous and I don't think the asynchronous functions can be, or even should be, forced into a synchronous behaviour. Thanks everyone for your comments, I will scrap this idea and think of a different way of recovering the buff values.
1498466933
Jakob
Sheet Author
API Scripter
So, I don't know if the above works, but here's a way to do what you want to do there in an almost synchronous way (in the sense that all the actual work can be done in one synchronous part). var devicebonus = function (skillname) {   console.log("===> skillname / " + skillname);   getSectionIDs("repeating_stddevice", function (idArray) {     let allAttrs = [       ...idArray.map(id => "repeating_stddevice_" + idArray[id] + "_sdevice"),       ...idArray.map(id => "repeating_stddevice_" + idArray[id] + "_sactive")     ];     getAttrs(allAttrs, v => {       var lbonus = [0];       idArray.forEach(id => {         switch (skillname) {         case "Ambush":           if (v["repeating_stddevice_" + idArray[id] + "_sactive"] === "1" && v["repeating_stddevice_" + idArray[id] + "_sdevice"] === "Stealthsuit_1") {             lbonus.push(1);           }           break;         default:           console.log("============> no active applicable device for " + skillname);         }       });       setAttrs({         sbonus: _.max(lbonus)       });     });   }); };
1498644761

Edited 1498645325
Ghostroo
Sheet Author
Thanks Jakob. I am using your code and seeing if I can get the result I want.  Essentially sbonus must be set before a subsequent calculation. I have found a way to ensure that by including the subsequent calculating function as a callback to the setAttrs. I do not understand the difference between function() and => , other than => inherits this  from its parent rather than having its own this.   Could the code be rewritten as function() and not lose effectiveness in this circumstance?
1498655237
Jakob
Sheet Author
API Scripter
Ghostroo said: Thanks Jakob. I am using your code and seeing if I can get the result I want.  Essentially sbonus must be set before a subsequent calculation. I have found a way to ensure that by including the subsequent calculating function as a callback to the setAttrs. Yep, that's the way to go here. As long as the code can be kept to run one instance of setAtrs() only, this will work fine. I do not understand the difference between function() and => , other than => inherits this from its parent rather than having its own this. Could the code be rewritten as function() and not lose effectiveness in this circumstance? Yes, it doesn't make a difference here, it's simply an aesthetic choice of mine to use arrow functions.
1498680780
Lithl
Pro
Sheet Author
API Scripter
Ghostroo said: I do not understand the difference between function() and => , other than => inherits this  from its parent rather than having its own this. Arrow functions also do not bind arguments , super , or new.target . The primary goal of the arrow function design was shorter syntax and non-binding of this . That allows programmers to avoid the necessity of Function.prototype.bind , Function.prototype.call , or  Function.prototype.apply to set the value of this . Lack of a super  binding means they cannot be used as constructors, but ES6 adds in class syntax to use instead. Note that an arrow function without brackets implicitly adds return to the start of its expression. So, the following are equivalent: const example = () => 5; // const example = function() { return 5; }; Brackets are required for an arrow function to have multiple statements, and if they are included you have to use return  explicitly if you want it: const example = () => { const rand = Math.random(); return Math.floor(rand * 20) + 1; }; A quirk of this feature of arrow functions crops up when a single-statement arrow function needs to return an object literal. The literal needs to be wrapped in parentheses for the syntax to work: const example = () => ({foo: 'bar'});
Thank you all for your responses, by combining suggestions I was able to get the outcome I was looking for.