Skip to Content
Technical Articles

How to split code in SAPUI5 by using fragments and XML view

When we are going to create a huge SAPUI5 app with many huge views, splitting the huge views is one of the most important principles in design and implementation.

We can easily move some part of a view inside a XML fragment like the following:

<core:FragmentDefinition xmlns="sap.m" xmlns:l="sap.ui.layout" xmlns:core="sap.ui.core"
	controllerName="com.mjzsoft.FragmentTest.controller.Part1"> <!-- This part is redundant -->
	<VBox>
		<items>
			<Text text="This part is from XML fragment"/>
			<Button text="Call Function of Fragment" press=".Part1.onClick"/>
			<Button text="Call Function of Parent Directly" press="onClick"/>
		</items>
	</VBox>
</core:FragmentDefinition>

And we can add it in another XML file like the following:

<mvc:View controllerName="com.mjzsoft.FragmentTest.controller.View1" xmlns:html="http://www.w3.org/1999/xhtml" xmlns:mvc="sap.ui.core.mvc"
	displayBlock="true" xmlns="sap.m" xmlns:l="sap.ui.layout" xmlns:core="sap.ui.core">
	<App id="idAppControl">
		<pages>
			<Page title="{i18n>title}">
				<content>
					<l:VerticalLayout>
						<l:content>
							<core:Fragment fragmentName="com.mjzsoft.FragmentTest.view.Part1" type="XML" 
							id="prefix1"/>
							<!--By using id you can add a fragment multiple times as it is used as a prefix in elements id-->
						</l:content>
					</l:VerticalLayout>
				</content>
			</Page>
		</pages>
	</App>
</mvc:View>

But normally this is not enough and the event handler of the elements inside the fragments (like the buttons) will not call the functions inside the fragment’s controller but it will call the functions inside the parents view (Here View1). So if we move some parts of the view into a fragment and inject it inside the parent view, we actually still need to move the functions from parent view controller to another controller.

If you use the fragment for dialog this code split is easier as we can pass the controller as soon as we  create the dialog.

// Dialog1 uses the functions inside the parent controller
var oDialog1 = sap.ui.xmlfragment("com.mjzsoft.FragmentTest.view.myDialogFragment", 
               this);
// Dialog2 uses the functions inside of its own passed controller "com.mjzsoft.FragmentTest.controller.myDialogFragment"
var oDialog2 = sap.ui.xmlfragment("com.mjzsoft.FragmentTest.view.myDialogFragment", 
               "com.mjzsoft.FragmentTest.controller.myDialogFragment");				

But what if we want our fragment element inside the XML view also access its own controller and not the parent controller. Actually as we said already in first code box there is a line that is redundant in our fragment file. Actually this line controllerName="com.mjzsoft.FragmentTest.controller.Part1".

The reason is that it does not have any effect on where the elements are bind for their event handlers. To achieve this goad we have 2 options.

1) The first option is to create a variable in parent controller as an instance of the controller of the fragment. Actually something like the `Part1` in the following:

sap.ui.define([
	"sap/ui/core/mvc/Controller",
	"com/mjzsoft/FragmentTest/controller/Part1.controller"
], function (Controller, Part1) {
	"use strict";
	return Controller.extend("com.mjzsoft.FragmentTest.controller.View1", {
		Part1: new Part1(this), // this pointer is window here and not the Controller  
		onInit: function () {
			
		},
		onClick: function (oEvent) {
			console.log("I am in View1 Controller.");
		}
	});
});

By this technique the elements inside the XML fragment must call the functions of Part1 like this:

<Button text="Call Function of Fragment" press=".Part1.onClick"/>

if we remove .Part1. from the event handler name then it will call the functions inside the parent controller.

What are the pros and cons of this method?

Pros: We can call functions of both controller.

Cons: We cannot access the fragment controller object inside the parent controller. While we have defined Part1 variable in parent controller but it seems we will have 2 Part1 variables. One is created at the time the view is created and fragment elements are bound to that. And one other which is accessible by this.Part1 in the parent controller while is different from the one that elements are bound to.

If you like to download the whole code regarding this method you can access our github. Check the github for latest code.

Here is an example of what has been printed in console by pressing on the buttons.

 

2) The second option is to generate that part of the view in the onInit function and then inject it in the desired container or element:

onInit: function () {
	//Create new fragment with own controller
	var oFragmentController = new FragmentController(this);
	var oFragment = sap.ui.xmlfragment("com.mjzsoft.FragmentTest.view.Part1", oFragmentController);
	this.getView().byId("myContainer").addContent(oFragment);
	console.log(oFragmentController);
}

 

What are the pros and cons of this method?

Pros: We can call functions of both controller by passing the this pointer of the parent controller and set it as a class variable inside the controller. Please note for this we need to define a constructor inside the fragment controller.

Cons: We cannot directly access the functions inside the parent controller in the fragment XML file.

sap.ui.define([
	"sap/ui/core/mvc/Controller"
], function (Controller) {
	"use strict";
	return Controller.extend("com.mjzsoft.FragmentTest.controller.Part1", {
		parent: null,
		onInit: function () {
			console.log("onInit of fragment will not call.");
		},
		onAfterRendering: function () {
			console.log("onAfterRendering of fragment will not call.");
		},
		onClick: function (oEvent) {
			console.log("I am in fragment controller");
		},
		constructor: function(oArg){
			console.log("The fragment controller has been made");
			console.log("You can pass some Argument to be set!!!", oArg);
			this.oParent = oArg;
			return Controller.call(this);
		}
	});
});
3 Comments
You must be Logged on to comment or reply to a post.
  • hello

    thank you for this article.

    this something i have been thinking about for some time – best way to break up a large view.

    i have been thinking about using nested views rather than frafments. in this way the control is a module – everything is self contained and therefore reusable.

    any thoughts appreciated.

    cheers

    pat

      • What we suggested here is more or less nested view, just we explained more and with more details. It will never make the spaghetti code even prevent that. Makes the code clear and you don’t need to use event bus because you access to the parent controller. As I tested event bus, when we use the event bus we will not access to the same copy of the controller object. Actually we are accessing to a new copy of the targeted controller.