$variable._args[0] in widgets

Twine Version: 2.8.1
sugarcube: 2.36.1

G’day.

I was looking the forums for similar problems, but somehow it didn’t work for me. I tried with “”, ‘’, , but to no avail. What am I not understanding here?


<<widget "ab" container>><div class="actions-row"><div>_contents</div></div><</widget>>

<<nobr>><<widget "compass">><div class="compass">
  <<if _args[0] != "">>
    <<if $explored._args[0] == 100>>
      <<ab>><a data-passage="_args[0]" data-setter="$energy -= 0.2+$Oeu">N</a><</ab>>
    <</if>>
  <</if>>
  <div class="WandE">
    <<if _args[1] != "">>
      <<if $explored._args[1] == 100>>
        <<ab>><a data-passage="_args[1]" data-setter="$energy -= 0.2+$Oeu">W</a><</ab>>
      <</if>>
    <</if>>
    <<if _args[2] != "">>
      <<if $explored._args[2] == 100>>
        <<ab>><a data-passage="_args[2]" data-setter="$energy -= 0.2+$Oeu">E</a><</ab>>
      <</if>>
    <</if>>     
  </div>
    <<if _args[3] != "">>
      <<if $explored._args[3] == 100>>
        <<ab>><a data-passage="_args[3]" data-setter="$energy -= 0.2+$Oeu">S</a><</ab>>
      <</if>>
    <</if>>  
  </div><</widget>><</nobr>>

But this error comes out:

Error: <<compass>>: errors within widget code (Error: <<if>>: bad conditional expression in <<if>> clause: Cannot read properties of undefined (reading '0'); Error: <<if>>: bad conditional expression in <<if>> clause: Cannot read properties of undefined (reading '2'); Error: <<if>>: bad conditional expression in <<if>> clause: Cannot read properties of undefined (reading '3'))
<<compass "T2N" "" "T1NE" "TBJ0">>

note: you didn’t state this, nor did you supply an example, but I will assume that the $explored Story variable contains an Generic Object with at least the following properties…

<<set $explored to {T2N: 100, T1NE: 100, TBJ0: 100}>>

There are two syntaxes that can be used to access a property of an Object:

  1. Dot Notation (eg. $explored.T2N )
  2. Bracket Notation (eg. $explored['T2N'] )

With Dot Notation you must supply the property name when you write the code. However, with Bracket Notation the String representation of the property name can also be from a variable or generated via an expression when the code is executed.

There are three main issues with your widget based example:

  1. You’re using the wrong syntax when trying to dynamically access properties of the Object stored in $explored.
  2. You’re not using Attribute Directive markup when trying to assign the value of an expression to an attribute of a HTML element, in this specific case the current value of one of the _args Array elements to a data-passage attribute.
  3. The <a> element you are supplying as content to the <<ab>> widget is evaluated inside that widget’s context, and that context has its own _args Array. Which will be empty because you’re not passing an arguments when you call <<ab>>.

The following variation of your example has the above issues fixed.

<<widget "ab" container>>
	<div class="actions-row">
		<div>_contents</div>
	</div>
<</widget>>

<<widget "compass">>
	<div class="compass">
		<<if _args[0] != "">>
			<<if $explored[_args[0]] == 100>>
				<<ab _args[0]>>
					<a @data-passage="_args[0]" data-setter="$energy -= 0.2+$Oeu">N</a>
				<</ab>>
			<</if>>
		<</if>>
		<div class="WandE">
			<<if _args[1] != "">>
				<<if $explored[_args[1]] == 100>>
					<<ab _args[1]>>
						<a @data-passage="_args[0]" data-setter="$energy -= 0.2+$Oeu">W</a>
					<</ab>>
				<</if>>
			<</if>>
			<<if _args[2] != "">>
				<<if $explored[_args[2]] == 100>>
					<<ab _args[2]>>
						<a @data-passage="_args[0]" data-setter="$energy -= 0.2+$Oeu">E</a>
					<</ab>>
				<</if>>
			<</if>>
		</div>
		<<if _args[3] != "">>
			<<if $explored[_args[3]] == 100>>
				<<ab _args[3]>>
					<a @data-passage="_args[0]" data-setter="$energy -= 0.2+$Oeu">S</a>
				<</ab>>
			<</if>>
		<</if>>  
	</div>
<</widget>>

A couple of suggestions (which you can ignore if you want):

  1. Assign the special nobr Passage Tag to the Passage that contains your widget definitions, that way you don’t need to use the <<nobr>> macro on the content of that Passage.
  2. The == mathematical comparison operator allows JavaScript to coerce (change) the data-types of the values being compared, which can cause issues. The === mathematical comparison operator is a better choice, as it doesn’t allow such coercion. And the same issue can occur when using != instead of !== when doing a not equal comparison.

Hey, Greyelf, thank you for the elucidation.

ChatGPT tells me with bracket the variable is defined as String and therefore allows more “fiddling” in the coding.

Would’ve the bracket notation also possible if my initial sets were:

<<set $explored1N = 0, $explored1N = 0>>

to

<<if $explored['_args[0]'] != "">> 

I think, I feel, it is not possible as in JavaScript it is set as one variable and not as a variable array - like in $explored.T1N. Am I correct?

All in all, thanks to your correction, it works like I wished. Very big thanks again.


  1. About the <<nobr>> passage … facepalm at myself. Yes, of course. Thanks for hinting.

  2. About the operators. I do not understand, I guess I would need more experience in JavaScript to understand. ChatGPT tells me, with triple operators a String value is considered false even it has a 1 in it. Wouldn’t it be flexible if I use the double operators, as I can use the 1 as a string as well as a value?

As far as I know, the only Programming Language that ChatGPT can actually test its “code recommendations” for is Python, and ChatGPT is well known for “hallucinating” and mixing the syntax / features of different Programming Languages together in its examples. So I would take any suggestion it makes regarding code with a grain of salt.

As I showed earlier, with Bracket Notation the Property Name is represented by a String value. And in your <<if $explored['_args[0]'] != "">> example the String value is the series of letters / numbers / punctuation characters that make up '_args[0]', not the value of the _args[0] variable reference.

So if you want to use the current String value stored in the _args[0] variable reference as the Property Name being passed to the Bracket Notation then you would do the following…

<<if $explored[_args[0]] != "">>

Regarding the difference between === and == in JavaScript.

"abc" === "abc"      <= true
"123" === "123"      <= true
"123" === 123        <= false, because "123" is a String and 123 is a Number.

"abc" == "abc"      <= true
"123" == "123"      <= true
"123" == 123        <= true, because the 123 Number is coerced to a "123" String.

But relying on such coercion can result in comparisons being true even when you don’t actually want them to be considered such. Which is why the general advice is to use ===.

Hm, ok, then by next opportunity I shall try and fiddle them out (with the brackets and Strings).

About the operators … I didn’t knew … it feels like that all variable definitions and settings are generally strings(?) No need to answer if an explanation goes to deep.

Ah, I forgot to mention: Thank you much, my compass widget works smooth now.

Some variables will be Strings, like $name or $location or $direction.
Some variables will be Numbers, like $age or $health or $strength.
Some variables will be Booleans, like $hasLamp or $meetJane or $visitedLibrary.
Some variables will be Arrays, like $backpack or $friends or _args.
Some variables will contain other data-types, like Date or Map or Set or…

The point is that a variable can contain a value that can be one of many different data-types, which depends on what that variable represents and how that variable’s value will be used.