Motivation
First time we develop complex UIs/Views in SAPUI5 could write 1000-2000 lines in a view. Table creations, forms, layouts, etc. could produce several lines of code. Also we want to standarize our components with same aspect, same styles, same button icons, etc.
SAPUI5 offers a simple mechanism to reuse code: Fragments (https://sapui5.netweaver.ondemand.com/sdk/#docs/guide/Fragments.html).
This blog describes a simple example to create two SAPUI5 tables with one Fragment and reuse table code creation.
NOTE: As of the initial release of Fragments in version 1.15.0 of SAPUI5, they are marked as experimental, so the API can change at any time and they are not guaranteed to work properly.
Step by step
1. Declare your new fragments package.
Remember load new fragment resources in your html file:
sap.ui.localResources("fragment");
2. Create a fragment
This fragment creates a table with data binding, different type columns, etc.
sap.ui.jsfragment("fragment.Table", {
createContent: function(oController) {
//Obtain data from controller
var aData = oController.fragmentInitData.data;
//Create an instance of the table control
var oTable = new sap.ui.table.Table({
title: oController.fragmentInitData.title, //Obtain title from controller
visibleRowCount: 7
});
//Obtain column definition from controller
for (var i=0; i<oController.fragmentInitData.columns.length; i++) {
var template;
//Define the columns and the control templates to be used
if (oController.fragmentInitData.columns[i].type == "Text")
template = new sap.ui.commons.TextView().bindProperty("text", oController.fragmentInitData.columns[i].bindingPath);
else if (oController.fragmentInitData.columns[i].type == "Rating")
template = new sap.ui.commons.RatingIndicator().bindProperty("value", oController.fragmentInitData.columns[i].bindingPath);
else if (oController.fragmentInitData.columns[i].type == "Checkbox")
template = new sap.ui.commons.CheckBox().bindProperty("checked", oController.fragmentInitData.columns[i].bindingPath)
if (template != undefined) {
oTable.addColumn(new sap.ui.table.Column({
label: new sap.ui.commons.Label({text: oController.fragmentInitData.columns[i].title}),
template: template,
sortProperty: oController.fragmentInitData.columns[i].bindingPath,
filterProperty: oController.fragmentInitData.columns[i].bindingPath,
width: "200px"
}));
}
}
var oModel = new sap.ui.model.json.JSONModel();
oModel.setData({modelData: aData});
oTable.setModel(oModel);
oTable.bindRows("/modelData");
//Initially sort the table
oTable.sort(oTable.getColumns()[0]);
//Bring the table onto the UI
return oTable;
}
});
3. Use your new fragment
In this view, we reuse the previous fragment twice:
//Define some sample data
var aData = createSampleData();
//Define fragment init data
oController.fragmentInitData = {
title: "This is a Table Fragment Example",
data : aData,
columns: [{title: 'Last Name', bindingPath: 'lastName', type: "Text"},
{title: 'Name', bindingPath: 'lastName', type: "Text"},
{title: 'Selected', bindingPath: 'checked', type: "Checkbox"},
{title: 'Rating', bindingPath: 'rating', type: "Rating"}]
};
//Instantiate fragment.Table with this init data "oController.fragmentInitData"
var table1 = sap.ui.jsfragment("fragment.Table", oController);
//Define some sample data again
var aData2 = createSampleData();
//Define fragment init data. Different init data this time (different title and less columns).
oController.fragmentInitData = {
title: "This is a Table Fragment Example 2",
data : aData,
columns: [{title: 'Last Name', bindingPath: 'lastName', type: "Text"},
{title: 'Name', bindingPath: 'lastName', type: "Text"}]
};
//Instantiate fragment.Table with this init data "oController.fragmentInitData"
var table2 = sap.ui.jsfragment("fragment.Table", oController);
var oDivider1 = new sap.ui.commons.HorizontalDivider("divider1");
oDivider1.setHeight(sap.ui.commons.HorizontalDividerHeight.Large);
//Present data into VerticalLayout
var oLayout = new sap.ui.layout.VerticalLayout({
content: [table1, oDivider1, table2]
})
Result
EDIT: JSBin Example
We could reuse our JS code with functions (for example: createTable()), protyping “classes”, etc. This is a different SAPUI5 standard way to create common Fragments to be reused in our apps as the same way with “Views”.
Enjoy! 😉
Thank you.
Thanks Rauf 😉 Take a look to How to create custom control from scratch Another different way to reuse SAPUI5 custom code.
Kind regards
Dear Angel,
thanks for the great introduction to fragments, this looks really useful! Just a quick question: Why do you create the data which you pass to the fragment as a property of the controller? Couldn’t you just pass the ‘fragmentInitData’ into the fragment’s constructor?
Warm regards
Marco
Thanks Marco,
I’ve tried passing fragmentInitData on fragment constructor but I haven’t found other way to instantiate fragment than this:
var table2 = sap.ui.jsfragment(“fragment.Table”, oController);
Fragment constructor receives oController on creation. I’ve cloned JSBin code, if you could suggest how we could acchieve this, it will be very useful:
JS Bin – Collaborative JavaScript Debugging
Thanks!
Dear Angel,
here is my proposal, just passing the fragmentInitData to the constructor: http://jsbin.com/lebayiku/2/
Warm regards
Marco
Thanks Marco! It’s interesting know that works.
An appreciation I think this example is working because as I think JS is a typeless language, it is possible to facilitate fragmentIniData parameter instead of oController. But the purpose of Fragment.createContent method could require oController in order to interact with some methods or logic of your UI:
SAPUI5 SDK – Demo Kit
Defined in: <sap/ui/core/Fragment.js>.
Parameters:
What do you think about it? May be I’m wrong
Kind regards!
Dear Angel,
I think you’re right – there might be a case where you need to pass the corresponding controller to the fragment. In your scenario, however, it just seems to introduce an indirection which you don’t need.
Maybe it is a good idea to pass the controller when scenarios get more complicated and you need to handle events from the fragments with controller methods. So I guess there is no “right” and “wrong” here 😉
Warm regards
Marco
Hi Marco,
You are right, practically most of the times we require controller object. I have authored a blog where I have explained fragment with controller instance for event handling. In that example method “doSomething()” is supposed to be defined in controller.
SAPUI5 Dialog(with BusinessCard) As XML Fragment along With Controller
Hi, one interesting question:
assume that
1) u have xml fragment with predefined control id
2) u need to use it several times per view
when u try to add second+ time the jsconsole shouts at u:
Error: Error: adding element with duplicate id
How did sapui5 manages this situation? it has the fragment system, but does not have the way control id’s are rendered in a correct way.
What can we do in this situation?
Good example, but I’m still struggling with the use of the same fragment with two views; this get me the error of ‘duplicate IDs’ of the inner variables..
tried to use the destroy, but doesn’t fix the issue..
Anyone know what is the canonical process to do this?
Hi Marc,
Avoid using ids on fragments, or pass a parameter in order to create the IDs dependant on prefixes eg: prefix + ‘_my_text_inside_fragment’.
Hope this helps,
Kind regards
Hi,
thank you for the reply Angel,
So, I can’t use a fragment as a commum method over diferents view..?
That leads me to code replication and all its issues.. =/