Skip to main content

Petstore: Rendering Flavors

The Rendering flavors section (/console/dev/flavors) shows the same list of ten pets drawn by four different view technologies, each inside the console chrome: the HTML5 bean builder, FreeMarker, Mustache and the standalone React app. It replaces the earlier standalone view resources with pages inside the console. Each flavor is a level-3 subtab under Developer, so a reader can flip between them and compare the output.

Four subtabs​

FlavorsRest (/console/dev/flavors) redirects / to the first flavor with a 303 to html. The enum Flavor holds each flavor's id, nav label, caption and the GitHub link to its source. The nav tree in base.ftlh lists them as the nodes html, freemarker, mustache and react under flavors.

SubtabResourceWhat serves the list
htmlHtmlFlavorRestA Table built with the HTML5 bean builder, serialized by HtmlSerializer
freemarkerFreemarkerFlavorRestThe fragments/pets.ftlh template through the FreeMarker view bridge
mustacheMustacheFlavorRestA pets.mustache template through MustacheViewRenderer and a MustacheMixin
reactFlavorsRest.reactThe standalone app at /petstore-ui, embedded in an iframe

All three server flavors draw the same rows. FlavorPets.rows returns the first ten pets by id with display-ready strings (the price has two decimals), so the engines differ only in markup.

Chrome-less fragments​

Each server flavor is a child resource that serves two things: its console page at /, and a bare fragment at /fragment with no page chrome. The console page is one html card whose content comes from the fragment through src=. The fragment is a <table class="petstore-flavor" data-flavor="...">, so a test can tell the engines apart and compare their cells exactly. The FreeMarker fragment carries no <@page> for the same reason: it is loaded into a card, not served as a page.

The React flavor has no fragment. Its page carries an iframe pointing at /petstore-ui, the same app that talks to the /petstore API on its own. /petstore-ui is PetstoreUiResource, which is not part of the console.

Template​

flavors.ftlh serves the three server flavors. The engine, label, caption and source attributes come from Flavor.page():

<@page tab="dev/flavors/${engine}" toolkit="views">
<@card id="caption" title=label>
<div class="jc-prose"><p>${caption}</p><p><a href="${source}">View source</a></p></div>
</@card>
<@card id="flavor" title="Pets" src="/console/dev/flavors/${engine}/fragment"/>
</@page>

flavors-react.ftlh has the same caption card and replaces the last card with the iframe.

Builder twin​

var p = PageSpec.create().tab("dev/flavors/" + flavor.id()).toolkit("views")
.html("caption", flavor.label(), "<div class=\"jc-prose\"><p>%s</p><p><a href=\"%s\">View source</a></p></div>"
.formatted(escapeHtml(flavor.caption()), escapeHtml(flavor.sourceUrl())));
if (flavor == Flavor.REACT)
return p.html("flavor", "React app", "<iframe src=\"/petstore-ui\" ... ></iframe>");
return p.htmlSrc("flavor", "Pets", "/console/dev/flavors/" + flavor.id() + "/fragment");

?builder renders this on every subtab.

Source: FlavorsRest, HtmlFlavorRest, FreemarkerFlavorRest, MustacheFlavorRest, Flavor and FlavorPets in juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/dev/flavors/; DevPageBuilders.flavor in .../console/pages/; flavors.ftlh, flavors-react.ftlh and fragments/pets.ftlh under .../console/templates/, and pets.mustache under .../console/fragments/.

Tests: FlavorsRest_Test, PetstoreFlavors_BrowserTest.

See also​