Formatter
You can format values like currencies, numerics, dates, or booleans directly on the frontend with UI5 type formatters.
UI5 formatter types use a special JSON-based binding syntax with these key elements:
parts: [...]— lists the model paths used as input (e.g., amount + currency)type: '...'— the UI5 formatter type (e.g.,sap.ui.model.type.Currency)formatOptions: {...}— optional settings that control the output formatconstraints: {...}— optional constraints applied when user input is parsed back
Note on ABAP syntax: inside string templates (|...|), escape the curly braces as \{ and \}, because { } normally denotes an embedded ABAP expression.
The path = abap_true parameter on _bind returns only the raw model path rather than the full binding expression, so you can embed it inside the parts array or a single-path path: entry. The path is e.g. /AMOUNT, unquoted — so the quotes around it are yours to write, and they have to be ' or ". A backtick is an ABAP string delimiter, not a UI5 one: written here it survives into the binding string, and UI5 answers with a syntax error and renders nothing at all.
For example, this ABAP code:
|\{ parts: ['{ client->_bind( val = amount path = abap_true ) }'], type: 'sap.ui.model.type.Currency' \}|produces this UI5 binding string at runtime:
{ parts: ['/AMOUNT'], type: 'sap.ui.model.type.Currency' }The sections below show the binding-string pattern for each ABAP type that needs a formatter. Each pattern is the minimum that makes the value display and parse correctly — for runnable apps with full formatOptions, constraints, and read-only variants, see the samples repository.
Currency
ABAP p LENGTH n DECIMALS m + a c LENGTH 3 currency code (a plain string also works, as in the worked example below) → UI5 Currency formatter. Two parts entries; the type combines them into a locale-aware amount string:
)->tag( `Input`
)->a( n = `value` v = |\{ parts: [ '{ client->_bind( val = amount path = abap_true ) }',
'{ client->_bind( val = currency path = abap_true ) }' ],
type: 'sap.ui.model.type.Currency' \}|Common formatOptions:
showMeasure: false— hides the currency symbolshowNumber: false— hides the amount, shows only the symbolpreserveDecimals: false— trims trailing zeroscurrencyCode: false— hides the ISO codestyle: 'short'/'long'— compact (123M) or full-text (123 million US dollars) notation
The Full Worked Example below demonstrates each of these variants in a single app.
Digit Sequence
ABAP n LENGTH n is sent as a digit string, leading zeros included. Without a type the zeros render literally. Use the OData String type with isDigitSequence: true:
)->tag( `Text`
)->a( n = `text` v = |\{ path: '{ client->_bind( val = numeric path = abap_true ) }',
type: 'sap.ui.model.odata.type.String',
constraints: \{ isDigitSequence: true \} \}|This strips the leading zeros for display and re-pads them on write-back.
Date
ABAP d is an 8-character string YYYYMMDD. DatePicker accepts it directly via client->_bind( mv_date ) for the default case. For explicit locale or pattern control, use sap.ui.model.type.Date with a source pattern that matches the wire format:
)->tag( `DatePicker`
)->a( n = `value` v = |\{ path: '{ client->_bind( val = mv_date path = abap_true ) }',
type: 'sap.ui.model.type.Date',
formatOptions: \{ pattern: 'yyyy-MM-dd',
source: \{ pattern: 'yyyyMMdd' \} \} \}|source.pattern is the wire format (ABAP side); the outer pattern is what the user sees.
Time
ABAP t is a 6-character string HHMMSS. Same pattern as Date, with sap.ui.model.type.Time:
)->tag( `TimePicker`
)->a( n = `value` v = |\{ path: '{ client->_bind( val = mv_time path = abap_true ) }',
type: 'sap.ui.model.type.Time',
formatOptions: \{ pattern: 'HH:mm:ss',
source: \{ pattern: 'HHmmss' \} \} \}|Boolean
A boolean needs no formatter. abap_bool is "X" or "" in ABAP, but it does not travel that way: the framework serializes the boolean ABAP types to a JSON true / false and converts back on the way in. So bind the attribute straight to the control and both directions work:
)->tag( `CheckBox`
)->a( n = `selected` v = client->_bind( mv_flag ) )Inside a table row template, the same thing relative to the row:
)->tag( `CheckBox`
)->a( n = `selected` v = `{CHECKBOX}` )It is the TYPE that decides, not the value
The mapping is keyed on the ABAP type: abap_bool, abap_boolean, xsdboolean, flag and xfeld become a JSON boolean. A flag you declared as c LENGTH 1 looks identical in the debugger and travels as the string"X" — a CheckBox bound to it stays unchecked, because "X" is not true. Type the attribute abap_bool and the problem disappears; that is the fix, not an expression binding.
An expression binding ({= ${/MV_FLAG} === 'X' }) is the wrong tool here twice over: it compares against a value that is not on the wire, and an expression binding cannot write back, so the box would not flip the attribute even if the comparison held.
A boolean written into the view is a different question
All of the above is about a bound value. An ABAP boolean put straight into the XML as an attribute value — )->a( n = visible v = flag ) — is stringified, and UI5 reads any non-empty string as true, so abap_false renders the control visible. Use the builder's boolean parameter for that: )->a( n = visible b = flag ). The linter rule unconverted-abap-boolean catches it.
Timestamp
timestamp and timestampl are packed numbers with no built-in UI5 type that reads them directly. Two practical approaches:
Split in ABAP — break the timestamp into separate d and t fields before sending, bind each with the Date / Time formatter above, recombine after the event. Simplest when the UI shows date and time as separate fields anyway.
Send as string with a source pattern — convert to a string in yyyyMMddHHmmss format on the ABAP side, then bind with sap.ui.model.type.DateTime:
)->tag( `DateTimePicker`
)->a( n = `value` v = |\{ path: '{ client->_bind( val = mv_ts_string path = abap_true ) }',
type: 'sap.ui.model.type.DateTime',
formatOptions: \{ pattern: 'yyyy-MM-dd HH:mm:ss',
source: \{ pattern: 'yyyyMMddHHmmss' \} \} \}|Conversion happens in ABAP (WRITE timestamp TO ts_string … or a helper); the framework moves the string verbatim.
A custom JS formatter is the third option when neither fits.
The formatters abap2UI5 ships
A UI5 type covers the value property, which is a string. It does not cover a property that wants a JavaScript Date object — DatePicker.dateValue, PlanningCalendarAppointment.startDate — and JSON has no date type, so the model physically cannot carry one. For that, and for one text case, the framework ships a small curated formatter module. It is a public contract: z2ui5/model/formatter, also published as the z2ui5.Formatter global for releases without core:require.
Load it once on the view root, then name a helper in the binding string:
view->a( n = `core:require` v = `{Formatter: 'z2ui5/model/formatter'}` ).
...
)->tag( `DatePicker`
)->a( n = `dateValue` v = |\{ path: '{ client->_bind( val = mv_date path = abap_true ) }',
formatter: 'Formatter.DateAbapDateToDateObject' \}|| Helper | Takes | Returns |
|---|---|---|
DateAbapDateToDateObject | an ABAP d on the wire (YYYYMMDD) | a Date at midnight local time |
DateAbapDateTimeToDateObject | an ABAP d and t as two parts (the t may be omitted → midnight) | a Date with the time applied |
DateCreateObject | anything the JS Date constructor parses (an ISO string, utclong) | a Date |
expandInlineIcons | a formatted-text string carrying %%icon:sap-icon://<name>%% placeholders | the same text with the theme's icon glyphs inlined — for a MessageStrip text |
An initial or empty value yields null, never an Invalid Date. That matters more than it sounds: an Invalid Date is truthy, so a control accepts it and fails much later — a sap.ui.unified calendar throws for every rendered day and takes the whole view down. null is what "no date" means to a UI5 date property, so an optional date field in a bound row stays empty instead.
The module grows through framework pull requests only; a helper of your own goes in your own module and is required the same way. See Z2UI5_CL_SMP_APP_457 (DatePicker) and Z2UI5_CL_SMP_APP_456 (PlanningCalendar) in the samples repository.
Full Worked Example
The class below combines the Currency and Digit Sequence patterns in one app and demonstrates every formatOptions variant listed under Currency:
CLASS z2ui5_cl_smp_app_067 DEFINITION PUBLIC.
PUBLIC SECTION.
INTERFACES z2ui5_if_app.
DATA amount TYPE p LENGTH 14 DECIMALS 3.
DATA currency TYPE string.
DATA numeric TYPE n LENGTH 12.
DATA check_initialized TYPE abap_bool.
PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.
CLASS z2ui5_cl_smp_app_067 IMPLEMENTATION.
METHOD z2ui5_if_app~main.
IF check_initialized = abap_false.
check_initialized = abap_true.
numeric = `000000000012`.
amount = `123456789.123`.
currency = `USD`.
ENDIF.
CASE client->get( )-event.
WHEN |BACK|.
client->nav_app_leave( client->get_app( client->get( )-s_draft-id_prev_app_stack ) ).
WHEN |BUTTON|.
" the roundtrip is the point of this button: the edited values travel
" to the server and come back, and every formatter above renders them
" again from the model
client->message_toast_display( |Amount { amount }, currency { currency }| ).
ENDCASE.
DATA(view) = z2ui5_cl_ui5_view_builder=>factory(
)->ele( n = `View` ns = `mvc`
)->a( n = `xmlns` v = `sap.m`
)->a( n = `xmlns:mvc` v = `sap.ui.core.mvc`
)->a( n = `xmlns:form` v = `sap.ui.layout.form`
)->ele( `Shell`
)->ele( `Page`
)->a( n = `title` v = `abap2UI5 - Currency Format`
)->a( n = `navButtonPress` v = client->_event( |BACK| )
)->a( n = `showNavButton` b = xsdbool( client->get( )-s_draft-id_prev_app_stack IS NOT INITIAL )
)->ele( n = `SimpleForm` ns = `form`
)->a( n = `title` v = `Currency`
)->a( n = `editable` b = abap_true
)->ele( n = `content` ns = `form`
)->tag( `Title`
)->a( n = `text` v = `Input`
)->tag( `Label`
)->a( n = `text` v = `Documentation`
)->tag( `Link`
)->a( n = `text` v = `https://sdk.openui5.org/api/sap.ui.model.type.Currency`
)->a( n = `href` v = `https://sdk.openui5.org/api/sap.ui.model.type.Currency`
)->tag( `Label`
)->a( n = `text` v = `One field`
)->tag( `Input`
)->a( n = `value` v = |\{ parts: [ '{ client->_bind( val = amount
path = abap_true ) }', '{ client->_bind( val = currency
path = abap_true ) }'], type: 'sap.ui.model.type.Currency' \}|
)->tag( `Label`
)->a( n = `text` v = `Two fields`
)->tag( `Input`
)->a( n = `value` v = |\{ parts: [ '{ client->_bind( val = amount
path = abap_true ) }', '{ client->_bind( val = currency
path = abap_true ) }'], type: 'sap.ui.model.type.Currency', formatOptions: \{showMeasure: false\} \}|
)->tag( `Label`
)->a( n = `text` v = `Two fields`
)->tag( `Input`
)->a( n = `value` v = |\{ parts: [ '{ client->_bind( val = amount
path = abap_true ) }', '{ client->_bind( val = currency
path = abap_true ) }'], type: 'sap.ui.model.type.Currency', formatOptions: \{showNumber: false\} \}|
)->tag( `Label`
)->a( n = `text` v = `Default`
)->tag( `Text`
)->a( n = `text` v = |\{ parts: [ '{ client->_bind( val = amount
path = abap_true ) }', '{ client->_bind( val = currency
path = abap_true ) }'], type: 'sap.ui.model.type.Currency' \}|
)->tag( `Label`
)->a( n = `text` v = `preserveDecimals:false`
)->tag( `Text`
)->a( n = `text` v = |\{ parts: [ '{ client->_bind( val = amount
path = abap_true ) }', '{ client->_bind( val = currency
path = abap_true ) }'], type: 'sap.ui.model.type.Currency', formatOptions: \{ preserveDecimals : false \} \}|
)->tag( `Label`
)->a( n = `text` v = `currencyCode:false`
)->tag( `Text`
)->a( n = `text` v = |\{ parts: [ '{ client->_bind( val = amount
path = abap_true ) }', '{ client->_bind( val = currency
path = abap_true ) }'], type: 'sap.ui.model.type.Currency', formatOptions: \{ currencyCode : false \} \}|
)->tag( `Label`
)->a( n = `text` v = `style:'short'`
)->tag( `Text`
)->a( n = `text` v = |\{ parts: [ '{ client->_bind( val = amount
path = abap_true ) }', '{ client->_bind( val = currency
path = abap_true ) }'], type: 'sap.ui.model.type.Currency', formatOptions: \{ style : 'short' \} \}|
)->tag( `Label`
)->a( n = `text` v = `style:'long'`
)->tag( `Text`
)->a( n = `text` v = |\{ parts: [ '{ client->_bind( val = amount
path = abap_true ) }', '{ client->_bind( val = currency
path = abap_true ) }'], type: 'sap.ui.model.type.Currency', formatOptions: \{ style : 'long' \} \}|
)->tag( `Label`
)->a( n = `text` v = `event`
)->tag( `Button`
)->a( n = `text` v = `send`
)->a( n = `press` v = client->_event( `BUTTON` )
)->end(
)->end(
" Remove leading zeros from a numeric string with OData type formatting.
" isDigitSequence: true tells the formatter to treat the value as a digit
" sequence — resolves to: { path: "/NUMERIC",
" type: 'sap.ui.model.odata.type.String',
" constraints: { isDigitSequence: true } }
)->ele( n = `SimpleForm` ns = `form`
)->a( n = `title` v = `No Zeros`
)->a( n = `editable` b = abap_true
)->ele( n = `content` ns = `form`
)->tag( `Title`
)->a( n = `text` v = `Input`
)->tag( `Label`
)->a( n = `text` v = `Documentation`
)->tag( `Link`
)->a( n = `text` v = `https://sdk.openui5.org/api/sap.ui.model.odata.type.String%23methods/formatValue`
)->a( n = `href` v = `https://sdk.openui5.org/api/sap.ui.model.odata.type.String%23methods/formatValue`
)->tag( `Label`
)->a( n = `text` v = `Numeric`
)->tag( `Input`
)->a( n = `value` v = client->_bind( val = numeric )
)->tag( `Label`
)->a( n = `text` v = `Without leading Zeros`
)->tag( `Text`
)->a( n = `text` v = |\{ path : '{ client->_bind( val = numeric
path = abap_true ) }', type : 'sap.ui.model.odata.type.String', constraints : \{ isDigitSequence : true \} \}| ).
client->view_display( view->stringify( ) ).
ENDMETHOD.
ENDCLASS.For a full runnable copy, see the sample implementation in class Z2UI5_CL_SMP_APP_067 in the samples repository.
Working Samples
Complete apps from the sample catalogue that use what this page describes. Each is a single class — pull the repository with abapGit and start it with ?app_start=<class>.
| Sample | Class |
|---|---|
| Currency Amounts (sap.ui.model.type.Currency) | Z2UI5_CL_SMP_APP_067 |
| ABAP Date and Time Strings (DATS/TIMS) | Z2UI5_CL_SMP_APP_450 |
| Date Object for the DatePicker | Z2UI5_CL_SMP_APP_457 |
| Date Objects for the PlanningCalendar | Z2UI5_CL_SMP_APP_456 |
| Inline Icons in a Text | Z2UI5_CL_SMP_APP_466 |
| When Not to Use One: Compute in ABAP | Z2UI5_CL_SMP_APP_453 |
