Skip to Content
Technical Articles

UI5 Advanced Programming Model – Service layer (UI5con 2019)

This blog is part of a blog series about my UI5con presentation about the UI5 Advanced Programming Model at Walldorf in 2019:

Implementing the Service layer
Implementing the model layer
Implement Create
Make it Reactive
GitHub project

In this blog I’m going to start on implementing the UI5 Advanced Programming Model. I’m going to start from the Fiori Master-Detail template in SAP Web IDE. In this app, I’m going to add the Service layer and consume it by the detail controller. After this blog, I will have the following architecture:

Start by generating a basic app from the wizard in the SAP Web IDE:

Fill in a name for the project, title and namespace. (I will use “be.wl”, be careful when copy pasting code when you’re using a different namespace)

I’m using an odata service for this that contains two entities (Person and Skill) with a one to many relation. This model can have multiple persons and each person can have zero,, one or more skills. Here you have all the details to create your own service for following the steps.

Bind the Object Collection to the “Persons” entityset and the “Firstname” property to the title. In “Line Item Collection” use the association with the skills of a person. Each line will show the name of the skill with the score.

Click on Finish and the wizard will generate you an app that looks like this:

Now, I want to improve this app to do more than just viewing persons and their skills.  I also want to possibility to create new persons and assign skills to it. Creating a new persons with skills should be done at the same time. For this, I will use a deep create which is not possible with the “createEntry” function of the OData model. Because of this, I’m going to implement the UI5 Advanced Programming Model. Implementing this programming model will only impact the detail page. This is the only page that will require some changes. The master list has everything it needs and the OData model does a perfect job here. The detail page requires some custom logic with a client side model to make it fit to my needs. The first step of adapting the programming model  is to add a service layer. 

In the service layer, I will add a CoreService object which contains generic function that wraps every function of the OData model into promises. 

This CoreService will not be used directly. It will be used to extend from and create more specific functions that consume the generic wrapper functions and allow us to use promises all the way 🙂  

Let’s start by adding the CoreService object in the Service folder:

The full code can be found here:

As this contains wrapper functions for the OData model, it requires the OData model as part of the constructor:

Next, I created a PersonService which extends from the CoreService object.

 ], function (CoreService, Sorter) {
 	"use strict";

 	var PersonService = CoreService.extend("be.wl.UI5AdvancedProgramingModelApp.service.PersonService", {
 		constructor: function (model) {, model);
 	return PersonService;

This service will contain some more specific functions related to the Person service, like “getPerson”. The line “this.odata(sObjectPath).get(mParameters)” will execute the “read” operation of the OData model but wrapped into a promise. “sObjectPath” contains the path of a specific person object while “mParameters” are the parameters required by the read operation. The parameters are the same as defined in the UI5 documentation for the read operation.

getPerson: function (id) {
	var sObjectPath = this.model.createKey("/Persons", {
		Id: id
	var mParameters = {
		urlParameters: {
			$expand: "PersonHasSkills"
	return this.odata(sObjectPath).get(mParameters);


Full code of the person service can be found here:

To use this person service, I’m going to create an object of the person service in the component.js and pass the OData model to it. This is done in the component to have one instance of the object for the full app. 

I also added a function “getService” to make it easier to access this service object from other controllers.

 this._oPersonService = new PersonService(this.getModel());
 getService: function (sService) {
 	return this["_o" + sService + "Service"];


Now, I want to use the person service in detail controller to get the person instead of using the OData model.

First, I’m going to put functions in comment that I don’t need anymore because they are related to the OData model:

In the onInit function, I get the PersonService instance from the component by using my function “getService” for this. 

Also remove line 35, this is not needed anymore because I’m not using the OData model anymore here: 


In the “_onObjectMatched” function, I’m not going to use the “bindView” function anymore. Instead I use the person service and call the “getPerson” function. As soon as I have the information of the person, I’m going to put it in a JSON model and bind it to the view.

this.getOwnerComponent().oListSelector.selectAListItem("/" + sObjectPath);
// this._bindView("/" + sObjectPath);
this.PersonService.getPerson(sObjectId).then((result) => {
	this.setModel(new JSONModel(, "pers");

This requires me to change the bindings in the detail view with the named JSON model. It also impacts the association to the skills of the person, it requires “results” behind it:

The app will still look the same 🙂

Even though the app looks the same, we have already more control of the detail page and we’re using promises 🙂

Next step is to implement the model layer:

You can find the full demo project on GitHub:

You must be Logged on to comment or reply to a post.
  • Hi Wouter Lemaire,

    I liked your blog, it’s very complete, but I was wondering why during the implementation of the OData Model (CoreService.js), you were using AJAX calls and not OData model calls that you encapsulate in a promise? Such as this :

    read: function(){
      return new Promnise(function(resolve, reject){"/someEntitySet('withKey')", { 
            success: function(response){ resolve(response); },
            error: function(error){ reject(error); }
         } );

    In that manner, you get the synchronous mechanism?!


    • There are actually no AJAX calls. The ‘ajax’ you see in the CoreService is just an object property (object ‘core’ inside function ‘odata’) whose value is a function that wraps the OData model calls you are referring to.


      Wouter Lemaire, let’s assume that for some reason exactly the same ‘Person’ data is also needed inside the Master controller, at roughly the same time it is requested in the Detail controller. How would you access/get it in the Master controller? How could we avoid a duplicate ‘get’ request to the oData service?