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 .
×
D&D 2024 has arrived! Pre-order the new core rulebooks now and get an exclusive pre-order bonus for free!
Create a free account

Sheet Templating with Handlebars

1593958612

Edited 1593959866
GiGs  said: Interesting! Would Mustache have been a better solution than Handlebars? I gave it some thought, and I still like Handlebars better. In fact, I would contend that Roll20's roll templates should have used Handlebars too. The primary distinction between the two is that Mustache is explicitly "logic-less", just putting the values from the source data directly into the output. Handlebars split from Mustache to be able to include logic. The helper functions created by Roll20 for Mustache actually violate the "logic-less" principle, and are implemented in a sort of hacky way. In Mustache, helper functions only receive the inner content and the renderer as parameters; they do not accept additional tokens inside the {{ }} as tokens. I'm pretty sure Roll20 took version 0.8 (from 7 years ago) and modified it to identifier their helpers and provide additional arguments - I can't find a Mustache version that has the same `renderTokens` function that's bundled in the Roll20 app.js. All the other things that Mustache does, Handlebars also does with the same syntax. (Handlebars doesn't have all of that documented, like using `#<property>` or `^<property>` for conditional sections or iterating over arrays, but those do still work. Handlebars documentation presents `#if`, `#unless`, and `#each` built-in helpers, which are more robust versions of the same thing.)  I suppose file size and runtime performance *might* have been concerns. Handlebars' file is larger, and I'm sure it runs slower, but I expect the difference is a matter of milliseconds. edit : Actually, the Handlebars homepage promotes faster execution than most other templates. I didn't realize that Mustache doesn't precompile templates like Handlebars does. Handlebars is likely faster, so long as Roll20 actually precompiles the rolltemplates. The "logic" supported in Handlebars and not in Mustache ranges from simple conditions - basically what Roll20 hacked Mustache to do - to transformations of the source data. The Mustache's design philosophy is those conditions and transformations should be run to generate the source data passed to the template, but Roll20's implementation doesn't have such a step. The closest it has are the auto-calc formulas, which themselves are pretty limited. On the other hand, since custom sheet authors still won't be able to register their own helper functions anyway, the result is effectively the same. Using Handlebars would just mean Roll20 won't have to use a customized version of the template renderer. (That the Roll20 Mustache is 7 years and 4 major versions out-of-date is kind of concerning.) Maybe if they were using Handlebars, they can import libraries of helpers like I did in my sheet generator, but maybe that would overwhelm amateur contributors.
1593959256

Edited 1593960221
The differences between the libraries in 2013 were less significant, but Handlebars still had at that time the exact functionality that Roll20 was looking for when they modified Mustache to take parameters in the registered helpers:&nbsp; <a href="https://github.com/handlebars-lang/handlebars.js/tree/v1.3.0#differences-between-handlebarsjs-and-mustache" rel="nofollow">https://github.com/handlebars-lang/handlebars.js/tree/v1.3.0#differences-between-handlebarsjs-and-mustache</a> edit:&nbsp; The "Handlebars Helpers" was also available at that time:&nbsp; <a href="https://github.com/helpers/handlebars-helpers/tree/v0.4.2" rel="nofollow">https://github.com/helpers/handlebars-helpers/tree/v0.4.2</a>
1593977038
GiGs
Pro
Sheet Author
API Scripter
Thanks for the explanation! I guess we'll never know why they chose one over the other.&nbsp;
1596655043

Edited 1596655568
GiGs
Pro
Sheet Author
API Scripter
I have encountered a problem with this, and I think I found the solution once before, but cant find it now. When making buttons, you will sometimes want to use code that refers to an attribute name from the data.json file, like this: &lt;button&nbsp;type="roll"&nbsp;value="/roll&nbsp;1d20 + @{{{Attribute}}}"&gt;&lt;/button&gt; basically getting the {{Attribute}} value, and wrapping it in @{ } But the triple {{{}}} has its own use in handlebars, and stops this working. How do I get this to work? Edit: &nbsp;aha, I should have realised. html entities, the classic roll20 fix whenever things aren't working. Is there are more elegant approach using helpers?
1596661615
GiGs
Pro
Sheet Author
API Scripter
I just found out this system breaks when creating custom rolltemplates, which use {{ }} extensively for their own purpose. Is there a way to fix this? If there's no innate method, one way would be use a different symbol within rolltemplates, like [[ ]] (since you cant use inline rolls within the rolltemplate design code), then have them converted to {{ }} when the program generates the html file.
You can have a given token-like thing ignored by handlebars with a \: \{{ignored}} @\{{{Attribute}}} If you have a large section with several token-like things, and you don't actually need tokens in that section, you can try the "raw block": {{{{raw}}}} {{ignored}} @{{{Attribute}}} {{{{/raw}}}} Denoting portions of a parsed string that the parser should ignore, even though they look like the things the parser is looking for, is referred to as "escaping". <a href="https://handlebarsjs.com/guide/expressions.html#escaping-handlebars-expressions" rel="nofollow">https://handlebarsjs.com/guide/expressions.html#escaping-handlebars-expressions</a>
whoops, you actually wanted the Attribute value in single-curly-brace, not the whole triple-curly-brace ignored. You can add whitespace between your curly-braces in the template, and use the ~ to suck back in the whitespace in the result. @{ {{~Attribute~}} }
1596664417

Edited 1596664650
GiGs
Pro
Sheet Author
API Scripter
Thanks, that should work for attributes. Is there a way to make rolltemplates work? There are two problems. the raw template block might work for the rolltemplate design. But it wont work for button rolls, where you use both {{ }} and @{ } and will want to use the handlebars {{ }} and the rolltemplate templating {{ }}.
1596665327
GiGs
Pro
Sheet Author
API Scripter
It doesnt look like the raw text block works for rolltemplate design. This is what my attempt gave me:&nbsp; It looks like a bunch of escape characters are interfering. If you want to test this yourself, you can try with jakob's custom rolltemplate from the wiki:&nbsp; <a href="https://wiki.roll20.net/Roll_Templates#Jakob.27s_Better_Default_Rolltemplate" rel="nofollow">https://wiki.roll20.net/Roll_Templates#Jakob.27s_Better_Default_Rolltemplate</a>
1596675114

Edited 1596675362
Kind of weird and frustrating that the built-in `raw` doesn't work as described. Using the \ to escape everything still works: &lt;rolltemplate class="sheet-rolltemplate-custom"&gt; &nbsp; &lt;div class="sheet-container sheet-color-\{{color}}"&gt; &nbsp; &nbsp; &lt;div class="sheet-header"&gt; &nbsp; &nbsp; &nbsp; \{{#title}}&lt;div class="sheet-title"&gt;\{{title}}&lt;/div&gt;\{{/title}} &nbsp; &nbsp; &nbsp; \{{#subtitle}}&lt;div class="sheet-subtitle"&gt;\{{subtitle}}&lt;/div&gt;\{{/subtitle}} &nbsp; &nbsp; &lt;/div&gt; &nbsp; &nbsp; &lt;div class="sheet-content"&gt; &nbsp; &nbsp; &nbsp; \{{#allprops() title subtitle desc color}} &nbsp; &nbsp; &nbsp; &lt;div class="sheet-key"&gt;\{{key}}&lt;/div&gt; &nbsp; &nbsp; &nbsp; &lt;div class="sheet-value"&gt;\{{value}}&lt;/div&gt; &nbsp; &nbsp; &nbsp; \{{/allprops() title subtitle desc color}} &nbsp; &nbsp; &nbsp; \{{#desc}}&lt;div class="sheet-desc"&gt;\{{desc}}&lt;/div&gt;\{{/desc}} &nbsp; &nbsp; &lt;/div&gt; &nbsp; &lt;/div&gt; &lt;/rolltemplate&gt; But of course, that's silly and obnoxious. The handlebars-helpers library includes a `noop` helper that does what we need. But I left out the "misc" helpers, and I don't remember why. So I created a new release that includes the "misc" helpers. <a href="https://github.com/PrimalZed/character-sheet-generator/releases/tag/v0.4.0-beta" rel="nofollow">https://github.com/PrimalZed/character-sheet-generator/releases/tag/v0.4.0-beta</a> With that, you should be able to use the `noop` in a block helper: {{{{noop}}}} &lt;rolltemplate class="sheet-rolltemplate-custom"&gt; &nbsp; &lt;div class="sheet-container sheet-color-{{color}}"&gt; &nbsp; &nbsp; &lt;div class="sheet-header"&gt; &nbsp; &nbsp; &nbsp; {{#title}}&lt;div class="sheet-title"&gt;{{title}}&lt;/div&gt;{{/title}} &nbsp; &nbsp; &nbsp; {{#subtitle}}&lt;div class="sheet-subtitle"&gt;{{subtitle}}&lt;/div&gt;{{/subtitle}} &nbsp; &nbsp; &lt;/div&gt; &nbsp; &nbsp; &lt;div class="sheet-content"&gt; &nbsp; &nbsp; &nbsp; {{#allprops() title subtitle desc color}} &nbsp; &nbsp; &nbsp; &lt;div class="sheet-key"&gt;{{key}}&lt;/div&gt; &nbsp; &nbsp; &nbsp; &lt;div class="sheet-value"&gt;{{value}}&lt;/div&gt; &nbsp; &nbsp; &nbsp; {{/allprops() title subtitle desc color}} &nbsp; &nbsp; &nbsp; {{#desc}}&lt;div class="sheet-desc"&gt;{{desc}}&lt;/div&gt;{{/desc}} &nbsp; &nbsp; &lt;/div&gt; &nbsp; &lt;/div&gt; &lt;/rolltemplate&gt; {{{{/noop}}}}
1596679953
GiGs
Pro
Sheet Author
API Scripter
That works, thanks! Is there a way to avoid using html replacements in a button value? Here's an example of using that Custom rolltemplate, combined with handlebars each loop. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {{#each &nbsp; Attributes &nbsp; as &nbsp;|Attribute|&nbsp; }} &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt; input &nbsp; type= "number" &nbsp; class= "stat-value" &nbsp; name= "attr_ {{ Attribute }} " &nbsp; value= "0" &nbsp;readonly &nbsp;/&gt; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt; button &nbsp; type= "roll" &nbsp; class= "stat-button" &nbsp; value= "&amp;{template:custom}&nbsp;&amp;#123;&amp;#123;title=@{character_name}&nbsp;&amp;#125;&amp;#125;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &amp;#123;&amp;#123;subtitle= {{ capitalize &nbsp;Attribute }} &amp;#125;&amp;#125;&nbsp;[[&nbsp;[[@{&nbsp; {{~ Attribute ~}} &nbsp;}]]-[[1d20]]&nbsp;]]&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &amp;#123;&amp;#123;Score=$[[0]]&amp;#125;&amp;#125;&nbsp;&amp;#123;&amp;#123;Roll=$[[1]]&amp;#125;&amp;#125;&nbsp;&amp;#123;&amp;#123;Result=$[[2]]&amp;#125;&amp;#125;" &nbsp; &gt; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt; span &nbsp; class= "stat-name" &gt; {{ capitalize &nbsp;Attribute }} &lt;/ span &gt; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/ button &gt; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {{/each}} I've put some artifical linebreaks in the button value to make it readable. Well, more readable... Rolltemplates require you to use a lot of {{ }} sections, and handlebars wants you to use its version of {{ }} within those. I can't noop or escape the outer {{ }}, because the inner {{ }} ones are for handlebars.
This seemed to work: {{#each attributes as |attribute| }} \{{subtitle={{capitalize attribute~}} }} {{/each}} A combination of the escape for the ignored open double-curly-braces, and then a whitespace between the actual close double-curly-braces and the ignored close double-curly-braces with a ~ to suck it in.
1596682214
GiGs
Pro
Sheet Author
API Scripter
Thanks!&nbsp; By my testing, the ~ doesnt seem to be necessary here. This worked for me: \{{subtitle={{capitalize&nbsp;Attribute}}&nbsp;}} Probably because you dont actually need to get rid of that whitespace in this instance.