Magnolia: FMX Rendering

- Dec 17, 2017 3:45:41 PM

The Magnolia FMX Renderer is an additional templating language based upon my own FMX library (see FMX). And how does this language looks like? It's quite similar to Freemarker as it's essentially an XML representation of Freemarker.

So why would you wanna like to use if it's just an alternative representation?

This extension has several advantages over the direct use of Freemarker:

  • Since it's XML it's similar to the code that's supposed to be rendered in most cases  (typically html/xml code). Therefore it's no longer a bunch of Freemarker language elements which pollutes the template code itself.
  • You still can use the classic Freemarker code, so there's no pressure to migrate existing templates on the spot.
  • The overhead to support the FMX extension is basically miniscule and can be ignored (I couldn't find any significant performance losses using the profiler YourKit). Furthermore the overhead only affects the loading time, so once the template has been accessed, there's no difference to standard Freemarker templates.
  • One of the strongest features are specific FMX elements and attributes allowing to tackle some common use cases in an elegant manner.

 

If you are in a hurry or you simply want to get a quick impression about it's use in Magnolia, here's a little video of approx 15min (I promise I'll practice to create better videos ;-):

 

Setup in Magnolia

The setup in Magnolia is pretty easy as shown in the video. Just add the following dependency to your Magnolia project and you are good to go:

<dependency>
  <groupId>com.kasisoft.mgnl</groupId>
  <artifactId>ks-mgnl-fmx</artifactId>
  <version>0.6</version>
</dependency>

This module registers the additional TemplateLoader class com.kasisoft.mgnl.fmx.freemarker.MgnlFmxTemplateLoader below the path /server/rendering/freemarker/templateLoaders/fmx . This node also allows to setup debugging mode using the property debug. If this property is set to true the FMX templates will be logged together with their corresponding FTL representations.

Apart from that: Everything stays the same.

 

Let the fun part begin

Empty attributes

One common problem while writing templates is the rendering of attributes and their values. If values are mandatory there's no issue but sometimes they aren't so you are in a pickle. You can either create a testing IF-statement around the attribute rendering or you simply live with the fact that the rendered code might contain empty attributes. Let's have a look at such a scenario:

<div class="contact" data-name="${model.name}" [#if model.age?has_content]data-age="${model.age}"[/#if] 
>
    <p>You aren't too old for this page.</p>
</div>

This is a really simple scenario as there's only one attribute that can be empty. But let's think of an example where you've got more than one and I bet you already came across such an example. You would add another IF-statement and another one, and another one and so on. At some point the template won't be readable anymore. Or you go in favor for a readable template and you simple generate empty attributes which isn't nice either.

Now here comes FMX to the rescue (see empty-attributes):

<div class="contact" data-name="${model.name}" fmt:data-age="model.age">
    <p>You aren't too old for this page.</p>
</div>

This immediately looks better. Simply prefixing the attribute name with fmt and you get the desired result. The FmxTemplateLoader is generating the corresponding test within the templating code for you whilst the fmt prefix is essentially a marker so the loader knows the attributes that need some processing.

With makes templating easier

What? fmx:with is an FMX construct which allows to make life somewhat easier as it can be used to simplify expressions. While migrating to Magnolia 5.6 I stumbled upon an issue. I'm using a custom extension to the ExtendedAggregationState and the model reference ${state} didn't gave me that reference any longer (this change came in order to improve the security of Magnolia). My custom instance was still accessible but I had to use ${state.unwrap()} instead. Since I've used this state quite often if would have been cumbersome to make these changes to each expression. So there it was: A pretty use case for fmx:with (see also fmx-with):

<fmx:with fmx:value="state.unwrap()" fmx:name="state">
    <meta property="og:url" content="${state.canonicalUrl!}" />
</fmx:with>

This example is pretty basic but it shows how to simplify expressions using this mechanism.

 

Optional Parent Elements

Sometimes you need to render elements with children whereas the parental one is optional. For instance you've got a teaser element with a quite complex structure. This teaser element should be wrapped by an A tag if a link has been specified. So you've got two possibilities here:

[#if model.link?has_content]<a href="${model.link}">[/#if]
<div class="teaser-element>
     <img src="..."/>
     <div class="text-location"> 
         ....
     </div>
</div>
[#if model.link?has_content]</a>[/#if]

or 

[#if model.link?has_content]
<a href="${model.link}">
<div class="teaser-element>
     <img src="..."/>
     <div class="text-location"> 
         ....
     </div>
</div>
</a>
[#else]
<div class="teaser-element>
     <img src="..."/>
     <div class="text-location"> 
         ....
     </div>
</div>
[/#if]

Neither of these constellations look appealing and remember that this is just an example so these are quite basic.

The  fmw:wrap (see fmx:wrap) attribute is here to save us from such constructs:

<a href="${model.link}" fmx:wrap="model.link?has_content">
<div class="teaser-element>
     <img src="..."/>
     <div class="text-location"> 
         ....
     </div>
</div>
</a>

As you can see the solution was pretty easy. Attach an fmx:wrap attribute with a corresponding predicate and you either generate code including a parent or only the inner code.

 

Render Lists

Of course it's sometimes necessary to render lists and to make things more interesting, the list will only be rendered if it has content. The list rendering portion is realized through the fmx:list (see fmx:list) construct. The test for emptiness of a literal/list can be done using fmx:depends (see fmx:depends). Let's have a look at the following example in pure FTL:

[#if model.list?has_content]
    <ul>
         [#list model.list as element]
             <li>Listelement: ${element}</li>
         [/#list]
    </ul>
[/#if]

We can do way better through the use of FMX:

<ul fmx:depends="model.list?has_content">
    <li fmx:list="model.list">Listelement: ${it}</li>
</ul>

Doesn't that look much better? We've mainly added some attributes to the code that is supposed to be rendered and reached the same goal.

This template is what I call easily readable and thereofre much better maintainable.

 

What about Magnolia specific directives?

Of course it's necessary to have those directives cms.init, cms.area and cms.component , too (see fmx-directive). These type of directives are essentially just easy mappings from FMX to FTL. Here they are in FMX (I don't think that they need much of an explanation):

<fmx:cms-init />

<fmx:cms-component content="component"/>

<fmx:cms-area name="main"/>

 

Resume

I didn't present all possible FMX constructs and some of the aforementioned constructs can be used in alternative ways (f.e. as dedicated XML elements). You can find a complete documentation at the project page for FMX.

Let me know whether you've got a fancy idea for an extension.

 

Resources

A Github project allowing to experiment with that feature can be found here:

 

 

Comments are moderated. Therefore they're only being published when the moderator decides to enable them.

FEEDBACK