UI5 Advanced Programming Model – Model 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||https://blogs.sap.com/2019/12/27/ui5-advanced-programming-model-service-layer-ui5con-2019|
|Implementing the model layer||https://blogs.sap.com/2019/12/27/ui5-advanced-programming-model-model-layer-ui5con-2019|
|Make it Reactive||https://blogs.sap.com/2019/12/27/ui5-advanced-programming-model-make-it-reactive-ui5con-2019|
In the previous part of this blog series, I’ve added a service layer into my UI5 app for the detail page. Because this still requires a lot of logic in the controller, like creating the JSONModel (including updating the JSONModel and so on), I’m going to add an additional layer. I’m going to add a layer that keeps the state of the app or a specific part of it. The JSONModel will be wrapped into the state to remove any logic related to bindings from the controller to the state.
In this example, I’m going to create a PersonState which comes in between the controller and the service layer. It could be possible to have several states in a complex app. For that reason, I’ve added reusable functions in some kind of basestate where every state can extend from.
First, created the BaseState in the state folder:
Full code of the “BaseState” can be found here:
For now, these are the important functions: (The other functions are part of making the app reactive which will be explained in the blog post about the making the app reactive)
Each state will use the base state as its core and extend from it. Every state will include its own JSONModel which will contain all the data that is being controlled by this state and used in the views. The function “getModel” takes care of the instantiation of the JSONModel and wraps this into the state. It will only put the object “data”, which will be provided by the state, into the JSONModel. This will avoid that there will be too much in the JSONModel.
The “updateModel” function is just a shorter version of the refresh function of the JSONModel.
Create “PersonState” as an extension of the “BaseState” in the “state” folder:
The state is the glue between the controller and the service. For knowing which service the state has to use, the state requires the service as part of the constructor.
In the constructor of each state, the state will also define the scope for the “data” object which will be used in the JSONModel.
The “BaseState” contains logic in the constructor to make the app reactive. This comes in the last part of this blog series. This logic requires that the “data” object is already defined. That’s why I have to declare the “data” object before calling the “BaseState”.
Full code of the PersonState:
Now, we can add a “getPerson” function to the state. This function will call the “getPerson” function in the service and put the result in the “data” object. In the end, it will also update the JSONModel to reflect the changes in the UI.
The “PersonState” needs to be initialized with the “PersonService”. This is done in the “Component.js” to have one unique instance of the state.
Again, I’ve added a “getState” function to easily access the state on other places in the app.
The detail controller should not use the “PersonService” anymore. This needs to be changed to the state.
In the onInit, I put the line that gets the “PersonService” in comment. Instead, I get the “PersonState” instance and use the function “getModel” to set the model of the view with the model in the state. This will create a JSONModel in the PersonState with scope of the data object. Everytime something changes in the data object in the PersonState, it will be reflected in the view after a refresh of the model without any action in the controller of the view. After these two lines, we will never access the JSONModel directly in the controller.
In the “_onObjectMatched” function, we can just use the “getPerson” function of the “PersonState” and the state will take care of updating the result in the UI.
The bindings in the view needs to be updated because I put the result of the “getPerson” in the property “Person” of the data object.
The app still works exactly the same but it’s now using a state between the controller and the service
Still, if we want to use the result from the service in the view, we need to use “results” to get the skills of a person. This is something I don’t like. Next to that, the data has no structure and the state could become very large in case of a complex apps that requires some data manipulation or other logic. Therefore, I want to take it one step further and divide the logic of the state into objects to have separation of concerns. This will end up in a virtual representation of the data model in the front-end similar to the data model in the backend.
For both objects, Person and Skill, I have a common function. Therefore, I’m going to create a “BaseObject”. This will be the starting point for each object to extend from.
The Person object has also some other functions for client side logic that are related to the Person object, like adding skills to a person locally or deleting a skill.
The most important part is the getJSON function, this will return the data of the Person object based on the format that the backend requires it. It will return all the different properties like defined in the OData model including associations like Skills of the person. The Skill object has also a getJSON function that will be used by the person object to build a deep entity structure. Later, when I’m going to create a new Person in the backend, I will be able to just call the getJSON function of a person and get the full object (including the skills) that matches the format in the backend.
Full code can be found here:
The Skill object is very similar to the Person object. It will again copy all the plain properties from the incoming data object into the current instance of the skill object. On top of that, I enrich the skill object with two properties to control the visualization of the skill table.
Next to that, some functions that contain specific logic which is not important for the moment.
Again, I have the function “getJSON” that returns the properties of the current instance of the Skill object in the structure that the OData service expects it.
Full code can be found here:
This is the copy function in the BaseObject that’s being used by Person and Skill:
Again, we need to update the binding of the skills table in the view. The skills are copied from the OData service into local Skill objects which is part of the Person object:
The app still works 🙂
Still looks the same but now has more structure behind the scene and is future proof for any advanced feature.
In the next blog, I’m going to show you the benefit of implementing these layers by implementing the “create” functionality: https://blogs.sap.com/2019/12/27/ui5-advanced-programming-model-create-ui5con-2019
You can find the full demo project on GitHub: