abap2UI5

Message Popover - Message Handling

The message handling concept sample shows how you can connect an error inside the page (such as input validation error) with an error, visualized as an item in a message popover.

Z2UI5_CL_SMPC_APP_065

Controls sap.m UI5 1.84

The facts

Repository
abap2UI5/samples-controls
Class
Z2UI5_CL_SMPC_APP_065
Source file
z2ui5_cl_smpc_app_065.clas.abap
Library
sap.m
UI5 entity
sap.m.MessagePopover
Demo kit sample
sap.m.sample.MessagePopoverMessageHandling
SAP's own sample
running at sdk.openui5.org ↗
Minimum UI5
1.84
In the playground
runs in the browser, with no system and nothing installed

Keywords: messagepopover, message, popover, sap.m, handling, vbox, simpleform, title, label, input, select, item

Controls it builds

Read out of the builder chain by the abap2UI5 linter, not from the category this sample is filed under — which is what makes “which samples build one of these” a question the catalogue can answer at all.

Libraries

Run it here

The sample running, in this page — the app on its own, with no editor over it. Switch to Playground with this code

Nothing is installed and nothing is sent anywhere: the ABAP is compiled to JavaScript in this browser and runs against abap2UI5 itself.

The ABAP

z2ui5_cl_smpc_app_065.clas.abap — 375 lines Read it on GitHub ↗
" @keywords messagepopover message popover sap.m handling vbox simpleform title label input select item
" @summary The message handling concept sample shows how you can connect an error inside the page (such as input validation error) with an error, visualized as an item in a message popover.
CLASS z2ui5_cl_smpc_app_065 DEFINITION PUBLIC.

  PUBLIC SECTION.
    INTERFACES z2ui5_if_app.

    TYPES:
      BEGIN OF ty_s_form,
        name          TYPE string,
        street_name   TYPE string,
        street_number TYPE string,
        zip_code      TYPE string,
        zip_city      TYPE string,
        country       TYPE string,
        email         TYPE string,
        phone_number  TYPE string,
        phone_time    TYPE string,
        website       TYPE string,
      END OF ty_s_form.

    TYPES:
      BEGIN OF ty_s_employment,
        jobtitle    TYPE string,
        paygrade    TYPE string,
        weeklyhours TYPE string,
        unit        TYPE string,
        class       TYPE string,
        fte         TYPE string,
      END OF ty_s_employment.

    TYPES:
      BEGIN OF ty_s_message,
        message        TYPE string,
        description    TYPE string,
        type           TYPE string,
        target         TYPE string,
        additionaltext TYPE string,
        code           TYPE string,
      END OF ty_s_message.

    DATA t_forms      TYPE STANDARD TABLE OF ty_s_form WITH EMPTY KEY.
    DATA t_employment TYPE STANDARD TABLE OF ty_s_employment WITH EMPTY KEY.
    DATA t_messages   TYPE STANDARD TABLE OF ty_s_message WITH EMPTY KEY.

  PROTECTED SECTION.
    DATA client TYPE REF TO z2ui5_if_client.

    METHODS view_display.
    METHODS on_event.
    METHODS model_init.

  PRIVATE SECTION.
ENDCLASS.


CLASS z2ui5_cl_smpc_app_065 IMPLEMENTATION.

  METHOD z2ui5_if_app~main.

    me->client = client.
    IF client->check_on_init( ).
      model_init( ).
      view_display( ).
      " original onInit: MessageToast.show('Press "Save" to trigger validation.')
      client->message_toast_display( `Press "Save" to trigger validation.` ).
    ELSEIF client->check_on_navigated( ).
      view_display( ).
    ELSEIF client->check_on_event( ).
      on_event( ).
    ENDIF.

  ENDMETHOD.


  METHOD view_display.

    DATA(view) = z2ui5_cl_ui5_view_builder=>factory( ).

    view->ele( n = `View` ns = `mvc`
        )->a( n = `height`      v = `100%`
        )->a( n = `xmlns`       v = `sap.m`
        )->a( n = `xmlns:mvc`   v = `sap.ui.core.mvc`
        )->a( n = `xmlns:core`  v = `sap.ui.core`
        )->a( n = `xmlns:f`     v = `sap.ui.layout.form`
        )->a( n = `xmlns:z2ui5` v = `z2ui5.cc`

        )->ele( `Page`
            )->a( n = `id`         v = `messageHandlingPage`
            )->a( n = `showHeader` v = `false`

            )->ele( `content`

                )->ele( `VBox`
                    )->a( n = `id`    v = `formContainer`
                    )->a( n = `class` v = `sapUiSmallMargin`
                    )->a( n = `items` v = client->_bind( t_forms )

                    )->ele( n = `SimpleForm` ns = `f`
                        )->a( n = `editable` v = `true`
                        )->a( n = `layout`   v = `ColumnLayout`
                        )->a( n = `title`    v = `Personal`
                        )->a( n = `columnsM` v = `2`
                        )->a( n = `columnsL` v = `2`
                        )->a( n = `columnsXL` v = `2`

                        )->ele( n = `content` ns = `f`
                            )->tag( n = `Title` ns = `core`
                                )->a( n = `text` v = `Information`
                            )->tag( `Label`
                                )->a( n = `text` v = `Name`
                            )->tag( `Input`
                                )->a( n = `required` v = `true`
                                )->a( n = `value`    v = `{ path: 'NAME', type: 'sap.ui.model.type.String' }`
                                )->a( n = `change`   v = client->_event( `CHANGE` )
                            )->tag( `Label`
                                )->a( n = `text` v = `Street/No.`
                            )->tag( `Input`
                                )->a( n = `value` v = `{STREET_NAME}`
                            )->tag( `Input`
                                )->a( n = `value` v = `{STREET_NUMBER}`
                            )->tag( `Label`
                                )->a( n = `text` v = `ZIP Code/City`
                            )->tag( `Input`
                                )->a( n = `value` v = `{ path: 'ZIP_CODE', type: 'sap.ui.model.type.Integer' }`
                            )->tag( `Input`
                                )->a( n = `value` v = `{ZIP_CITY}`
                            )->tag( `Label`
                                )->a( n = `text` v = `Country`
                            )->ele( `Select`
                                )->a( n = `selectedKey` v = `{COUNTRY}`
                                )->tag( n = `Item` ns = `core`
                                    )->a( n = `key`  v = `England`
                                    )->a( n = `text` v = `England`
                                )->tag( n = `Item` ns = `core`
                                    )->a( n = `key`  v = `Germany`
                                    )->a( n = `text` v = `Germany`
                                )->tag( n = `Item` ns = `core`
                                    )->a( n = `key`  v = `USA`
                                    )->a( n = `text` v = `USA`

                            )->end(
                            )->tag( n = `Title` ns = `core`
                                )->a( n = `text` v = `Contact`
                            )->tag( `Label`
                                )->a( n = `text` v = `Email`
                            )->tag( `Input`
                                )->a( n = `value` v = `{ path: 'EMAIL', type: 'sap.ui.model.type.String', constraints: { search: '^\\w+[\\w-+\\.]*\\@[a-zA-Z]+.[a-zA-Z]+' } }`
                            )->tag( `Label`
                                )->a( n = `text` v = `Phone Number`
                            )->tag( `Input`
                                )->a( n = `value` v = `{PHONE_NUMBER}`
                            )->tag( `Input`
                                )->a( n = `value` v = `{PHONE_TIME}`
                            )->tag( `Label`
                                )->a( n = `text` v = `Personal website`
                            )->tag( `Input`
                                )->a( n = `value` v = `{WEBSITE}`

                        )->end(
                    )->end(
                )->end(

                )->ele( `VBox`
                    )->a( n = `id`    v = `formContainerEmployment`
                    )->a( n = `class` v = `sapUiSmallMargin`
                    )->a( n = `items` v = client->_bind( t_employment )

                    )->ele( n = `SimpleForm` ns = `f`
                        )->a( n = `editable` v = `true`
                        )->a( n = `layout`   v = `ColumnLayout`
                        )->a( n = `title`    v = `Personal`
                        )->a( n = `columnsM` v = `2`
                        )->a( n = `columnsL` v = `2`
                        )->a( n = `columnsXL` v = `2`

                        )->ele( n = `content` ns = `f`
                            )->tag( n = `Title` ns = `core`
                                )->a( n = `text` v = `Information`
                            )->tag( `Label`
                                )->a( n = `text` v = `Job Classification`
                            )->tag( `Input`
                                )->a( n = `value` v = `{JOBTITLE}`
                            )->tag( `Label`
                                )->a( n = `text` v = `Pay Grade`
                            )->tag( `Input`
                                )->a( n = `value` v = `{PAYGRADE}`
                            )->tag( `Label`
                                )->a( n = `text` v = `Standard Weekly Hours`
                            )->tag( `Input`
                                )->a( n = `value`  v = `{ path: 'WEEKLYHOURS', type: 'sap.ui.model.type.Integer', constraints: { maximum: 40 } }`
                                )->a( n = `change` v = client->_event( `CHANGE` )
                            )->tag( n = `Title` ns = `core`
                                )->a( n = `text` v = `Rating`
                            )->tag( `Label`
                                )->a( n = `text` v = `Unit`
                            )->tag( `Input`
                                )->a( n = `value` v = `{UNIT}`
                            )->tag( `Label`
                                )->a( n = `text` v = `Employee Class`
                            )->tag( `Input`
                                )->a( n = `value` v = `{CLASS}`
                            )->tag( `Label`
                                )->a( n = `text` v = `FTE`
                            )->tag( `Input`
                                )->a( n = `value` v = `{FTE}`

                        )->end(
                    )->end(
                )->end(

                )->tag( n = `MessageManager` ns = `z2ui5`
                    )->a( n = `items` v = client->_bind( t_messages )

            )->end(

            )->ele( `footer`
                )->ele( `OverflowToolbar`

                    )->ele( `Button`
                        )->a( n = `id`           v = `messagePopoverBtn`
                        )->a( n = `visible`      v = |\{= !!$\{message>/\}.length \}|
                        )->a( n = `text`         v = |\{= $\{message>/\}.length \}|
                        )->a( n = `type`         v = `Emphasized`
                        )->a( n = `ariaHasPopup` v = `Dialog`
                        " original: this.oMP.toggle(oEvent.getSource()) - a pure client-side toggle, so
                        " wired roundtrip-free (no on_event) anchored to the button by its own id
                        )->a( n = `press`        v = client->follow_up_action( val   = client->cs_event-control_by_id
                                                                               t_arg = VALUE #( ( `messagePopover` ) ( `toggleBy` ) ( `messagePopoverBtn` ) ) )

                        )->ele( `dependents`
                            )->ele( `MessagePopover`
                                )->a( n = `id`              v = `messagePopover`
                                )->a( n = `items`           v = `{message>/}`
                                )->a( n = `groupItems`      v = `true`
                                " activeTitlePress is a MessagePopover event (not MessageItem); it ships the
                                " pressed message's target control id so the handler can scroll+focus it
                                )->a( n = `activeTitlePress` v = client->_event( val = `ACTIVE_TITLE` arg = `${$parameters>/item}.getBindingContext('message').getObject().getControlIds()[0]` )
                                )->tag( `MessageItem`
                                    )->a( n = `title`       v = `{message>message}`
                                    )->a( n = `subtitle`    v = `{message>additionalText}`
                                    )->a( n = `type`        v = `{message>type}`
                                    )->a( n = `description` v = `{message>message}`
                                    )->a( n = `activeTitle` v = `true`
                                    " group name (Personal, <section>): a domain classification computed in
                                    " the backend (see model above) and carried on the Message code field,
                                    " NOT a frontend expression - the original derives it in its controller's
                                    " getGroupName; only Email sits in the Contact group, the rest in Information
                                    )->a( n = `groupName`   v = `{message>code}`

                            )->end(
                        )->end(
                    )->end(

                    )->tag( `ToolbarSpacer`
                    )->tag( `Button`
                        )->a( n = `type`  v = `Emphasized`
                        )->a( n = `text`  v = `Save`
                        )->a( n = `press` v = client->_event( `SAVE` )
                    )->tag( `Button`
                        )->a( n = `text` v = `Cancel`

                )->end(
            )->end(
        )->end( ).

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

  ENDMETHOD.


  METHOD on_event.

    CASE client->get_event( ).

      WHEN `ACTIVE_TITLE`.
        " original: activeTitlePress scrolls to the message's target control, closes the popover
        " and focuses the control; the full control id travels from the pressed MessageItem's
        " message object (getControlIds()[0]) and the frontend SCROLL_INTO_VIEW + SET_FOCUS act on it
        DATA(lv_control_id) = client->get_event_arg( ).
        IF lv_control_id IS NOT INITIAL.
          client->follow_up_action( val   = client->cs_event-scroll_into_view
                                    t_arg = VALUE #( ( lv_control_id ) ) ).
          client->follow_up_action( val   = client->cs_event-control_by_id
                                    t_arg = VALUE #( ( `messagePopover` ) ( `close` ) ) ).
          client->follow_up_action( val   = client->cs_event-set_focus
                                    t_arg = VALUE #( ( lv_control_id ) ) ).
        ENDIF.

      WHEN `SAVE`.
        " original generateInvalidUserInput(): force a set of fields invalid to demo the message
        " handling, then open the MessagePopover. abap2UI5 only auto-collects validation messages
        " from USER input, not from programmatically-set model values, so Save mirrors the four
        " issues explicitly on the SAME rows the original targets - formContainer.getItems()[4/5/6]
        " = John Miller / Stefan Bosch / Maria Fontes, plus the employment row: it injects the
        " invalid values and authors the matching four messages (3 Errors + 1 Warning), which the
        " z2ui5.cc.MessageManager reconciles into the message manager.
        IF lines( t_forms ) >= 7.
          " John Miller  -> /T_FORMS/4/NAME
          t_forms[ 5 ]-name     = ``.
          " Stefan Bosch -> /T_FORMS/5/ZIP_CODE
          t_forms[ 6 ]-zip_code = `AAA`.
          " Maria Fontes -> /T_FORMS/6/EMAIL
          t_forms[ 7 ]-email    = `MariaFontes.com`.
        ENDIF.
        IF t_employment IS NOT INITIAL.
          t_employment[ 1 ]-weeklyhours = `400`.
        ENDIF.
        t_messages = VALUE #(
          ( message = `A mandatory field is required` type = `Error` additionaltext = `Name`
            target = `/T_FORMS/4/NAME` )
          ( message = `Enter a number with no decimal places` type = `Error` additionaltext = `ZIP Code/City`
            target = `/T_FORMS/5/ZIP_CODE` )
          ( message = `Enter a valid value` type = `Error` additionaltext = `Email`
            target = `/T_FORMS/6/EMAIL` )
          ( message = `The value should not exceed 40` type = `Warning` additionaltext = `Standard Weekly Hours`
            description = `The value of the working hours field should not exceed 40 hours.`
            target = `/T_EMPLOYMENT/0/WEEKLYHOURS` ) ).
        " the message group (Personal, <section>) is a domain classification, so
        " it is computed in the backend (thin frontend) and rides on the Message
        " code field - the original derives it in its controller's getGroupName.
        LOOP AT t_messages REFERENCE INTO DATA(lr_msg).
          lr_msg->code = COND #( WHEN lr_msg->additionaltext = `Email`
                                 THEN `Personal, Contact`
                                 ELSE `Personal, Information` ).
        ENDLOOP.
        " original: oMP.openBy(oButton) after the values are set - open the popover anchored to the button
        client->follow_up_action( val   = client->cs_event-control_by_id
                                  t_arg = VALUE #( ( `messagePopover` ) ( `openBy` ) ( `messagePopoverBtn` ) ) ).

      WHEN `CHANGE`.
        " the sample's onChange manually adds/removes required-field and constraint messages; here
        " the typed binding + constraints collect those AUTOMATICALLY into the message> model
        " (no app code), so the handler only pushes the model back

    ENDCASE.

  ENDMETHOD.


  METHOD model_init.

    t_forms = VALUE #(
      ( name = `Julie Armstrong` street_name = `Mainstreet` street_number = `1278`
        zip_code = `12345` zip_city = `Maintown` country = `Germany`
        email = `Julie.Armstrong@company.com` phone_number = `+1 (610) 661-1000` phone_time = `12:00` website = `n/a` )
      ( name = `Denise Smith` street_name = `Mainstreet` street_number = `1567`
        zip_code = `12345` zip_city = `Maintown` country = `Germany`
        email = `Denise.Smith@company.com` phone_number = `+1 (610) 661-1000` phone_time = `12:00` website = `n/a` )
      ( name = `Richard Wilson` street_name = `Mainstreet` street_number = `2984`
        zip_code = `12345` zip_city = `Maintown` country = `Germany`
        email = `Richard.Wilson@company.com` phone_number = `+1 (610) 661-1000` phone_time = `12:00` website = `n/a` )
      ( name = `Gerd Becker` street_name = `Mainstreet` street_number = `3614`
        zip_code = `12345` zip_city = `Maintown` country = `Germany`
        email = `Gerd.Becker@company.com` phone_number = `+1 (610) 661-1000` phone_time = `12:00` website = `n/a` )
      ( name = `John Miller` street_name = `Mainstreet` street_number = `1618`
        zip_code = `AAA` zip_city = `Maintown` country = `Germany`
        email = `John.Miller@company.com` phone_number = `+1 (610) 661-1000` phone_time = `12:00` website = `n/a` )
      ( name = `Stefan Bosch` street_name = `Mainstreet` street_number = `4864`
        zip_code = `12345` zip_city = `Maintown` country = `Germany`
        email = `Stefan.Bosch@company.com` phone_number = `+1 (610) 661-1000` phone_time = `12:00` website = `n/a` )
      ( name = `Maria Fontes` street_name = `Mainstreet` street_number = `4864`
        zip_code = `12345` zip_city = `Maintown` country = `Germany`
        email = `` phone_number = `+1 (610) 661-1000` phone_time = `12:00` website = `MariaFontescompany.com` )
      ( name = `Antonio Ferrari` street_name = `Mainstreet` street_number = `2598`
        zip_code = `12345` zip_city = `Maintown` country = `Germany`
        email = `Antonio.Ferrari@company.com` phone_number = `+1 (610) 661-1000` phone_time = `12:00` website = `n/a` ) ).

    t_employment = VALUE #(
      ( jobtitle = `Senior UI Developer (UIDEV-SR)` paygrade = `Salary Grade 18 (GR-14` weeklyhours = `0`
        unit = `ABC` class = `Employee` fte = `1` ) ).

  ENDMETHOD.

ENDCLASS.

More in sap.m

Search every abap2UI5 sample · the full list on one page

On this page