This blog of our New Blog Series: ABAP Trial Version for Newbies series should be of interest to everybody who is working in the ABAP environment since Web Dynpro is SAP's main UI technology for currently developed and future applications. With Web Dynpro for ABAP SAP offers this mighty UI framework also for the large community of ABAP developers and it's probably a good advise for every ABAP developer to start exploring the Web Dynpro development model as soon as possible. With the SAP NetWeaver 7.0 ABAP Trial Version we have the perfect environment at hand to do this.
The best way to learn is by doing, so we will create a small Web Dynpro application which illustrates the basic idea behind the Web Dynpro programming model.
In our first small Web Dynpro example we use the YCL_CUSTOMER ABAP objects class we created in the ABAP Trial Version for Newbies: Part 17 - Your first ABAP Object of the series. The Web Dynpro application should display, change and save the data of a given customer. This small example will already demonstrate the separation of the UI layer from the core logic. The Web Dynpro layer uses the ABAP class but does not now anything about the way the class retrieves and stores the data while the customer class does not care if it is used by an application with a web user interface or called by other backend services.
The core of a Web Dynpro project is the Web Dynpro component. The component contains the visible parts of the UI and the logic which steers its behavior. We start in the ABAP Workbench (SE80) with creating a new Web Dynpro component. Select "Web Dynpro Comp./Intf." from the drop down list and enter the name for the new component for example "YWD_CUSTOMER".
Hit enter and confirm the pop up.
You can enter a description in the next window while leaving the other options untouched. Also the "Object Directory Entry" popup of the transport system should already be familiar to you. It's up to you if you want to create the component simply as local object or to put it into a transportable package.
The workbench has now created the Web Dynpro component with a component controller, which is the backbone of our WD component, a component interface (which will be explained later) and a window, which is the place where the visible parts of a Web Dynpro component - the views - will later be arranged and where it can be defined how they will be displayed.
The Web Dynpro model is based on the famous Model-View-Controller paradigm, used in almost all modern UI frameworks. The visible layout is defined by views, which contain the arrangement of UI elements (buttons, tables, input fields, etc...). The invisible parts, which steer the behavior of the UI, handle the data, contain event handlers etc.... are the so called controllers with the component controller being the core controller of the component. Its role is to handle and retrieve the data from the business logic or model which is in our case the YCL_CUSTOMER class.
We define the kind of data the WD component should handle in the component controller's context. Each Web Dynpro controller contains a hierarchical structure called context to store data. The hierarchy contains nodes as structuring elements and attributes for the data.
Select the component controller and go to the "Context" tab.
The context is still empty, just the root node named "CONTEXT" is visible. We want our Web Dynpro component to display the data of a customer. Therefore we define a context structure which contains the attributes of our customer class YCL_CUSTOMER.
Right click on the "CONTEXT" root node and select "Create - Node" ( Be sure to be in change mode).
The upcoming popup allows us to create a context structure based on ABAP dictionary data types. This comes in handy since the attributes of our customer class are based on data types already defined in the ABAP dictionary as columns of the SCUSTOM structure.
Therefore we choose "SCUSTOM" as "Dictionary structure" for the new node named "CUSTOMER".
Before confirming click the "Add Attribute from Structure" button!
In the next pop up select
ID
NAME
STREET
POSTCODE
CITY
COUNTRY
TELEPHONE
EMAIL
and confirm. The context of the component controller now contains a context node named "CUSTOMER" with attributes corresponding to the attributes of the SCUSTOM structure. The context is now able to store the data of one customer and should look like this:
Before we continue with the implementation of the necessary code to retrieve the data of a customer into the context, lets first create a view with the UI elements so we can test out the Web Dynpro component in an early stage and see how it works in general.
Right click the WD component name YWD_CUSTOMER at the left hand side and select "Create - View":
Enter "CUSTOMER_VIEW" as name and confirm. The workbench shows the layout tab of the newly created view. Because the layout preview tab displays the preview via HTTP a browser user/password popup might turn up. Use your system user (user: bcuser pwd: minisap) to start the preview (which is still empty).
A real world application contains usually several views, each for a specific task with its specific data and interactive UI elements and event handlers. Therefore Web Dynpro creates in addition to the component controller for each view another controller, the view controller. The task of the view controller is to handle only the data and user actions of its view, while the component controller is responsible for the whole data of the Web Dynpro component and the communication with the business logic. In our simple case we have only one view and therefore the view controller needs the same data as the component controller. To display the data the view needs it in its own context. The data transfer is done via context mapping between the two controller contexts.
Nodes of different contexts can be mapped if they have the same structure. The framework ensures that mapped context nodes contain at runtime always the same values.
To define the mapping between the component controller and the view controller go to the context tab of the view. The tab displays the context of the view controller on the left hand side and the context of the component controller at the right hand side. Now drag and drop the CUSTOMER node from the component controller context to the view controller context. This will create a new context node of the same structure in the view controller which is already mapped.
You will be asked if you really want to copy the node to the view controller and map it. Confirm this with and you should end up with a CUSTOMER context node in the view controller context which is mapped to the component controller context.
Now we want to create the layout of the view which should display the customer data out of the context. A view displays data and offers user interaction with UI elements. These UI elements are bound to the nodes and attributes of the view controller's context.
Switch back to the layout tab. You see the empty view area and the available UI elements. Select the "Standard Container" UI element library from the bottom of the list. First we need a group UI element as container for several input fields, labels, and buttons. The Group UI element is the left one in the first row of the Standard Container elements. Drag and drop it onto the layout area (1) and the group element will also appear in a hierarchical view at the right hand side with a caption as sub element (2). This tree displays all UI elements of a view under the main container called the ROOTUIELEMENTCONTAINER. Since some UI elements can contain other UI elements this can become a pretty complex tree structure. For example the Group UI element is such an container element which contains already a caption element.
New UI elements can either be added to the view by drag and drop or in the tree at the right hand side with a right mouse click. We want to display all attributes of the customer in text input fields. Instead of adding each input field individually we use a wizard which creates the input fields with correct labels and binds them to the customer context node of the view controller.
Right click onto the GROUP in the tree and choose "Create Container Form" from the menu.
In the upcoming "Create Context Binding" popup click on "Context" (1) and select the CUSTOMER node (2) in the "Choose Context" popup and confirm.
Confirm again the selection in the "Create Context Binding" pop up.
The wizard has created a label and an input field for each attribute of the CUSTOMER node and it has also created the binding between the context node attributes and the input fields. This means that any data which is stored in the context is automatically displayed in the input fields and any user input in the fields will be automatically stored in the context.
You see the labels and input fields in the layout preview. If you select a UI element in the tree structure the properties of the selected UI element are displayed below in a table and can be changed. Lets use this to clean up the layout.
First select the "CAPTION [Header]" element below the group element and set its text property to "Customer Data". Next select the GROUP element itself and change the "Layout" property from "FlowLayout" to "GridLayout". Change further down in the property list the "ColCount" property to 2 and save the view. The layout should now look more tidy with the labels in the first column of each row.
Lets have a look how our almost finished Web Dynpro component looks like in this state. We haven't implemented the data retrieval yet, therefore it will not display any data but the general layout is already fixed and I assume you are curious by now how the whole thing looks in a browser.
To be visible we have to put the view into the window of the component. The window is a container for views. Real applications contain several views and sometimes also several windows. Therefore it is necessary to declare which view belongs to which window.
Double click on the Window YWD_CUSTOMER (1) (By default the window of a WD component has the same name as the component itself but it is another kind of object). The still empty window structure is now displayed on the right hand side.
Now drag and drop the view CUSTOMER_VIEW from the left hand side to the YWD_CUSTOMER window in the window structure (2). You can check if the view has been included by clicking the triangle left from the window to display its structure.
Now activate the whole project by selecting the YWD_CUSTOMER component in the object tree with a double click and clicking on the activation button (sevenths icon in icon row on top of the workbench). A popup appears with all inactive objects you have created in the component. All should be selected. Confirm the popup.
The last thing we need to see our example in a browser is a new entity called Web Dynpro application. So far we have created a Web Dynpro Component, which contains the core Web Dynpro entities and defines our UI. The Web Dynpro Application is the handle which allows to start the component.
Right click on the YWD_CUSTOMER component and select "Create - Web Dynpro Application". You can add a description to the WD Application. Save the WD Application with the save button on top of the workbench. Since a WD Application is also a transportable object select a package or save it as local object.
Now a new node "Web Dynpro Applications" has appeared in the object tree with the YWD_CUSTOMER application beneath it. You can test the WD Application with the WD component by right mouse click on the application object (1) and selecting "Test" (2).
The browser will be started and tries to call the Web Dynpro application which itself instantiates the YWD_CUSTOMER Web Dynpro component and displays its window with the view. Since the application is running on the server you have to log on with your user credentials (user: bcuser pwd: minisap). The browser will display the following (Don't worry if a time out error occurs. This just means that the default 60sec limit for a HTTP request are not sufficient to compile the Web Dynpro framework on the server, which might be necessary if it is the first time you try to run a Web Dynpro application. Just try again until the view appears. Later request will be much faster.)
The browser should display the view:
OK, this probably doesn't knock your socks off but shows already some important features. For example the label texts are the correct ones retrieved from the ABAP dictionary data types. This works directly out of the box without any development effort. The texts are also language dependent. If you change the last two characters in the URL from "EN" to "DE" the view will be displayed with German texts for the labels (the Trial version contains only English and German texts, but real systems contain all kinds of languages).
There's also a value help available for the Customer Number, although we have nothing implemented for that. The Web Dynpro framework displays automatically a value help if one is defined in the ABAP dictionary for the data type which is used for the context attribute that is bound to the input field. In the case of the Customer Number there is a rather complex value help defined for the data type SCUSTOM-ID as part of the data types used in standard SAP training workshops. Experienced ABAP developers will be glad to hear that their existing value help defined for GUI based transaction is fully usable in Web Dynpro.
Let's continue with the implementation of the code that displays and allows to change customer data. We want to add a button and if a user has entered or selected a customer ID the view should display the address data of the customer.
As mentioned, retrieving data from the business logic or model is the task of the component controller. Select the component controller in the workbench. First we add a reference variable of our YCL_CUSTOMER class as attribute to the component controller. Select the "Attributes" tab and enter the attribute CUSTOMER of the type (reference) YCL_CUSTOMER:
The component controller has to offer a method which creates a customer object for a given customer ID and fills its context structure with the attributes of the customer.
Switch to the Methods tab of the component controller and create a new method READ_CUSTOMER. A double click on the method names opens the ABAP editor.
Implement the following code into the method:
First we declare the variables for a reference to a context node and for the ID of a customer.
Next we retrieve the reference of the CUSTOMER node from the controller's context. We use the node's "get_attribute" method to get the current value of the "ID" context attribute.
With the ID we instantiate the CUSTOMER object with the CREATE statement. WD_THIS is the self reference of the component controller. Therefore the customer object reference we have added as class attribute to the component controller can be accessed as WD_THIS->CUSTOMER.
Finally we set the context's attributes with the values of the current customer object.
This method should be called if the user clicks a button. To add the button to the view select the CUSTOMER_VIEW and go to the layout tab. The button is the first element in the "Standard Simple" library of UI elements. Drag and drop the button to the view. Select it in the tree hierarchy and set its text property to "Get Customer" (1).
The button has to trigger an action. To create such an action click on the create button next to the "onAction" property(2):
In the upcoming pop up name the new action GET_CUSTOMER and confirm. A double click on the newly created action name in the Property table (3) will open the ABAP editor for the action's method handler ONACTIONGET_CUSTOMER.
This event handler is a method of the view controller because the button belongs to the view. Remember that the view controller and the component controller are two different objects. It is not possible to link an action of a button directly to a component controller. Handling the events of UI elements of a view is always the task of the view controller. What we have to do now is simply calling the component controller's READ_CUSTOMER method from the event handler in the view controller. This is quite straightforward since every view controller has access to the reference of the component controller. Therefore our event handler consists of a single line of code:
Because this blog got longer than expected, I leave the last part, adding the functionality to save changed address data for you as homework. But you have everything you need to know at hand. Create a new SAVE_CUSTOMER method in the component controller in which you take the current context attribute values, put them in the customer object's attributes and call the save method of the customer object.
Add a button to the view, which triggers an action that calls the SAVE_CUSTOMER method of the component controller and you are done.
If this was your first encounter with Web Dynpro you probably think by now that the Web Dynpro framework is quite complex. A lot of new entities appeared and most of the concepts are new and it takes some time to get used to everything. The learning curve might be steeper than in other more low level UI environments, where you get first results faster. But you might have noticed something during this example: You never cared about any HTML, JavaScript, HTTP or browser issues. It was never necessary to think what kind of HTML data or script has to be sent to the browser to create a specific effect. All you thought about was really the user interface of your application. Where should a button be placed, which part of the business data should be displayed, which action should be triggered by a button and so on. These kind of questions can be complex and sometimes hard to solve. Creating good user interfaces is still an art. But when you deal with this art, you should concentrate on what your UI should look like and what it should do and not what happens during an HTTP request or what kind of effect a browser cache can have or if a specific JavaScript statement is supported by all browsers.
This is the fundamental feature of the Web Dynpro framework. Concentrate on the user interface and not on the underlying rendering technologies.