How to bind data to xml fragments in UI5
Introduction!
In this post I am going to talk all about fragments! but more specifically how we can set and display data within our SAPUI5 fragments properly.
Please be aware that It's been several months since I've done any blogs and about 10 months since I wrote anything on SAPUI5. I hope to write more on SAPUI5 for the rest of the year as I'm currently working on a project using SAPUI5, Business Technology Platform (formerly SAP Cloud Platform) and will be using things like BTP, APIM and Azure DevOps for automated deployments so stay tuned for blogs around these topics!
What is a fragment in SAP UI5?
A fragment in SAPUI5 is just a small snippet of our view code, this is re-usable and typically used for things like dialogs and fragments but can be used inside of existing views. Fragments do not have their own controller so all our JavaScript will live inside of the controller where we instantiate the fragment.
Getting started
The same as almost all of my blogs, I get started with a completely blank SAPUI5 project so nothing extra and nothing fancy. So if you're using WebIDE or Business Application Studio just use a standard Fiori template and I will be using a template from my own npm package ExpressUI5.
Create our button
Buttons are very easy, in our view (mine is called mainView) and I add the following XML inside of my content/ the area where I want my button to appear.
<Button press="onOpenFragment" text="Open Fragment" />
The 'press' attribute is the name of which function our button press will call. The 'text' attribute is the text which will appear inside of our button.
Create the function onOpenFragment and load the fragment
Inside of the controller in which our button is was placed (typically named the same as your view name) we will place the stub for our controller code.
onOpenFragment: function() {
}
Then we want to actually put in the code that calls the fragment, I should note here that those of you on older versions might see the method sap.ui.xmlfragment
but this has been deprecated since 1.58. and so I'll use the latest method sap.ui.core.Fragment.load
.
The great thing about Fragment.load is that it returns a promise something that I'm very happy about and have written a series on using promises for your oData calls.
So given that I've taken to assigning my fragment call in a separate function, this function can then be called in your lifecycle events like onInit or onAfterRendering as I will showcase below
onInit: function() {
this.loadMyFirstFragment();
},
loadMyFirstFragment: function() {
if (!this.oMyFirstDialog) {
this.oMyFirstDialog = Fragment.load({
name: "com.nathan.hand.fragment.bindings.view.fragments.myFirstFragment"
}).then(function (oMyFirstDialog) {
return oMyFirstDialog;
});
}
},
Given the above we can then open our fragment inside of our onOpenFragment function.
onOpenFragment: function() {
this.oMyFirstDialog.then(function(oDialog) {
oDialog.open();
});
}
The result should be a simple fragment that we created earlier and look like this:
Data binding
Now our most important thing that we want to do is bind data to our fragment once we have our fragment returned to us. I don't have any data setup in this example yet so let's just create a simple JSON Model and bind it to our view so we can see.
onAfterRendering: function() {
const oTextModel = new JSONModel({
myName: "Nathan",
});
this.getView().setModel(oTextModel, "textModel");
},
A simple model set inside of our 'onAfterRendering' function which will create a new model called "textModel" and set it to the view with the only variable being "myName".
In our view I just added in a text field and bound the model and variable like so:
<Text text="Hi {textModel>/myName}!" />
Which with some added house keeping just to make things like a little nicer I end up with:
<VBox>
<Text class="sapUiResponsiveMargin" text="Hi {textModel>/myName}!" />
<Button class="sapUiResponsiveMargin" press="onOpenFragment" text="Open Fragment" />
</VBox>
Which gives us a nice little message as clearly shown here:
Now let's try in the fragment!
First of all, what happens if we just place our text control inside of our fragment?
I took my text control and added it to our fragment, just copy and pasting the exact same XML code and this was the result:
As you can see a completely empty fragment, even though we had the view code as shown below:
<core:FragmentDefinition xmlns="sap.m" xmlns:core="sap.ui.core">
<Dialog
title="Hello fragments!">
<Text class="sapUiResponsiveMargin" text="Hi {textModel>/myName}!" />
</Dialog>
</core:FragmentDefinition>
Binding the model
There are two ways that we can show our data in our fragment which I'm going to discuss below, both have their different use cases and can be mixed.
Adding a dependent
To add our fragment as a dependent to our view we need to go back to our loadMyFirstFragment
function that we defined up above.
First we want to add our fragment to our view right? So we need to define our view as const oView = this.getView();
inside of our loadMyFirstFragment
function.
Next we need to add add the fragment as a dependent with the following line oView.addDependent(oMyFirstDialog);
giving us the finished result of:
loadMyFirstFragment: function() {
const oView = this.getView();
if (!this.oMyFirstDialog) {
this.oMyFirstDialog = Fragment.load({
name: "com.nathan.hand.fragment.bindings.view.fragments.myFirstFragment"
}).then(function (oMyFirstDialog) {
oView.addDependent(oMyFirstDialog);
return oMyFirstDialog;
});
}
},
Now if we re-run our UI5 application we will have all of the models bound to our view are now available in our fragment as demonstrated by my fragment binding:
Binding models directly to our fragment
Alternatively we can bind a model directly to our fragment but only our fragment, this is better if we're doing something contextual such as opening a fragment that shows us more data for a specific row of data.
So inside of our promise for opening our fragment we can set a model directly to the fragment exactly the same as if we did to our view except to our fragment.
const oTextModel = new JSONModel({
myName: "Leon",
});
oDialog.setModel(oTextModel, "textModel");
This leaves our full function looking like this:
onOpenFragment: function() {
this.oMyFirstDialog.then(function(oDialog) {
const oTextModel = new JSONModel({
myName: "Leon",
});
oDialog.setModel(oTextModel, "textModel");
oDialog.open();
});
}
Note that while we're reusing the exact same model name, the one set to the fragment directly takes priority as it was set last, and has no affect on our view model as shown in this screen shot:
Can I do both?
Yes! we can bind in both ways, giving our fragment access to either models created in the view or the fragment directly but remember not to overwrite the model names.
Conclusion
That's it! We've shown off how to bind data either directly to our fragment, alternatively ensuring that our fragment is able to use the views models, or use the best of both worlds.
Everything has it's use case and how we bind and use data in our fragments is important, while this tutorial was a very simplistic example I hope that it's shown you how to bind and use data in a fragment.
Once again I've had a bit of a break from blogging for a while, so please let me know if you've noticed any mistakes/ issues and if you'd like me to cover another topic. I'm back in action using SAPUI5 deployed on Business Technology Platform (formerly SAP Cloud Platform) and using some of the latest tools such as APIManagement so I'm sure to do more blogs on those things soon.
Thanks for reading and thank you for all the people who have reached out on platforms to show their appreciation for my posts, it's really helped motivate me to write some more :).
···