Samples / Learn / Message

Message

Every shape message_box_display( ) accepts - a text, a number, HTML, messages, a table, a structure, an object - the ABAP side of a message box.

Class
z2ui5_cl_smp_app_502
Repository
abap2UI5/samples
UI5 libraries
sap.m, sap.ui.core, sap.ui.layout
Lowest UI5 release
1.71
In the browser
Runs in the playground
Controls it builds
sap.m.Button, sap.m.HBox, sap.m.Label, sap.m.MessageStrip, sap.m.Page, sap.m.Shell, sap.m.Text, sap.m.Title, sap.m.Toolbar, sap.ui.core.mvc.View, sap.ui.layout.form.SimpleForm
Documentation
cookbook/translation_messages/logging ↗, cookbook/translation_messages/message ↗

The class

" @keywords messagebox details table structure tree object reference escape limit action onclose message_box_display
" @summary Every shape message_box_display( ) accepts - a text, a number, HTML, messages, a table, a structure, an object - the ABAP side of a message box.
" @docs https://abap2ui5.github.io/docs/cookbook/translation_messages/logging https://abap2ui5.github.io/docs/cookbook/translation_messages/message
CLASS z2ui5_cl_smp_app_502 DEFINITION PUBLIC.

  PUBLIC SECTION.
    INTERFACES z2ui5_if_app.

    DATA answer TYPE string.

  PROTECTED SECTION.
    TYPES:
      BEGIN OF ty_s_message,
        type    TYPE c LENGTH 1,
        id      TYPE string,
        number  TYPE n LENGTH 3,
        message TYPE string,
      END OF ty_s_message.
    TYPES ty_t_message TYPE STANDARD TABLE OF ty_s_message WITH EMPTY KEY.
    TYPES:
      BEGIN OF ty_s_address,
        street  TYPE string,
        city    TYPE string,
        country TYPE string,
      END OF ty_s_address.
    TYPES:
      BEGIN OF ty_s_item,
        posnr TYPE n LENGTH 6,
        matnr TYPE string,
        menge TYPE i,
        netwr TYPE p LENGTH 9 DECIMALS 2,
        waers TYPE c LENGTH 3,
      END OF ty_s_item.
    TYPES:
      BEGIN OF ty_s_order,
        vbeln     TYPE string,
        kunnr     TYPE string,
        erdat     TYPE d,
        s_address TYPE ty_s_address,
        t_item    TYPE STANDARD TABLE OF ty_s_item WITH EMPTY KEY,
      END OF ty_s_order.
    TYPES:
      BEGIN OF ty_s_node,
        name  TYPE string,
        child TYPE REF TO data,
      END OF ty_s_node.
    " The key column is NOT called `id`: the message formatter runs before the
    " data renderer and reads a component of that name as a message id, which
    " would turn this business table into a box of messages that do not exist.
    " The same holds for TYPE, NUMBER, MESSAGE, TEXT and the MSG* spellings.
    TYPES:
      BEGIN OF ty_s_row,
        key   TYPE string,
        descr TYPE string,
      END OF ty_s_row.
    TYPES ty_t_row TYPE STANDARD TABLE OF ty_s_row WITH EMPTY KEY.

    DATA client TYPE REF TO z2ui5_if_client.

    METHODS on_event.
    METHODS on_event_basic
      IMPORTING
        event         TYPE clike
      RETURNING
        VALUE(result) TYPE abap_bool.
    METHODS on_event_message
      IMPORTING
        event         TYPE clike
      RETURNING
        VALUE(result) TYPE abap_bool.
    METHODS on_event_data
      IMPORTING
        event         TYPE clike
      RETURNING
        VALUE(result) TYPE abap_bool.
    METHODS on_event_box
      IMPORTING
        event TYPE clike.

    METHODS view_display.
    METHODS render_basic
      IMPORTING form TYPE REF TO z2ui5_cl_ui5_view_builder.
    METHODS render_message
      IMPORTING form TYPE REF TO z2ui5_cl_ui5_view_builder.
    METHODS render_data
      IMPORTING form TYPE REF TO z2ui5_cl_ui5_view_builder.
    METHODS render_limit
      IMPORTING form TYPE REF TO z2ui5_cl_ui5_view_builder.
    METHODS render_box
      IMPORTING form TYPE REF TO z2ui5_cl_ui5_view_builder.
    METHODS render_section
      IMPORTING
        form  TYPE REF TO z2ui5_cl_ui5_view_builder
        title TYPE string.
    METHODS render_demo
      IMPORTING
        form  TYPE REF TO z2ui5_cl_ui5_view_builder
        label TYPE string
        text  TYPE string
        descr TYPE string
        press TYPE string.

    METHODS get_s_order
      RETURNING
        VALUE(result) TYPE ty_s_order.
    METHODS get_t_row
      IMPORTING
        rows          TYPE i
      RETURNING
        VALUE(result) TYPE ty_t_row.
    METHODS get_tree
      RETURNING
        VALUE(result) TYPE REF TO data.

  PRIVATE SECTION.
ENDCLASS.


CLASS z2ui5_cl_smp_app_502 IMPLEMENTATION.


  METHOD z2ui5_if_app~main.

    me->client = client.
    IF client->check_on_navigated( ).
      view_display( ).

    ELSEIF client->check_on_event( ).
      on_event( ).
    ENDIF.

  ENDMETHOD.


  METHOD on_event.

    DATA(event) = client->get_event( ).
    IF on_event_basic( event ) = abap_true.
      RETURN.
    ENDIF.

    IF on_event_message( event ) = abap_true.
      RETURN.
    ENDIF.

    IF on_event_data( event ) = abap_true.
      RETURN.
    ENDIF.

    on_event_box( event ).

  ENDMETHOD.


  METHOD on_event_basic.

    result = abap_true.

    CASE event.

      WHEN `TEXT`.
        " a character value is its own text - the shape the method always had
        client->message_box_display( `The document was saved.` ).

      WHEN `NUMBER`.
        " a number, a date, a hex value: shown the way the runtime writes it,
        " trimmed - the app formats nothing
        DATA(count) = 42.
        client->message_box_display( count ).

      WHEN `HTML`.
        " markup moves into the details, where UI5 renders it through a
        " FormattedText; the plain text behind it stays as the box text. Only
        " ul/ol/li/strong/em survive there - that is what the control keeps
        client->message_box_display(
            `<strong>Three checks failed:</strong><ul><li>No plant</li><li>No price</li><li>No route</li></ul>` ).

      WHEN `ESCAPE`.
        " every value the renderer writes is escaped, so business data that
        " looks like markup arrives as the text it is
        client->message_box_display( get_t_row( 2 ) ).

      WHEN OTHERS.
        result = abap_false.

    ENDCASE.

  ENDMETHOD.


  METHOD on_event_message.

    DATA t_empty TYPE ty_t_message.

    result = abap_true.

    " the message shapes themselves - one SY message, a BAPIRET2 structure, a
    " caught exception - are Z2UI5_CL_SMP_APP_008; a message TABLE is the
    " DATA_TABLE case of this app, so only the one silent case is left here
    CASE event.

      WHEN `MSG_EMPTY`.
        " the one case that shows NOTHING at all: complex data that is
        " initial. A call over the result table of something that returned
        " nothing stays as silent as it always was
        client->message_box_display( t_empty ).
        client->message_toast_display( `No box - the message table was empty` ).

      WHEN OTHERS.
        result = abap_false.

    ENDCASE.

  ENDMETHOD.


  METHOD on_event_data.

    DATA s_order TYPE ty_s_order.

    FIELD-SYMBOLS <s_tree> TYPE ty_s_node.

    result = abap_true.

    CASE event.

      WHEN `DATA_TABLE`.
        " a business table: a counting headline in the box, the rows as a
        " numbered list below it
        s_order = get_s_order( ).
        client->message_box_display( s_order-t_item ).

      WHEN `DATA_STRUCTURE`.
        " a nested structure: field names in bold, the nested structure and
        " the nested table as lists of their own
        s_order = get_s_order( ).
        client->message_box_display( s_order ).

      WHEN `DATA_OBJECT`.
        " an object shows its PUBLIC instance state - here the running app
        " itself, which is why its client reference is protected: a public one
        " would arrive in this box as part of the app
        client->message_box_display( me ).

      WHEN `DATA_REFERENCE`.
        " a data reference is followed and what it points at is rendered
        s_order = get_s_order( ).
        DATA(address) = REF #( s_order-s_address ).
        client->message_box_display( address ).

      WHEN `LIMIT_ROWS`.
        " 120 rows against a limit of 100: the box renders the first hundred
        " and SAYS how many it left out instead of truncating silently
        client->message_box_display( get_t_row( 120 ) ).

      WHEN `LIMIT_DEPTH`.
        " a node that points at the next one, six levels deep. The renderer
        " stops at five and writes an ellipsis where it stopped - without the
        " limit a structure that points at itself would never end
        DATA(tree) = get_tree( ).
        " IS ASSIGNED, not sy-subrc: a SUCCESSFUL dynamic ASSIGN does not reset
        " sy-subrc on every release (abap2UI5 #1937)
        ASSIGN tree->* TO <s_tree>.
        IF <s_tree> IS ASSIGNED.
          client->message_box_display( <s_tree> ).
        ENDIF.

      WHEN OTHERS.
        result = abap_false.

    ENDCASE.

  ENDMETHOD.


  METHOD on_event_box.

    DATA s_order TYPE ty_s_order.

    CASE event.

      WHEN `BOX_TYPE`.
        " the severity, when the data does not bring one: information (the
        " default, mapped to MessageBox.show), success, warning, error. The
        " wire carries the value as the event argument, so one event and one
        " handler serve all four buttons
        DATA(type) = client->get_event_arg( ).
        client->message_box_display( text = |This box was opened with type = { type }|
                                     type = type ).

      WHEN `BOX_OPTIONS`.
        " what the method itself carries: the kind of box and its title - the
        " two decisions an ABAP app makes about a message. A PLAIN
        " sap.m.MessageBox option ( icon, contentWidth, textDirection,
        " closeOnNavigation, dependentOn ) has no parameter here: it belongs
        " to the control, and Z2UI5_CL_SMP_APP_512 sets exactly these on the
        " very same box through the global object
        client->message_box_display( text       = `The delivery date lies in the past.`
                                     type       = `warning`
                                     title      = `Please check`
                                     styleclass = `sapUiSizeCompact` ).

      WHEN `BOX_ACTIONS`.
        " the buttons of the box, which one is emphasized and which one has
        " the focus - and onclose, the event that carries the pressed action
        " back as the first event argument. An action is a MessageBox.Action
        " name or, like `Later`, any text the app wants on a button
        client->message_box_display( text             = `Delete document 4711?`
                                     type             = `warning`
                                     title            = `Delete`
                                     actions          = VALUE #( ( `DELETE` )
                                                                 ( `Later` )
                                                                 ( `CANCEL` ) )
                                     emphasizedaction = `DELETE`
                                     initialfocus     = `CANCEL`
                                     onclose          = `BOX_CLOSED` ).

      WHEN `BOX_CLOSED`.
        " the answer of the box above. Nothing is rendered here: `answer` is
        " bound, and changed bound data reaches the open view on its own
        answer = client->get_event_arg( ).
        client->message_toast_display( |You pressed { answer }| ).

      WHEN `BOX_DETAILS`.
        " details the app writes itself - the second block of the box. UI5
        " puts them behind a "Show details" link; abap2UI5 opens that block,
        " so what is in here is on screen when the box is
        client->message_box_display(
            text    = `The posting was rejected.`
            details = `<ul><li>Company code 1000 is closed</li><li>Period 08 is not open</li></ul>` ).

      WHEN `BOX_DETAILS_TAKEN`.
        " ... and the rule about them: details never overwrite a rendering.
        " The table below fills that slot, so what the app passes here is
        " dropped rather than hiding the data it asked to see
        s_order = get_s_order( ).
        client->message_box_display( text    = s_order-t_item
                                     details = `this text is not shown - the table owns the details` ).

    ENDCASE.

  ENDMETHOD.


  METHOD view_display.

    DATA(view) = z2ui5_cl_ui5_view_builder=>factory(
        )->ele( n = `View` ns = `mvc`
            )->a( n = `displayBlock` v = `true`
            )->a( n = `height`       v = `100%`
            )->a( n = `xmlns`        v = `sap.m`
            )->a( n = `xmlns:mvc`    v = `sap.ui.core.mvc`
            )->a( n = `xmlns:form`   v = `sap.ui.layout.form` ).

    DATA(page) = view->ele( `Shell`
        )->ele( `Page`
            )->a( n = `title`          v = `abap2UI5 - Message - MessageBox for Any Data`
            )->a( n = `showNavButton`  b = client->check_app_prev_stack( )
            )->a( n = `navButtonPress` v = client->_event_nav_app_leave( ) ).

    page->tag( `MessageStrip`
        )->a( n = `text`     v = `client->message_box_display( ) takes TYPE any: throw in what the app already holds. ` &&
                   `Messages are recognized first and bring their own severity and title; everything else - a table, ` &&
                   `a structure, a tree, an object, a number, an HTML string - is rendered instead of dropped. ` &&
                   `One button per case, and each button is one call: the app pre-formats nothing. That is what this ` &&
                   `method is for; a pure sap.m.MessageBox option is set on the control instead - ` &&
                   `Z2UI5_CL_SMP_APP_512 is the same box, steered through the global object.`
        )->a( n = `type`     v = `Information`
        )->a( n = `showIcon` b = abap_true
        )->a( n = `class`    v = `sapUiSmallMargin` ).

    DATA(form) = page->ele( n = `SimpleForm` ns = `form`
        )->a( n = `editable`        b = abap_true
        )->a( n = `layout`          v = `ResponsiveGridLayout`
        )->a( n = `labelSpanXL`     v = `2`
        )->a( n = `labelSpanL`      v = `2`
        )->a( n = `labelSpanM`      v = `3`
        )->a( n = `labelSpanS`      v = `12`
        )->a( n = `adjustLabelSpan` v = `false`
        )->a( n = `columnsXL`       v = `1`
        )->a( n = `columnsL`        v = `1`
        )->a( n = `columnsM`        v = `1`
        )->ele( n = `content` ns = `form` ).

    render_basic( form ).
    render_message( form ).
    render_data( form ).
    render_limit( form ).
    render_box( form ).

    client->view_display( view->stringify( ) ).

  ENDMETHOD.


  METHOD render_basic.

    render_section( form  = form
                    title = `A text, a number, HTML` ).

    render_demo( form  = form
                 label = `Text`
                 text  = `A text`
                 descr = `A character value is its own text - the shape the method always had`
                 press = client->_event( `TEXT` ) ).

    render_demo( form  = form
                 label = `Number`
                 text  = `A number`
                 descr = `A number, a date, a hex value - shown the way the runtime writes it`
                 press = client->_event( `NUMBER` ) ).

    render_demo( form  = form
                 label = `HTML`
                 text  = `An HTML string`
                 descr = `Markup moves into the details, where UI5 renders it - the plain text stays in the box`
                 press = client->_event( `HTML` ) ).

    render_demo( form  = form
                 label = `Escaping`
                 text  = `Data that looks like HTML`
                 descr = `Every rendered value is escaped, so a value containing tags arrives as text`
                 press = client->_event( `ESCAPE` ) ).

  ENDMETHOD.


  METHOD render_message.

    render_section( form  = form
                    title = `Messages - recognized first, and they bring their own severity (SY, BAPIRET2 and exception: Z2UI5_CL_SMP_APP_008)` ).

    render_demo( form  = form
                 label = `Nothing`
                 text  = `An empty message table`
                 descr = `The one case that shows no box at all - a call that returned nothing stays silent`
                 press = client->_event( `MSG_EMPTY` ) ).

  ENDMETHOD.


  METHOD render_data.

    render_section( form  = form
                    title = `Any other data - rendered instead of dropped` ).

    render_demo( form  = form
                 label = `Table`
                 text  = `A business table`
                 descr = `A counting headline in the box, the rows as a numbered list below it`
                 press = client->_event( `DATA_TABLE` ) ).

    render_demo( form  = form
                 label = `Structure`
                 text  = `A nested structure`
                 descr = `Field names in bold; a nested structure and a nested table become lists of their own`
                 press = client->_event( `DATA_STRUCTURE` ) ).

    render_demo( form  = form
                 label = `Object`
                 text  = `An object reference`
                 descr = `The public instance state of the object - here the running app itself`
                 press = client->_event( `DATA_OBJECT` ) ).

    render_demo( form  = form
                 label = `Data reference`
                 text  = `A REF TO data`
                 descr = `The reference is followed and what it points at is rendered`
                 press = client->_event( `DATA_REFERENCE` ) ).

  ENDMETHOD.


  METHOD render_limit.

    render_section( form  = form
                    title = `Where the renderer stops - announced, never silent` ).

    render_demo( form  = form
                 label = `Row limit`
                 text  = `120 rows, 100 shown`
                 descr = `The box renders the first hundred and says how many it left out`
                 press = client->_event( `LIMIT_ROWS` ) ).

    render_demo( form  = form
                 label = `Depth limit`
                 text  = `A tree, six levels deep`
                 descr = `Five levels are rendered, an ellipsis marks where it stopped`
                 press = client->_event( `LIMIT_DEPTH` ) ).

  ENDMETHOD.


  METHOD render_box.

    render_section( form  = form
                    title = `The box itself` ).

    form->tag( `Label`
        )->a( n = `text` v = `Type` ).

    DATA(row) = form->ele( `HBox`
        )->a( n = `alignItems` v = `Center`
        )->a( n = `wrap`       v = `Wrap` ).

    DATA(t_type) = VALUE string_table( ( `information` )
                                       ( `success` )
                                       ( `warning` )
                                       ( `error` ) ).

    LOOP AT t_type INTO DATA(type).
      row->tag( `Button`
          )->a( n = `text`  v = type
          )->a( n = `press` v = client->_event( val = `BOX_TYPE`
                                                arg = type )
          )->a( n = `class` v = `sapUiTinyMarginEnd` ).
    ENDLOOP.

    row->tag( `Text`
        )->a( n = `text`  v = `The severity, when the data does not bring one - information is the default`
        )->a( n = `class` v = `sapUiSmallMarginBegin` ).

    render_demo( form  = form
                 label = `Options`
                 text  = `type, title, class`
                 descr = `What an ABAP app decides - a pure UI5 option is set on the control, see Z2UI5_CL_SMP_APP_512`
                 press = client->_event( `BOX_OPTIONS` ) ).

    render_demo( form  = form
                 label = `Actions`
                 text  = `Buttons and onclose`
                 descr = `actions, emphasizedAction, initialFocus - the pressed one comes back through onclose`
                 press = client->_event( `BOX_ACTIONS` ) ).

    form->tag( `Label`
        )->a( n = `text` v = `Your answer` ).

    form->tag( `Text`
        )->a( n = `text` v = client->_bind( answer ) ).

    render_demo( form  = form
                 label = `Details`
                 text  = `Details from the app`
                 descr = `The second block of the box - shown straight away, not behind a link`
                 press = client->_event( `BOX_DETAILS` ) ).

    render_demo( form  = form
                 label = `Details, taken`
                 text  = `A table plus details`
                 descr = `Details never overwrite a rendering - the table owns the slot, the text is dropped`
                 press = client->_event( `BOX_DETAILS_TAKEN` ) ).

  ENDMETHOD.


  METHOD render_section.

    form->ele( `Toolbar`
        )->tag( `Title`
            )->a( n = `text`  t = title
            )->a( n = `level` v = `H3`
            )->a( n = `class` v = `sapUiSmallMarginBegin sapUiSmallMarginTop sapUiTinyMarginBottom`
    )->end( ).

  ENDMETHOD.


  METHOD render_demo.

    form->tag( `Label`
        )->a( n = `text` t = label ).

    DATA(row) = form->ele( `HBox`
        )->a( n = `alignItems` v = `Center`
        )->a( n = `wrap`       v = `Wrap` ).

    row->tag( `Button`
        )->a( n = `text`  t = text
        )->a( n = `press` v = press
        )->a( n = `width` v = `15rem` ).

    row->tag( `Text`
        )->a( n = `text`  t = descr
        )->a( n = `class` v = `sapUiSmallMarginBegin` ).

  ENDMETHOD.


  METHOD get_s_order.

    result = VALUE #( vbeln     = `0000004711`
                      kunnr     = `0000001000`
                      erdat     = `20260904`
                      s_address = VALUE #( street  = `Dietmar-Hopp-Allee 16`
                                           city    = `Walldorf`
                                           country = `DE` )
                      t_item    = VALUE #( ( posnr = `000010` matnr = `TG-11` menge = 5  netwr = '249.90'  waers = `EUR` )
                                           ( posnr = `000020` matnr = `TG-12` menge = 2  netwr = '1199.00' waers = `EUR` )
                                           ( posnr = `000030` matnr = `TG-13` menge = 12 netwr = '58.50'   waers = `EUR` ) ) ).

  ENDMETHOD.


  METHOD get_t_row.

    DO rows TIMES.
      " values that look like markup on purpose: this table is what the
      " escaping row shows as well
      INSERT VALUE #( key   = |ROW-{ sy-index }|
                      descr = |<b>Position { sy-index }</b> of a result set nobody reads in a popup & nowhere else| )
             INTO TABLE result.
    ENDDO.

  ENDMETHOD.


  METHOD get_tree.

    DATA child TYPE REF TO data.

    FIELD-SYMBOLS <s_node> TYPE ty_s_node.

    " built from the bottom up, so every node holds the one below it
    DO 6 TIMES.
      CREATE DATA result TYPE ty_s_node.
      " UNASSIGN before the ASSIGN because this is a loop: a failed assign
      " leaves the previous round's binding in place, and IS ASSIGNED would
      " then read TRUE for the failure and build onto the WRONG node
      UNASSIGN <s_node>.
      ASSIGN result->* TO <s_node>.
      IF <s_node> IS NOT ASSIGNED.
        RETURN.
      ENDIF.

      <s_node>-name  = |Level { 7 - sy-index }|.
      <s_node>-child = child.
      child = result.
    ENDDO.

  ENDMETHOD.

ENDCLASS.