Did you ever need to render some piece of text or html most of which is predefined and just a few spots must be filled with dynamic content? If so, you most probably know about template engines. And most probably you’ve heard about Mustache.

Mustache is quite a popular thing in the world of web development. It is a logic-less template syntax, which allows to create a text template, mark some places with tags in it and then replace those tags with dynamic content from your program – values or lists of values. For example, here is a simple greeting template:

“Hello {{name}}!”, where {{name}} is a tag referring to variable (structure component) called name, which will be substituted to actual value during the processing and result in e.g “Hello User X !”

Mustache is good for generating HTML chunks (or whole pages) or virtually any other templatable text stuff. The original specification can be found here. The mustache syntax is implemented in huge number of languages. And recently, I’ve created an implementation of mustache processor in ABAP (called abap mustache, not surprisingly). The code is open source and free and hopefully can be useful for someone else. Here is the github project page.

A simple example to start with

Let’s say you want to generate a small HTML chunk with sales offers like below. The dynamic parts are sales items and prices and the store name.

Welcome to <b>My Super Store</b>!
<table>
  <tr><td>Boots</td><td>$99.00</td>
  <tr><td>T-Short</td><td>$49.00</td>
  <tr><td>Shirts</td><td>$59.00</td>
</table>

The template would look correspondingly:

Welcome to <b>{{shop_name}}</b>!
<table>
  {{#items}}
  <tr><td>{{name}}</td><td>${{price}}</td>
  {{/items}}
</table>

Notice that {{#items}} tag has # symbol at the beginning – this starts a section ended with corresponding {{/items}}. Sections may be used for:

  • conditional output (if items is not empty/false/zero then internal content of the section is included to the output, and skipped otherwise)
  • iterative output if items is a table. This is the case for our example: the content of the table is iterated and the section is repeated for each record. A section may contain other sections – the depth of the data structure is not limited for the moment. Notice that for our example name and price are supposed to be the components of an item. However, if some {{tag}} is not found in the current context (table line), it is then searched in upper context levels (e.g. in the structure that contain shop_name in our case).

So how to render this in abap? Supposing you have the following data structures …

types: 
  begin of ty_shop_item
    name type string,
    price type s_price, 
  end of ty_shop_item,

  ty_shop_item_tt type standard table of ty_shop_item,

  begin of ty_shop,
    shop_name type string,
    items type ty_shop_item_tt, 
  end of ty_shop.

… fill it somewhere correspondingly and render the template for your data …

data lo_mustache type ref to lcl_mustache.
data lv_text type string.

" c_nl is a local shortcut for newline char, e.g. from cl_abap_char_utilities
" it is not mandatory, I just suppose that we want readable html as output
lo_mustache = lcl_mustache=>create(
  'Welcome to <b>{{shop_name}}</b>!' && c_nl &&
  '<table>' && c_nl &&
  ' {{#items}}' && c_nl &&
  ' <tr><td>{{name}}</td><td>${{price}}</td>' && c_nl &&
  ' {{/items}}' && c_nl &&
  '</table>' ).

lv_text = lo_mustache->render( ls_my_data ). " ls_my_data type ty_shop 

This is how it works. Now lv_text contain the rendered html which you can use further.

Some more complex details

Data input

  • Input data may be a structure or a table of any type – it is detected automatically. For a table the template is repeated for each line. The depth of the data is not limited for the moment (so sections can contain other sections and so on).
  • Additionally, there is a special table type lcl_mustache=>ty_struc_tt which processed as a “universal” flat structure. name component corresponds to tag name (case insesitive) and the value may be put either to val field (as a string) or as a reference to dref. The latter can be then a reference to a structure or a table (with section data). Here is the above example using it:
data lt_uni_data type lcl_mustache=>ty_struc_tt.
field-symbols <i> like line of lt_uni_data.

append initial line to lt_uni_data assigning <i>.
<i>-name = 'shop_name'.
<i>-val = 'My Super Store'.

append initial line to lt_uni_data assigning <i>.
<i>-name = 'items'.
get reference of lt_items to <i>-dref. " lt_items type ty_shop_item_tt (filled elsewhere)
...
lv_text = lo_mustache->render( lt_uni_data ).
  • The lcl_mustache=>ty_struc_tt is mainly intended to be the root structure. Supposedly, it is convenient to prepare parts of data as regular structures and tables. However, combining them all together at the top level may be cumbersome. This is where lcl_mustache=>ty_struc_tt may serve. Although this is just an option.

Templates input and result output

  • The template may be a string or a table of strings (`type string_table`)
lo_mustache = lcl_mustache=>create( lv_template ). " DEFAULT iv_template = lv_template
* OR
lo_mustache = lcl_mustache=>create( it_template = lt_template_tab ). " TABLE of strings
  • Output can be requested as a string or a table of string (`type string_table`)
lv_text = lo_mustache->render( ls_my_data ).
* OR
lt_text_tab = lo_mustache->render_tt( ls_my_data ). " TABLE

There is certain specifics about new line character processing which can be found at the repository readme file and wiki of the project.

Partials

Another cool feature of mustache concept is Partials. A partial may be seen as an include. So you may prepare your template parts in convenient and logical pieces. And reuse them template-wide if necessary.

lo_mustache = lcl_mustache=>create(
  'Welcome to <b>{{shop_name}}</b>!' && c_nl &&
  '{{>items_template}}' ). " << CALLING A PARTIAL !

lo_partial = lcl_mustache=>create(
  '<table>' && c_nl &&
  ' {{#items}}' && c_nl &&
  ' <tr><td>{{name}}</td><td>${{price}}</td>' && c_nl &&
  ' {{/items}}' && c_nl &&
  '</table>' ).

lo_mustache->add_partial( " Register the partial
  iv_name = 'items_template'
  io_obj = lo_partial ).

lv_text = lo_mustache->render( ls_my_data ). " ls_my_data type ty_shop

More

Currently the class implements all the standard mustache features. There are some plans to implement some features inspired by Handlebars (another mustache-like library) and maybe some options to use class methods as tag data suppliers. This, however, depends on the real use-cases which me (or other users) of abap mustache will face.

Further details, examples and API description can be found in wiki of the abap mustache project.

Installation

The best option to install is to clone the repository to your SAP system using abapGit tool (it is a great software to share code, certainly a thing to try). Alternatively, it can be copied to the system as a single include (or actually two, if you prefer to preserve unit tests ;). Details are at project readme file.

Have fun !

To report this post you need to login first.

1 Comment

You must be Logged on to comment or reply to a post.

Leave a Reply