HandlebarsABAP
A single-file Handlebars templating implementation for ABAP 7.40 SP02 and up that fills a template from an ABAP structure or table, with block helpers, partials and custom helpers.
Merging structured ABAP data into HTML or text templates with the built-in WWW_HTML_MERGER function module is very limited. HandlebarsABAP compiles a Handlebars template and fills it from an ABAP structure or table, resolving deeply nested structures automatically, with block helpers such as if, unless and each, partials, and custom helpers registered as class methods, function modules or forms. A template can be compiled from a string or, when stored in SMW0, from binary data so language-specific characters are not misread.
Facts
Where it sits
Who is behind it
In the atlas: maintains 1, contributes to 1@monstermichlmaintainerMarc F. BernardContributes to this
In the atlas: contributes to 4@marcfbe1 contributor
What it declares
Every artifact
- Type
- Package
- Origin
- Independently maintained
As a package
- Where it runs
- does not say
- Distributed through
- does not say
Connections
Producing printed forms, PDFs and filled templates for people
Download
README
quoted from monstermichl/HandlebarsABAP under MIT, readHandlebarsABAP
Single-file 7.40 SP02 Handlebars implementation for ABAP.
What is Handlebars?
To quote the Handlebars documentation:
Handlebars is a simple templating language. It uses a template and an input object to generate HTML or other text formats. Handlebars templates look like regular text with embedded Handlebars expressions.
<p>{{firstname}} {{lastname}}</p>A handlebars expression is a
{{, some contents, followed by a}}. When the template is executed, these expressions are replaced with values from an input object.
Why Handlebars for ABAP?
Even though the WWW_HTML_MERGER function module exists to load and merge HTML templates with simple data, it is very limited in its use. Handlebars on the other hand allows for building highly flexible templates and automatically resolves deeply nested structures and tables, making it easy to quickly build extremely customizable HTMLs/texts based on its input data.
To compile a Handlebars template, simply pass it to the static compile-method and call the returned instance's template-method to fill the template with the provided data.
TYPES: BEGIN OF ts_title,
front TYPE string,
back TYPE string,
END OF ts_title.
TYPES: BEGIN OF ts_person,
title TYPE ts_title,
firstName TYPE string,
lastName TYPE string,
END OF ts_person.
" Compile the Handlebars template.
DATA(ls_compile_result) = zcl_handlebars_abap=>compile(
'{{#if title.front}}' &
'{{title.front}} ' &
'{{/if}}' &
'{{firstName}} {{lastName}}'
).
" Create a structure.
DATA(ls_person) = VALUE ts_person(
title = VALUE #( front = 'Ing.' back = 'BSc.' ) firstName = 'Peter' lastName = 'Parker'
).
" Let the magic happen.
DATA(ls_template_result) = ls_compile_result-instance->template( ls_person ).
WRITE / ls_template_result-text. " Prints 'Ing. Peter Parker'.
Installation
HandlebarsABAP can be installed by using the latest release-tag via abapGit or by simply creating a ZCL_HANDLEBARS_ABAP-class on the client and pasting the content of the latest ZCL_HANDLEBARS_ABAP.abap release file into it.
Stored templates
To compile a template stored via transaction SMW0, just pass the template name to compile. It is recommended to store the HTML-template as binary data in SMW0. Not storing it as binary data could lead to a wrong interpretation of language specific letters (e.g. ä, ö, ü, ß, ...). Even though templates stored as HTML are still supported, templates stored as binaries take precedence and are converted to UTF-8.
What's supported so far
Fields
{{title.front}} {{firstName}} {{lastName}} {{title.back}}
Block helpers
Builtin
if
{{#if condition}}
render something
{{else}}
render something else
{{/if}}
unless
{{#unless condition}}
render something
{{else}}
render something else
{{/unless}}
each
{{#each people}}
{{this.firstName}}
{{else}}
No person found
{{/each}}
with
{{#with person}}
{{this.firstName}}
{{else}}
No person found
{{/with}}
Inline helpers
Builtin
log
{{log "Hello World"}}
lookup
{{lookup (lookup employees 0) "firstname"}}
Block parameters
{{#each people as |person index|}}
{{index}}: {{person.firstName}}
{{else}}
No person found
{{/each}}
Sub expressions
{{#if (log "Hello World")}}
what?
{{else}}
log didn't return anything
{{/if}}
Partials
{{! name partial}}
{{firstName}} {{lastName}}
{{! card partial}}
-----------------------------------------------
Name: {{> name .}}
Description: {{description}}
Manager: {{#if is_manager}}Yes{{else}}No{{/if}}
{{> card this is_manager=true}}
{{#each employees}}
{{> card .}}
{{/each}}
How to register a partial
Partials can either be registered globally, directly on the zcl_handlebars_abap class, using register_partial_static or on an instance which gets returned calling compile, using register_partial. Globally configured partials get added to the instances created. However, partials registered on the instances take precedence.
register_partial( iv_name = 'name' iv_template_string = '{{firstName}} {{lastName}}' ).
Custom (block) helpers
It's possible to write and configure custom helpers like in HandlebarsJS but it works a bit differently. In HandlebarsJS a fn- and a reverse-function is passed to the helper which then need to be called to render either the block-content or the else-content. Since ABAP does not allow to pass functions to other functions or methods, the corresponding functions are part of the zcl_handlebars_abap class. The instance of the class gets passed to the helper and the helper needs to either invoke fn or reverse on that instance. The first argument that's passed to the corresponding function is considered the new context within the block (this).
METHOD hello.
ASSIGN it_args[ 1 ]->* TO FIELD-SYMBOL(<name>).
rs_result = io_instance->fn( NEW string( |Hello { <name> } | ) ).
ENDMETHOD.
How to handle data
Each helper function receives an it_args table as argument. This table contains references to the data passed, which could be a struct, a table or something simple like bool, int, string... The same holds true for the hash arguments passed via is_options-hashes.
It's the implementer's responsibility to handle the data correctly. To get a feeling how to implement helpers it can be useful to have a look into backend_eval_cond_helper/backend_eval_each_helper/backend_eval_with_helper.
What to return
The rs_result's text-property specifies, what will be rendered to the output. If something went horribly wrong the rs_result's error-property must be set. This will halt further template execution and will propergate the error up to the end result.
Custom-helper helper-methods
- is_truthy: Returns abap_true if the passed data evaluates to something that is considered truthy by HandlebarsJS standars.
- error: Creates an error string that contains the position where the error occurred.
How to register a custom helper
Custom helpers can either be registered globally, directly on the zcl_handlebars_abap class, using register_helper_static or on an instance which gets returned calling compile, using register_helper. Globally configured helpers get added to the instances created. However, helpers registered on the instances take precedence.
register_helper( iv_name = 'hello' ir_helper = NEW zcl_handlebars_abap=>ts_object_helper( object = lo_helper_object method_name = 'hello_helper' ) ).
Function/Method signatures
Helper functions/methods get passed the following properties IMPORTANT: Be aware of the breaking changes introduced in v1.0.0 and v2.0.0.
- args: The arguments passed to the helper.
- options:
- instance: The instance of the currently processed object on which to call fn/reverse.
- name: The name of the registered helper. This can be useful if method gets registered for different helper names.
- hashes: The hash arguments passed to the helper.
- data: The current data of the block.
(Class-)methods
This is the prefered way to implement helpers as it goes hand in hand with the modern approach of ABAP programming. Both static- and object-methods must implement the following signature to be callable in case of a block helper.
METHODS block_helper_method
IMPORTING
it_args TYPE zcl_handlebars_abap=>tt_data
is_options TYPE zcl_handlebars_abap=>ts_options
RETURNING
VALUE(rs_result) TYPE zcl_handlebars_abap=>ts_text_result.
In case of an inline helper, the returned struct-type changes from zcl_handlebars_abap=>ts_text_result to zcl_handlebars_abap=>ts_data_result. This also applies to Function modules and Forms!
IMPORTANT: Since ts_data_result contains a reference to the returned data, it is necessary to put the data on the heap instead of the stack (e.g. by using CREATE DATA).
METHODS inline_helper_method
IMPORTING
it_args TYPE zcl_handlebars_abap=>tt_data
is_options TYPE zcl_handlebars_abap=>ts_options
RETURNING
VALUE(rs_result) TYPE zcl_handlebars_abap=>ts_data_result.
To register a class-method, use the ts_class_helper structure. E.g.
register_helper( iv_name = 'hello' ir_helper = NEW zcl_handlebars_abap=>ts_class_helper( class_name = 'ZCL_HELPER_CLASS' method_name = 'hello_helper' ) ).
To register an object-method, use the ts_object_helper structure. E.g.
register_helper( iv_name = 'hello' ir_helper = NEW zcl_handlebars_abap=>ts_object_helper( object = lo_helper_object method_name = 'hello_helper' ) ).
Function modules
A function module must implement the following signature to be callable.
FUNCTION helper_method
IMPORTING
it_args TYPE zcl_handlebars_abap=>tt_data
is_options TYPE zcl_handlebars_abap=>ts_options
EXPORTING
es_result TYPE zcl_handlebars_abap=>ts_text_result.
To register a function module, use the ts_func_module_helper structure. E.g.
register_helper( iv_name = 'hello' ir_helper = NEW zcl_handlebars_abap=>ts_func_module_helper( function_name = 'ZCL_HELLO_HELPER' ) ).
Forms
A form must implement the following signature to be callable.
FORM helper_method
USING
it_args TYPE zcl_handlebars_abap=>tt_data
is_options TYPE zcl_handlebars_abap=>ts_options
CHANGING
cs_result TYPE zcl_handlebars_abap=>ts_text_result.
To register a form, use the ts_form_helper structure. E.g.
register_helper( iv_name = 'hello' ir_helper = NEW zcl_handlebars_abap=>ts_form_helper( report_name = 'Z_HELLO_HELPER' form_name = 'hello_helper' ) ).History
Changed 3, Fixes 7
Notes
Changes
Fresh stuff
- Add support for partials (https://github.com/monstermichl/HandlebarsABAP/issues/15)
- Add lookup helper (https://github.com/monstermichl/HandlebarsABAP/issues/16)
- Distinguish between int and float (https://github.com/monstermichl/HandlebarsABAP/issues/25)
Fixes
- Dot as path is not recognized properly (https://github.com/monstermichl/HandlebarsABAP/issues/18)
- Find partial is not executed on the correct instance in register_helper (https://github.com/monstermichl/HandlebarsABAP/issues/19)
- Newlines after helpers lead to rendered newlines (https://github.com/monstermichl/HandlebarsABAP/issues/20)
- If relative path passes root fallback is applied (https://github.com/monstermichl/HandlebarsABAP/issues/21)
- Relative paths like .. or ../.. are not resolved correctly (https://github.com/monstermichl/HandlebarsABAP/issues/22)
- Sub-expressions are limited to simple types (https://github.com/monstermichl/HandlebarsABAP/issues/23)
- Argument evaluation does not work properly if argument is a single path (https://github.com/monstermichl/HandlebarsABAP/issues/24)
Changed 1, Fixes 1
Notes
Changes
Fresh stuff
- Add version constant for better tracking (https://github.com/monstermichl/HandlebarsABAP/issues/13).
Fixes
- Different language information in abapGit files (https://github.com/monstermichl/HandlebarsABAP/issues/12).
Breaking 1, Changed 1, Fixes 2
Notes
Changes
Fresh stuff
- Add support for hash arguments (https://github.com/monstermichl/HandlebarsABAP/issues/8). BREAKING CHANGE: See https://github.com/monstermichl/HandlebarsABAP/issues/8#issuecomment-5072874511.
Fixes
- Literals are not passed properly to helpers (https://github.com/monstermichl/HandlebarsABAP/issues/7).
- Handlebars crashes on empty helper blocks (https://github.com/monstermichl/HandlebarsABAP/issues/9).
Changed 1
Notes
Changes
- Implement loading of binary data stored in SMW0 (https://github.com/monstermichl/HandlebarsABAP/issues/5).
Changed 1
Notes
Changes
- Make sure HandlebarsABAP runs on 7.5 (https://github.com/monstermichl/HandlebarsABAP/commit/02e8bb01082d8500603c2d830ae4da187ac573ca).
Changed 3
Notes
Changes
- Drastically simplify helper implementation by removing ts_data (Breaking change for custom helpers) (https://github.com/monstermichl/HandlebarsABAP/commit/b448591cd22a5fac375ff17a4d0d37e6bfd37d2c).
- Improve path resolution (https://github.com/monstermichl/HandlebarsABAP/commit/b448591cd22a5fac375ff17a4d0d37e6bfd37d2c).
- Don't error on undefined properties but evaluate to empty string (https://github.com/monstermichl/HandlebarsABAP/commit/5cf39ee06a3e5b7f53e5bb9001385ed13481567f).
Others providing
Compare all 11 on the Document output page →- abap2xlsxabap2xlsxA library that reads and generates .xlsx Excel workbooks directly from ABAP data, without exporting through a list viewer, installed through abapGit and tested from SAP_ABA 731.independent, abapLibrary that reads and generates Excel spreadsheets from ABAPfrom ABAP
- zcl_docxzcl_docxAn ABAP class that fills a Word .docx template tagged with content controls, with a report, ZDOCX_GET_TYPES, that generates the ABAP types the template needs; the document opens in Word or returns as an xstring.independent, abapABAP class filling a tagged Word .docx template with datafrom ABAP
- zcl_pdfzcl_pdfA standalone ABAP class that draws simple PDFs without Adobe Document Server: text in built-in fonts, text fitted into a box and rectangles, returned as a raw string to store, send or display.independent, abapStandalone ABAP class drawing PDFs without Adobe Document Serverfrom ABAP
- XTTXTTAn ABAP template engine that merges data, including nested structures, tables and trees, into Excel, Word, HTML and PDF templates prepared in ordinary editors with markers in curly brackets.independent, abapABAP template engine for Excel, Word, HTML and PDFfrom ABAP
- SAP Forms Service by AdobeSAP Forms Service by AdobeAdobe Document Services on BTP: renders print and interactive PDF forms from stored templates, called over a REST API from a cloud application or connected to an ABAP system.sap official, btpAdobe Document Services on BTP for print and interactive PDFsfrom BTP/BAIP
- SAP Print serviceSAP Print servicePrint queues between cloud applications and a customer's local printers: an application queries queues and creates print queue items through public APIs, and the queues are managed in the service.sap official, btpPrint queues between cloud applications and local printersfrom BTP/BAIP
- @cap-js/print@cap-js/printA CDS plugin that prints a document held in a CAP entity, through SAP Print service and a @PDF.Printable annotation.sap official, capPrints documents held in CAP entities through SAP Print servicefrom CAP
- open-abap-pdfopen-abap-pdfA standalone PDF generator written in ABAP that builds a multi-page document from text, lines, rectangles and circles placed at given coordinates and returns it as a string or an xstring.community org, abapStandalone PDF generator written in ABAPfrom ABAP
- @salvatorela/cap-plugin-adobe-forms@salvatorela/cap-plugin-adobe-formsA CAP plugin that wraps the SAP Forms service by Adobe on BTP as a CAP service, rendering a PDF from a form template with credentials from a destination or direct configuration.independent, capCAP plugin wrapping the SAP Forms service by Adobe on BTPfrom CAP
- SAP Spool PDFSAP Spool PDFAn ABAP class that submits a report to the spool, typically in the background, and converts the resulting spool request into a PDF it can return as an xstring, save, show or email.independent, abapSubmits an ABAP report to the spool and converts the output to PDFfrom ABAP
Details
Sources
Sources are graph entities: the same page may back several entries, and an unreachable source flags every row that rests on it.
Compatibility and provenance
abaplint
Contributors
History in the atlas
- First seen.
For your agent
- Ref
artifact:monstermichl-handlebarsabap- JSON
- /artifacts/monstermichl-handlebarsabap.json
- MCP
- Connecting an agent