Single-file 7.40 SP02 Handlebars implementation for ABAP.
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.
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.
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'.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.
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.
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}}' ).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.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.
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.
- 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.
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' ) ).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.
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' ) ).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' ) ).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' ) ).