SAP Tutorial: Complete CAP Java 14

Brian Heise
9 min readSep 30, 2021

Programmatic creation of SAPUI5 components, submitting data with the oData V4 model, and handling validation errors

Photo by Thomas Lefebvre on Unsplash

Contents

You’ve made it to Part 14 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 rebuilding the sample CAP application, 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. As always, the code for this tutorial can be found in this git repo.

Since the beginning of the series we’ve been working on building a Fiori Elements List Report app that displays a list of books for the customer to view, and more recently we’ve been implementing a custom button and dialog in the page so that users can add a review for a book. Last time, we wrote the logic to link the dialog with the review list of the particular book that the user selected. Today we’re going to complete the form and handle some issues with validations.

Let’s get started!

Step 1: Completing the Form

First let’s simply finish the form. If you recall from last time, we already set up a title input, as shown below:

Our current form

We need to also create a rating field and a comment field. Last time we temporarily defined this input in our AddReviewDialogHandler file, as shown below:

The add review form currently

As you can see, it’s just written in the file. That was good for testing, but this element is going to get rather complicated later so we should really move this to a dedicated file. Let’s do that.

Create the new file

Note the naming convention: we’re going to return a function from this file that will create the form, so we give it a lower case first letter.

Move and refactor

Here we moved the code into the new file and refactored some. Specifically, we put all the logic for the title element in it’s own function (and we’ll do the same for the other form elements). This is just to help us organize the code.

Import and invoke the new function

Here we delete the old code and instead import the new function and call it. This should yield the same result as before. Now let’s move on.

We need an element to handle the rating. Luckily for us SAPUI5 provides the perfect component: RatingIndicator. Let’s build that out.

Import the rating indicator

First we import the RatingIndicator from the m library.

Create a helper function to build the element

Next we make a helper function to build the indicator. As before we provide it with the appropriate label and bind it’s value to the rating field of the Review. We also need to provide the maxValue property, which indicates how many stars are available to select. Since our rating can accept a value between 0 and 5 (inclusive), we’ll specify 5.

Create and add the element

As before, we call our helper function to create the rating element, then add it to the container.

The form

Looking good! Now the same process for the comment. This time we’ll use the TextArea control. Following the same steps as before we import TextArea, make a helper method to organize the logic related to this, then call the function and add the result to our form container. Below is the helper function:

Comment element creator helper function

And the result:

The final form (SAPUI5 automatically changed the formatting)

Not bad! Now let’s add the last bit of logic to the dialog handler so we can actually submit a review.

Step 2: Submit Logic

Let’s return to our submit handler in AddReviewDialogHandler and change it to look like this:

The new submit handler

First change the function to async. Next we use the helper method we defined in the last episode to get the dialog itself. Next we use the setBusy method to display a busy indicator while the request is processing (in local development this will happen so fast you won’t notice a difference, but when the response takes some time to arrive it will display a busy indicator and block interaction with the dialog).

After that, we set up a try-catch block. The first thing is to await the submission of the review. We do that by retrieving the oData V4 model from the review dialog then call submitBatch method and pass in the updateGroupId that we defined in the last episode. Next we console log “SUCCESS” just so we know whether it worked or not. Then we close the dialog. Note that we close the dialog in the try block because if there’s an error we don’t want the dialog to close.

In the catch block we simply console log the error for now. In the finally block we turn off the busy indicator because we want that to happen regardless of success or failure.

We can test the function:

Add info for a review and click submit
Success outputs to the console

Now we can go to our srv/api-test/cat-service.http file to confirm that the data was saved. We use the test we wrote to output all the Reviews in the database and we find this:

Our review was saved

Fantastic! If you experiment with the UI you’ll also notice that SAPUI5 has already implemented our backend validations with no additional work on our part. Try writing a title or comment that’s too long and you get this:

Validations already in effect

This already is much better than what we saw in the CAP sample app’s implementation of this feature. However, there are a few big problems remaining. The first is that we can still click the submit button even though we have validation errors. Luckily because we’re using the standard way of creating an entity, CAP’s backend validations are in effect, so we won’t end up saving incorrect data but it’s still not good practice to allow a user to submit data that we know is not correct since it can bog down the backend with processing unnecessary requests.

The second problem is that we’ve hard-coded our form so that it will not respond to changes on the backend. What if we want to change the number of stars to 3? What if we update the labels for the inputs? The current implementation requires us to update the front and the back separately, which is a good way to make mistakes since it’s pretty easy to update one and forget to update the other. In the next sections let’s learn how to fix these issues.

Step 2.5: Cancel Logic

One feature of the oData V4 model is that once we create a new entity as we did in the beforeOpenDialog handler, it hangs around until we either explicitly cancel it or submit it. With our current implementation, if we were to open an add review dialog and then cancel, then open another and submit, two reviews would be submitted: the one we intended to submit and the one we cancelled earlier. Let’s go to our cancel handler and update it to prevent this from happening:

Resetting the binding

First we access the binding on the formContainers aggregation, then we call the resetChanges method. This will remove the unwanted review.

Step 3: Preventing Submission of Invalid Values

There are plenty of ways to handle this, but my preferred way is to disable the submit button when there are errors, so I’ll show you how to implement that.

First we need a way to store the error state of our form; we’ll use SAPUI5’s JSONModel for that. Let’s import the model and instantiate it in our beforeOpen event handler. Notice that we don’t provide any conditional for this code. The reason is that we’d like to re-initialize this each time we open it on the off chance that any error state remains after closing the dialog.

Setting up the error state model

Here we import the JSON model and then create a model with two properties: hasErrors and inputErrors. The inputErrors property is initialized to an empty object; we’ll use this to store the error state of each input. The hasErrors property is a computed property using a JavaScript getter. Basically anytime this property is accessed, it’ll run the function which will return true if any of the properties of the inputErrors object resolves to true; otherwise, it returns false. This ensures that our hasErrors property will always reflect the content of inputErrors, meaning we don’t have to worry about manually updating the property any time inputErrors changes.

Next we need to utilize the validationError and validationSuccess events to update inputErrors. Let’s go to AddReviewDialog.fragment.xml to set these up, and while we’re at it let’s bind hasErrors to the submit button as well.

Changes to the view

Note the use of an expression binding on line 25 to make the button only be enabled when there are no form errors. If you’re unfamiliar with expression binding, check this documentation.

Now, back to the dialog handler, let’s implement the logic for these events. First, let’s define a helper method to allow us to easily update our inputErrors object:

setInputError helper method

This method takes the input element and a boolean representing the presence or absence of an error. Next we use the input element to access the formErrors model. Next we extract the inputErrors object and copy it using rest syntax. After that, we add a property to the inputErrors object using the id of the input element and set it to either true or false. Finally, we update the model with the new error data.

Now let’s use the two validation events to update our inputErrors object, as shown below:

Validation event handlers

In both cases, we simply retrieve the element which either passed or failed the validation. Then we call our helper method to set the property to either true or false.

And there we have it! If you go to the add review form and trigger a validation error, you’ll see that the submit button gets disabled. Also, if you resolve an error, you’ll see that the button becomes enabled again.

Our form is almost complete. There’s just the matter of making it responsive to backend changes. There’s quite a lot of things to do on that topic, so we don’t have time to jump into it this week. I hope you’ll check out next week’s episode to learn all about it. Until then, happy coding!

Conclusion

In this episode we learned how programmatically build a SAPUI5 form using JavaScript, how to handle submission logic using the oData V4 model, and how to utilize the built-in validations that come along with the V4 model to disable the the submit button if any input values are invalid.

Next week we’re going to take a deep dive into the oData V4 metadata object and see how we can use it to make our form responsive to changes in our CDS on the backend.

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.

--

--

Brian Heise

Full Stack web developer employed at Liferay Japan