Utility files in SAPUI5
Something I see a lot in UI5 applications is chunks of code being repeated either within the same application or within the same suite of applications. Using a utility file to manage this code separately from your business logic (A.K.A your controllers) means that the separation of concern is much more clear, especially for developers new to the codebase.
When I start building a new application, I don't create any utility files. I wait until my code starts to become less manageable and then I start to abstract whatever is getting in the way. One of my most common utility files is a data wrapper, which is something my friend and colleague Nathan Hand has been doing for a very long time.
Data Wrapper utility file
This utility file houses all of the oData calls for an application and wraps them in a Promise, making it much easier and more readable when you're picking through a tricky bug in your controller.
Creating a utility file
In your UI5 application, create a folder under webapp/utils
and then create a JS file named dataWrapper.js
.
Now that we have our utility file, we can use the below snippet as the template for the file
Now we can work on adding our data functions, I'm going to write some examples of CRUD operations for todo items, like a todo list.
Wrapping oData in a Promise
You should be quite familiar with how to read an entity set from a service model.
You will notice in the example above that I've only got a comment for the success and error callbacks. Typically this is where we bind a function or write some code that will handle the returning data/error. However, we're going to wrap this piece of code in a Promise and this will make a really clear distinction between our definition (where and how we get the data) vs what we do once we have our answer from the service.
In our dataWrapper.js
we will add a function to read some data from our service. We're going to write some additional code that wraps the above example in a Promise too.
The above example will make an oData read request to the /ToDoListSet
of the MyService
model, upon receiving a response it will resolve the Promise with either; the oData response, upon success, or the error, upon failure.
You can create a function for every operation you want to run. For this example, we only have one entity set so we would have the following functions:
- Read (returnTodoItems)
- Create (createTodoItem)
- Update (updateTodoItem)
- Delete (deleteTodoItem)
This is where the value of this approach helps, what we have done in our data wrapper is define a function to fetch data. The implementation of this function (or entire utility file) is a separate concern.
Using the utility file
Now that we have a data wrapper utility, we need to import it into our controller to use it. So from your controller, you need to import the utility file. My example is using the application namespace Leon.Hassan
.
Importing the utility to a controller
In the above example, lines 4 and 9 are the salient points. I have imported the data wrapper utility and included it in my controller function signature. Now that I have successfully imported the utility, we can use it in our controller.
Invoking the utility
In the example above, the utility is imported as dataWrapper
so I can call any functions in the utility from this object.
In the above example, I call the returnTodoItems
function from the data wrapper. Once a response has been received, it will resolve the promise and trigger the promise chain in our controller. The promise chain is the then
and catch
functions chained from the promise, they will not be triggered until a response is received and the promise is fulfilled.
If the service returns an error, it will skip straight to the catch
function and render the error in a message box component.
If the service returns data, it will create a JSON model from the data and bind it to the view. If an error occurs in your then
function, it will skip to the catch
function and behave in the same manner as if the service had returned an error. This can happen if you write bad code when receiving your oData.
Other use cases for utility files
In my experience, Utility files are underused in favour of throwing all your code into the controller. It's also my experience that trying to get to grips with a codebase that has controllers with upwards of 10k lines of code is; time-consuming, frustrating and is usually endemic of a wider problem.
I've found that for most applications, I create utility files for data operations, fragment management and data exports. Utility files don't just have to be for bits of code you want to use more than once, they're also very useful for separating your definition from your implementation.
Leave a comment
Let me know what you think of this approach. Have you used it before, did you love it or hate it? Have you got a better way of managing your code? Let me know in the comments, or tweet me.