SAP Tutorial: Complete CAP Java Part 12
Adding a custom components to Fiori Elements, basic SAPUI5 dialog setup
Contents
- Previous: Adding a custom action to a Fiori Elements page, problems with the sample app’s implementation
- Current: Adding a custom components to Fiori Elements, basic SAPUI5 dialog setup
- Next: beforeOpen events for dialogs and creating input forms with the oData V4 Model
This is Part 12 of the Complete CAP Java tutorial series, where I’m showing you how to build an application using SAP’s CAP for Java framework by showing you step by step how to build the sample CAP application that SAP has provided, with some tweaks and improvements along the way. This application is a bookshop app, and we’ve currently been working on a page for customers to browse the available books.
In the last few episodes we’ve been creating a custom action to add a review to a book, and in the previous episode we added a button to our Fiori Elements UI so we can trigger that action. However, we also identified some problems with the implementation in CAP’s sample app and suggested some solutions (be sure to check out the last episode for the details). In this installment I’m going to demonstrate one of the solutions — providing a custom SAPUI5 component so we can have more control over the appearance of the form used to submit the information for a new review. Let’s jump right into it.
Step 1: Setting Up a Simple Component and Adding it to the UI
First let’s define a simple SAPUI5 component — just a button that opens an alert dialog and add it to our application. If you’re new to SAPUI5, I recommend taking a look at this walkthrough tutorial to get you up to speed with the framework.
First, we need to delete our UI specification for this button that we made before. Open up app/browse/fiori-service.cds and remove the FieldGroup and LineItem annotations that we specified last time.
Next, let’s create some space for our custom components. First make a folder called “custom”. This will hold all custom components that we make. Since we might make more in the future we’ll make a second subfolder called “AddReview” to hold the files related to this add review action. Finally, create two files, one called AddReviewButton.fragment.xml and AddReviewButtonHandler.js. The former will contain the xml UI specification for the button and the latter will contain the logic. Note that the .fragment in the name is important! We need to use a fragment in an extension point. Your folder structure should look like this:
Now let’s put in just enough filler code so we have something to insert into the UI to confirm that everything is working as expected. First, in AddReviewButton we’ll define a simple SAPUI5 button:
Just as a brief explanation, we are using the FragmentDefinition component to define a fragment. The attributes of this element are simply establishing aliases for the SAPUI5 modules used in this component so we don’t have to type them all out.
Next we provide a VerticalLayout from the layout module and within that we require our CustomColumn.js file so we can use the logic we write there in this file. Check here for more information about VerticalLayout.
Finally, we provide a Button component, with a text attribute for the text we want to display in the button and a press attribute that directs to a method called onPress from our AddReviewHandler.js file. We’ll define this next.
Here we import the MessageBox component (the syntax is from jQuery so check that if you’re unsure what the above is doing). We’re going to delete this later but we just want to use it here to confirm that our files are linked. We return an object with our onPress function that opens the MessageBox and displays “Button pressed!”. For more info on the MessageBox, check this documentation.
Now we need to actually insert this button into the UI. We’ll do that in our manifest.json file. Go down through the file to where we defined our ListReport page. From there, locate the settings object and add in the following code:
Let’s break this down. First, we provide an object called controlConfiguration. Within that we need to provide the fully qualified name of the CDS annotation that our component will need. Since it’s a table column, we of course need the LineItem annotation. Next, within the LineItem annotation, we need columns object (note the difference from regular CDS — there are a lot of little inconsistencies like this so watch out). Next we provide an object to actually define the column. This can be named whatever you want — just give it a name that clearly indicates what it is. Finally, we provide a header property that holds the text for the column header and a template property that contains the path to the xml view file. Note that this path starts with the id of the app, which is defined at the top of our manifest.json file:
Now, if we boot up the app we’ll see our button and we can click it to confirm that our handler file is properly linked:
Good deal! We have our custom component working. Next we need to actually implement the logic for this action. Let’s get to it.
Step 2: Setting Up the Add Review Dialog
Now we need to scrap that test popover that we set up in the previous step and build a dialog that will hold our inputs for a review. First let’s go back to AddReviewButton.fragment.xml and rename our press function to be a little more descriptive of its purpose:
Next let’s define the basic layout for our fragment:
In our dialog definition we start out with a FragmentDefinition like we did with the button fragment. Following that, we use SAPUI5’s dialog element. We provide it with a title, Add Review, and link it to it’s own handler (which we will create later). Then we provide a buttons aggregation with two Button elements (the type=”Emphasized” makes the submit button appear blue). In a little while we’ll see how this renders, but first we need to handle the logic that loads this fragment, so let’s go back to AddReviewButtonHandler.js to do that.
Let’s start with just an empty openDialog function (note that we also import Fragment from sap.ui.core since we will need it to load the fragment later):
Before we try to open the dialog, we need to decide where in the List Report we want to anchor it. Since we only need one of these fragments, we should just attach it to the main parent component. Let’s do that first.
To access our list report we first use sap.ui.getCore(), which returns the object that defines the global instance of our SAPUI5 app. Next we use the byId method from the core to extract our BooksList List Report app. In your own application, the you should use the following pattern to get yours: <appId>::<target>. You saw how to find the app id above. The target is as you see below (from manifest.json).
Next we have to actually load the fragment. This step is a little complicated, but basically we want to make sure that we only load this dialog once so we don’t end up loading it repeatedly after every click — we could bog down the user’s browser otherwise. However, the only chance we have to load it is in this click handler. Therefore, we’re going to first add a check to see if we’ve already loaded the dialog, as shown below:
The reason we’ve checked this.oAddReviewDialog is that we’re going to cache the dialog within the handler itself. Next, let’s load the fragment. Note that fragment loading is asynchronous so we will have to use async/await.
First we specify the function as asynchronous with the async keyword, then we await Fragment.load. For our settings object, we’ll provide two keys: id and name. The id here serves two purposes. One is of course so we can access the fragment from elsewhere if we want to in the future by using the byId method as we used it above to get the Booklist Page, but it will also cause SAPUI5 to throw an error if we instantiate a second dialog. This will help us know whether we made a mistake in setting up our logic to make sure the fragment only loads once.
The second property is name, which is simply a string to identify the fragment. It follows the same pattern that we used to load our AddReviewButton fragment in manifest.json from the previous step.
Of course, right now we’re just loading the fragment then throwing it into oblivion, so let’s go ahead and cache it.
Next, we have to add our dialog as a dependent of the Booklist Page. This is so the dialog will be part of the page’s lifecycle, meaning that it will be destroyed when the page is destroyed. This will also give it access to the oData service that we have registered on the page, which we’ll definitely need later on.
Finally, once all of this is finished, we open the dialog:
After all of that, we can finally boot the app and see the result:
Those two buttons don’t do anything yet, so once we open the dialog it’ll stay open forever. Let’s fix that. First make a file called AddReviewDialogHandler.js and give it the following content.
Here we defined two functions: submit and cancel, which if you recall from our dialog fragment were the press handlers for our submit and cancel buttons:
SAPUI5 automatically passes in an event object into this event handler functions, which we can use to access the element that triggered the event using the getSource() method, which would be the button we clicked on. Our goal now is to close the dialog, so we don’t actually want the button but the dialog. Since the dialog will always be the immediate parent of the button, we can call getParent on the button object. Finally, once we have the dialog, we use the close method to, you know, close it. In this case I made a helper method called getAddReviewDialog to make what’s happening a little more explicit. Now, if you restart the app and try closing the dialog, you’ll see that it works!
Conclusion
In this episode we learned how to create a custom component using SAPUI5 and insert it into the column of a table in a Fiori Elements page. We also learned how to set up a basic dialog. In the next installment we’ll learn how to leverage the validations that we defined using CDS to make sure that our custom UI stays in sync with our backend data definition. Stay tuned!
Was anything unclear in this tutorial? Leave a question below and I’ll get back to you as soon as possible. Was anything incorrect? Please leave a comment below and let me know (a source for the correction would be most helpful). Thanks for your comments!
Support
Did you like this blog? Want to make sure I can keep creating them? Then consider subscribing on Patreon.