ABAP2UI5 – Development of UI5 Apps in pure ABAP (1/3)

Introduction

This blog series introduces the ABAP2UI5 Project. It is an open source project which helps you develop standalone UI5 applications in pure ABAP. The predecessor was introduced in this blog post. The project now has some improvements making it possible to use a wide range of UI5 controls. For example, it is now possible to display tables and lists and define different layouts with headers and footers. With these you can create applications for a lot of different use cases.

General idea

ABAP2UI5 wants to provide a “cloud ready” and “non-sap-gui” way to create UI5 apps in pure ABAP without using OData, Javascript, Annotations or RAP – just like in the “old days” when we just needed a few lines of ABAP code to display tables and inputs with the CL_GUI_ALV_GRID and the CL_GUI_CFW framework.

Blog Series

(1/3) ABAP2UI5 – Development of UI5 Apps in pure ABAP (this blog post) This blog post focuses on how we can use ABAP2UI5 to develop applications in general
(2/3) ABAP2UI5 – Lists, Tables, Toolbar and editable Examples to display lists and tables with an ABAP2UI5 application are explained here.
(3/3) ABAP2UI5 – Demo apps (coming soon) Some full working demo apps are introduced in this blog post.

If you are interested in the technical background, take a look at this first blog post: ABAP2UI5 – Development of UI5 Selection Screens in pure ABAP

Project Information

All project information can be found in the GitHub repository and on twitter:

Repository and Installation https://github.com/oblomov-dev/abap2ui5
News, Feedback and Updates https://twitter.com/OblomovDev

Demo

For a first impression of ABAP2UI5, take a look to this demo – everything is created in the backend in pure ABAP. No additional app deployment or javascript is needed:

An example application looks like this:

CLASS z2ui5_cl_app_demo_01 DEFINITION PUBLIC.

  PUBLIC SECTION.

    INTERFACES z2ui5_if_app.

    DATA product TYPE string.
    DATA quantity TYPE string.

ENDCLASS.

CLASS Z2UI5_CL_APP_DEMO_01 IMPLEMENTATION.

  METHOD z2ui5_if_app~controller.

    CASE client->get( )-lifecycle_method.

      WHEN client->cs-lifecycle_method-on_init.
        product = 'tomato'.
        quantity = '500'.

      WHEN client->cs-lifecycle_method-on_event.

        CASE client->get( )-event.

          WHEN 'BUTTON_POST'.
            client->display_message_toast( |{ product } { quantity } ST - GR successful| ).

        ENDCASE.

      WHEN client->cs-lifecycle_method-on_rendering.

        DATA(view) = client->factory_view( ).

        view->page( title = 'Page title'
           )->simple_form('Form Title'
             )->content( 'f'
                )->title( 'Input'
                )->label( 'quantity'
                )->input( view->_bind( quantity )
                )->label( 'product'
                )->input( value = product editable = abap_False
                )->button( text = 'post' press = view->_event( 'BUTTON_POST' ) ).

    ENDCASE.
  ENDMETHOD.
ENDCLASS.

The other code examples can be found here:

Selection-ScreenList / Table

As you can see, the only thing you have to do to develop an UI5 app with ABAP2UI5 is to create a new class implementing the following interface:

INTERFACE z2ui5_if_app
  PUBLIC .

  INTERFACES if_serializable_object.

  METHODS controller
    IMPORTING
      client TYPE REF TO z2ui5_if_client.

ENDINTERFACE.

Let’s go through the development process step by step starting with the controller:

Development with ABAP2UI5

Controller

The interface Z2UI5_IF_APP provides the method “controller,” which gives you the control of the frontend UI5 app similar to a Javascript controller of an UI5 freestyle application. Therefore they look very similar:

UI5-Controller vs. ABAP2UI5-Controller
sap.ui.controller("sap.ui.myApp.controller.one", {

	onInit: function() {

	},

	onBeforeRendering: function() {
		 
	},

	onEvent: function() {
	//button event handling here
	},

	onAfterRendering: function() {
		
	},

	onExit: function() {

	}
	
});
METHOD z2ui5_if_app~controller.

    CASE client->get( )-lifecycle_method.

      WHEN client->cs-lifecycle_method-on_init.
       "set initial values....

      WHEN client->cs-lifecycle_method-on_event.
        "event handling....

      WHEN client->cs-lifecycle_method-on_rendering.
        DATA(view) = client->factory_view( ).
        "rendering here....

    ENDCASE.

ENDMETHOD.

The ABAP2UI5 controller has three lifecycle events. The path on_init is called when an app is started, the path on_rendering is called after every server request when the view is rendered and the path on_event when an UI5 event is raised.

Of course when everything is handled in one method this is not the idea of clean code, but ABAP2UI5 doesn’t want to make any requirements on how apps are structured. So there is only this method as a basic layer and then every users can implement different methods on top of this by themselves. Maybe one for init, one for user_commands etc.

View

ABAP2UI5 provides the possibility to develop UI5 Views similar to an UI5 freestyle application. The only difference is that views are not defined in XML, instead they are created with an ABAP2UI5 view generator class. With this the user has complete freedom in structuring the view and also the flexibility to use a lot of different UI5 controls. A simple example (compared to a normal UI5 View) looks like this:

Example 1

UI5-View vs. ABAP2UI5-View
<Page title="Page title" showNavButton="true" navButtonTap="onEvent" >
	<f:SimpleForm title="Form Title">
		<f:content>
			<Title text="Input" />
			<Label text="quantity"/>
			<Input editable="true" value="{/oUpdate/QUANTITY}"/>
			<Label text="product" />
			<Input value="tomato" />
			<Button press="onEvent" text="post" enabled="true"/>
		</f:content>
	</f:SimpleForm>
</Page>
view->page( title = 'Page title' nav_button_tap = view->_event_display_id( client->get( )-id_prev_app )
        )->simple_form('Form Title'
          )->content( 'f'
             )->title( 'Input'
             )->label( 'quantity'
             )->input( view->_bind( quantity )
             )->label( 'product'
             )->input( value = product editable = abap_False
             )->button( text = 'post' press = view->_event( 'BUTTON_POST' ) ).

And a more detailed example looks like this:

Example 2

UI5-View vs. ABAP2UI5-View
<Page title="Example - ZZ2UI5_CL_APP_DEMO_02" showNavButton="true" navButtonTap="onEvent">
	<l:Grid defaultSpan="L6 M12 S12" class="sapUiSmallMarginTop">
		<l:content>
			<f:SimpleForm title="Input">
				<f:content>
					<Label text="Input with value help" />
					<Input placeholder="fill in your favorite colour" showClearIcon="false" editable="true" value="{/oUpdate/SCREEN-COLOUR}" suggestionItems="{/MT_SUGGESTION}" showSuggestion="true">
						<suggestionItems>
							<core:ListItem text="{VALUE}" additionalText="{DESCR}" />
						</suggestionItems>
					</Input>
				</f:content>
			</f:SimpleForm>
			<f:SimpleForm title="Time Inputs" editable="true">
				<f:content>
					<Label text="Date" />
					<DatePicker value="{/oUpdate/SCREEN-DATE}" />
					<Label text="Date and Time" />
					<DateTimePicker value="{/oUpdate/SCREEN-DATE_TIME}" />
					<Label text="Time Begin/End" />
					<TimePicker value="{/oUpdate/SCREEN-TIME_START}" />
					<TimePicker value="{/oUpdate/SCREEN-TIME_END}" />
				</f:content>
			</f:SimpleForm>
		</l:content>
	</l:Grid>
	<l:Grid defaultSpan="L12 M12 S12" class="sapUiSmallMarginTop">
		<l:content>
			<f:SimpleForm title="Input with select options" editable="true">
				<f:content>
					<Label text="Checkbox" />
					<CheckBox text="this is a checkbox" selected="{/oUpdate/SCREEN-CHECK_IS_ACTIVE}" enabled="true" />
					<Label text="Combobox" />
					<ComboBox showClearIcon="false" selectedKey="{/oUpdate/SCREEN-COMBO_KEY}" items="{/1}">
						<core:Item key="{KEY}" text="{TEXT}" />
					</ComboBox>
				</f:content>
				<Label text="Segmented Button" />
				<SegmentedButton selectedKey="{/oUpdate/SCREEN-SEGMENT_KEY}">
					<items>
						<SegmentedButtonItem icon="sap-icon://accept" key="BLUE" text="blue" />
						<SegmentedButtonItem icon="sap-icon://add-favorite" key="GREEN" text="green" />
						<SegmentedButtonItem icon="sap-icon://attachment" key="BLACK" text="black" />
					</items>
				</SegmentedButton>
				<Label text="Switch disabled" />
				<Switch type="Default" enabled="false" state="/2" customTextOff="B" customTextOn="A" />
				<Label text="Switch accept/reject" />
				<Switch type="AcceptReject" enabled="true" state="/oUpdate/SCREEN-CHECK_SWITCH_01" customTextOff="off" customTextOn="on" />
				<Label text="Switch normal" />
				<Switch type="Default" enabled="true" state="/oUpdate/SCREEN-CHECK_SWITCH_02" customTextOff="NO" customTextOn="YES" />
			</f:SimpleForm>
		</l:content>
	</l:Grid>
	<footer>
		<OverflowToolbar>
			<Link text="Go to Source Code" target="_blank" href="" enabled="true" />
			<ToolbarSpacer />
			<Button press="onEvent" text="Clear" enabled="true" icon="sap-icon://delete" type="Reject" />
			<Button press="onEvent" text="Send to Server" enabled="true" type="Success" />
		</OverflowToolbar>
	</footer>
</Page>
DATA(page) = view->page( title = 'Example - ZZ2UI5_CL_APP_DEMO_02' nav_button_tap = view->_event_display_id( client->get( )-id_prev_app ) ).

DATA(grid) = page->grid( 'L6 M12 S12' )->content( 'l' ).

grid->simple_form('Input' )->content( 'f'
        )->label( 'Input with value help'
        )->input(
            value       = view->_bind( screen-colour )
            placeholder = 'fill in your favorite colour'
            suggestion_items = view->_bind_one_way( mt_suggestion ) )->get(
            )->suggestion_items( )->get(
                )->list_item( text = '{VALUE}' additional_text = '{DESCR}' ).

grid->simple_form('Time Inputs' )->content( 'f'
        )->label( 'Date'
        )->date_picker( view->_bind( screen-date )

        )->label( 'Date and Time'
        )->date_time_picker( view->_bind( screen-date_time )

        )->label( 'Time Begin/End'
        )->time_picker( view->_bind( screen-time_start )
        )->time_picker( view->_bind( screen-time_end ) ).

page->grid( default_span  = 'L12 M12 S12' )->content( 'l'
       )->simple_form('Input with select options' )->content( 'f'

    )->label( 'Checkbox'
    )->checkbox(
         selected = view->_bind( screen-check_is_active )
         text     = 'this is a checkbox'
         enabled  = abap_true

    )->label( 'Combobox'
    )->combobox(
         selectedkey = view->_bind( screen-combo_key )
         items      = view->_bind_one_way( VALUE ty_t_combo(
             ( key = 'BLUE'  text = 'green' )
             ( key = 'GREEN' text = 'blue'  )
             ( key = 'BLACK' text = 'red'   )
             ( key = 'GRAY'  text = 'gray'  ) )
         ) )->get( )->item( key = '{KEY}' text = '{TEXT}'
        )->get_parent( )->get_parent(

    )->label( 'Segmented Button'
    )->segmented_button( view->_bind( screen-segment_key ) )->get(
        )->items( )->get(
             )->segmented_button_item( key = 'BLUE'  icon = 'sap-icon://accept'       text = 'blue'
             )->segmented_button_item( key = 'GREEN' icon = 'sap-icon://add-favorite' text = 'green'
             )->segmented_button_item( key = 'BLACK' icon = 'sap-icon://attachment'   text = 'black'
       )->get_parent( )->get_parent(

    )->label( 'Switch disabled'
    )->switch( enabled = abap_false    customtexton = 'A' customtextoff = 'B'
    )->label( 'Switch accept/reject'
    )->switch( state = screen-check_switch_01 customtexton = 'on'  customtextoff = 'off' type = 'AcceptReject'
    )->label( 'Switch normal'
    )->switch( state = screen-check_switch_02 customtexton = 'YES' customtextoff = 'NO' ).

page->footer( )->overflow_toolbar(
         )->link(
             text = 'Go to Source Code'
             href = client->get( )-s_request-url_source_code
         )->toolbar_spacer(
         )->button(
             text  = 'Clear'
             press = view->_event( 'BUTTON_CLEAR' )
             type  = 'Reject'
             icon  = 'sap-icon://delete'
         )->button(
             text  = 'Send to Server'
             press = view->_event( 'BUTTON_SEND' )
             type  = 'Success' ).

In the second example you can see that you need 70 lines of code for a view. Maybe that seems to be a lot for a single view and the generating process is very technical. But again ABAP2UI5 just wants to provide the basic layer and functionality to create UI5 applications so that the system footprint of ABAP2UI5 stays very small. Apart from this every user can build useful wrappers around the view rendering.

ABAP2UI5 Library

At the moment the following UI5 controls are available and more will be added in the future:

Which UI5 controls are working with ABAP2UI5?

Only controls which don’t have their “own javascript logic” are working. That means for example we can use the Input control, because it has just a few attributes, which are send directly back to the server and we don’t need any additional javascript for this. Most of the controls 70-80% work like this, so we can use them with ABAP2UI5 out of the box.

Controls which need an additional Javascript implementation like the Message Manager are not possible. But there would be the possibility to build a Custom Control around it and make it controllable by the server and with this a part of ABAP2UI5. But the question is if it is worth the effort. In the future it makes sense to take a look on certain very popular controls, but first let’s keep ABAP2UI5 simple and skip this.

Events

There are two event methods possible at the moment.

Event

view->( )->button( 
             text = 'post' 
             press = view->_event( 'BUTTON_POST' )
 ).
Most of the times it is enough to just send a simple user-command back to the server. For this you can use the method _event( )

Event Display ID

 view->page( 
    title.         = 'Page title' 
    nav_button_tap = view->_event_display_id( 
                               client->get( )-id_prev_app ) 
    ).

 
With this method ABAP2UI5 jumps back to a response with a certain id. For example, this is very helpful when you just want to navigate back after calling a new app. In the section REST and Draft you can see that every request generates a new id and a draft of the actual app status is saved. That gives us the possibility to also jump to every previous state with the method _event_display_id()

If there is the need for more event functions, it is possible to extend ABAP2UI5 in the future.

Model (Data Binding)

There are three possibilities how ABAP2UI5 sends data to the frontend:

Direct XML

view( )->label( 'product'
      )->input( 
           value    = product 
           editable = abap_false
  ).
         
Just put the value into the attribute, then ABAP2UI5 writes the value directly into the UI5-XML-View and sends it to the frontend

One-Way-Binding

view->( )->label( 'product'
        )->input( 
            value    = _bind_one_way( product )
            editable = abap_False
   ).
ABAP2UI5 writes the value into the View Model and binds the View Model to the XML View. Helpful for deep data models like tables, we will take a look to this in the next blog post

Two-Way-Binding

view( )->label( 'quantity'
      )->input( view->_bind( quantity ) ).​
Same as one-way-binding, but in addition the frontend values are send back to the server with every request. So make sure that the value you bind here are public attributes of your app that ABAP2UI5 can assign it and update the values from outside. This binding mode is useful for inputs or editable tables.

So the user has to decide during the view definition how data is send to the frontend and if it should be send back or not. To keep the request payload small it is a good idea to use two-way-binding only if it is needed and values are changed at the frontend. It is also recommended not to overload the app with thousands of attributes, because ABAP2Ui5 loops after every request over all attributes of the class and checks if values exists which have to be updated. So far i never had problems, but just be aware and keep in mind that there is a loop working in the background.

REST and Draft

The whole framework is based on REST, therefore no session exists between two requests and it is compatible with any mobile use cases or mobile devices. It is also compatible to “restful” environments like BTP ABAP Environment or the new language version ABAP Cloud.

Besides that after every request the actual state of the app is serialized and persisted to the database. The Z2UI5_IF_APP includes the interface IF_SERIALIZABLE_OBJECT, therefore it is possible to use attributes of the app instance between two requests and it feels like working with a stateful application like in a dynpro or selection-screen environment:

The persistence of ABAP2UI5 is called a draft table, although it is not completely the same as you know from RAP. The ABAP2UI5 drafts are not type specific, they just persist the app class in a generic way. Therefore there is no extra work in creating typed draft tables, everything works automatic in the background.

The drafts are very helpful for interim results, but maybe don’t exaggerate with this possibility. It is not a good idea to build endless chains of apps saving each other in attributes or try to save millions of entries in internal tables. But of course this is also not a good programming style in other contexts. On the other hand the ABAP server and database are very powerful so saving internal tables with for example a thousands of entries is not a problem.

But don’t forget to delete the table Z2UI5_T_DRAFT from time to time, otherwise it will fill up.

Summary

That was the first part of the introduction to the ABAP2UI5 Project. Fell free to install this project and try out the examples and demos. For news and improvements follow this project on Twitter or take a look to the GitHub repository from time to time.

In the next blog post we will use ABAP2UI5 to send deep data models to the frontend and develop apps displaying lists and tables.

What do you think of this approach? Feel free to share your feedback and ideas.

Thanks for reading!

Original Article:
https://blogs.sap.com/2023/02/22/abap2ui5-development-of-ui5-apps-in-pure-abap-1-3/

ASK SAP EXPERTS ONLINE
Related blogs

LEAVE A REPLY

Please enter your comment!
Please enter your name here