UI5 Advanced Programming Model – Make it Reactive (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 this part, I’m going to implement the following features by using Reactive Programming:
- Make a row in the skill table read-only as soon as the name and score is filled in.
- Add an empty line
- Automatically update the model in case the person object changes.
The BaseState is being used as a base for any State object. Now, for using reactive programming and enable this for each state, I have added a small generic piece of code in the BaseState. This logic will determines any changes in the JSONModel, which is part of the State, by using the eventhandler “attachPropertyChange”. This eventhandler contains all the information of the property that has been changed in the UI. Based on this information, I’m going to search (in the “data” object of the state) for a function in the object that’s being changed with the name of the property being changed followed with “Changed”. For example: if the property “Score” of the object “Skill” changes in the UI, the code will look for the function “ScoreChanged” in the Skill object.
In case of a nested object, the code will also check for a “Changed” function of the parent object. For example: the property “Score” is part of the “Skill” object which is again an entry of the “Skills” array of the “Person” object. So the code will check for a function “SkillsChanged” in the “Person” object. This is because of “Skills” is the parent property of the Skill object and it’s part of the “Person” object.
Last but not least, the code will also look for a “Changed” function in the state itself. Sometimes, it can be useful to have the changed function of a property in the state. This can be in case that you need to have access to other variables that are defined in the state or because you are maybe not using seperate objects. For example if “Score” changes in the UI, it will look for the function “ScoreChanged” in the state itself.
This magic is done by the following piece of code added in the constructor of the “BaseState”:
The full code of the “BaseState” can be found here: https://github.com/lemaiwo/UI5Con2019/blob/master/webapp/state/BaseState.js
If I now added the following functions to the “Skill” object, it will change the UI as soon as the “SkillName” or “Score” is filled in. When one of the fields is filled in, the “delete” button will be visible. When both fields are filled in, it will change to display mode:
Full code of the Skill object: https://github.com/lemaiwo/UI5Con2019/blob/master/webapp/model/Skill.js
As soon as there is no empty line anymore, we need to add an empty line. Therefor, I’ve added a function “SkillsChanged” to the Person object. “Skills” is a property of the “Person” object and the parent of the “Skill” object. As soon as one field in one of the Skill rows changes, it will call this function and add an emtpy line:
Full code of Peron: https://github.com/lemaiwo/UI5Con2019/blob/master/webapp/model/Person.js
Last step, getting rid of the manual refresh of the model with the “updateModel” function. Personally, I do not have a problem triggering this manual because this gives you more control on the refresh behavior. But I’m also aware of other developers that just don’t want to think of it and take care about it. For them, I’m going to make the “Person” object in the state observable to automatically update the model on each change.
To do this, I added the “observable-slim” library to my project:
Create a file for this library in the “libs” folder
Copy paste the code from this file into the created file
Define the library in the manifest:
In the state, I use this library to make the “data” object observable. With this library, I’m able to assign an eventhandler to the “data” object for every change in it. In this eventhandler I update the model and automatically it will be executed on every change in the “data” object. This means that I only need to define “updateModel” once:
Now the app is able to view Persons with their skills and also create a new person together with multiple skills. The app is even able to automatically switch a skill row from edit to display, adding a new empty line and only writing the “updateModel” once.
There is only one functionality left that’s not yet working, the “delete skill” button.
For this, I added a “deletePersonSkill” function in the “PersonState”. This will delete a skill, based on the index, from the current “Person” object. Thanks to observable library, I don’t need to update the model 🙂
I added the following function in the “BaseController” which I’m going to use to determine selected row index based on the source of the control:
In the controller of the “detail” view, I created an eventhandler for the delete button which will use the “getIndexFromPath” function to determine the selected row and pass it to the “deletePersonSkill” function in the “PersonState”
You can find the full demo project on GitHub:
I am really motivated after seeing your blog to create apps the way you have explained. I have one question regarding Freestyle apps
Can we create Freestyle apps with draft functionality?
In theory yes, but you will have to implement all the different requests to the backend yourself.
In case you use the fiori programming model from sap in the backend with bopf and so on, you can simply use the exact same http requests like fiori elements in your freestyle app.
If your backend is not with the fiori programming model from sap, then you will also need to foresee these functionality in the backend yourself.
I do have one question like is it possible to handle routing based on this? The usual case is user scans the data into field via barcode scanner and upon enter it should move to next page if data is correct.
Yes, but this is something I would handle in the controller by using the change event.