Show TOC

Step 19: Reuse DialogsLocate this document in the navigation structure

In step 16 we created a dialog as fragment, because we wanted it to be reusable across views or across our whole app. But we placed the logic for retrieving the dialog instance and for opening and closing it respectively in the controller of the HelloPanel view. Sticking to this approach would require copying and pasting the code to the controller of each view that needs our dialog. This would of course cause an undesired code redundancy we definitely want to avoid. In this step, we will implement the solution to this problem: We now expand our reuse concept and invoke the dialog at component level.

Preview
Figure 1: The dialog is now opened by the component (no visual changes to last step)
Coding

You can view and download all files in the Explored app in the Demo Kit under Walkthrough - Step 19.

webapp/Component.js
sap.ui.define([
	"sap/ui/core/UIComponent",
	"sap/ui/model/json/JSONModel",
	"sap/ui/demo/wt/controller/HelloDialog"
], function (UIComponent, JSONModel, HelloDialog) {
	"use strict";
	return UIComponent.extend("sap.ui.demo.wt.Component", {
		metadata : {
			manifest : "json"
		},
		init : function () {
			// call the init function of the parent
			UIComponent.prototype.init.apply(this, arguments);
			// set data model
			var oData = {
				recipient : {
					name : "World"
				}
			};
			var oModel = new JSONModel(oData);
			this.setModel(oModel);
			// set dialog
			this.helloDialog = new HelloDialog();
		},

		exit : function () {
			this.helloDialog.destroy();
		}
	});
});

The dialog instantiation is refactored to a new helper object that we can directly access through the component. In its initialization method we store a public reference to it that can be accessed from every controller.

We destroy the helper object when the component is being destroyed, for example, when the user leaves the app in the SAP Fiori launchpad. Therefore, we add the exit method that is called by SAPUI5 automatically to the component.

webapp/controller/HelloDialog.js (New)
sap.ui.define([
	"sap/ui/base/Object"
], function (Object) {
	"use strict";

	return Object.extend("sap.ui.demo.wt.controller.HelloDialog", {

		_getDialog : function (oView) {
			// create dialog lazily
			if (!this._oDialog) {
				// create dialog via fragment factory
				this._oDialog = sap.ui.xmlfragment("sap.ui.demo.wt.view.HelloDialog", this);
				// connect dialog to view (models, lifecycle)
				oView.addDependent(this._oDialog);
				// detach the dialog from the view's lifecycle
				oView.attachBeforeExit(function () {
					oView.removeDependent(this._oDialog);
				}.bind(this));
			}
			return this._oDialog;
		},

		destroy: function () {
			if (this._oDialog) {
				this._oDialog.destroy();
			}
		},

		open : function (oView) {
			this._getDialog(oView).open();
		},

		onCloseDialog : function () {
			this._getDialog().close();
		}
	});

});

The implementation of the HelloDialog reuse object extends a base object to inherit the core functionality of SAPUI5.

Our _getDialog method is refactored from the HelloPanel controller and instantiates our dialog fragment as in the previous steps. Note that now the reuse object is passed on as a controller to the fragment.

When the view is being destroyed, we have remove the dialog from the dependent aggregation of the view again. Otherwise the reuse dialog would be destroyed automatically as it's part of an aggregation of the view, but the reuse dialog should only be destroyed when the whole application component is being destroyed. We therefore attach to the beforeExit lifecycle event of the view and remove the dependent aggregation right before the view is destroyed.

In the exit lifecycle method of SAPUI5 that is called when the component is being destroyed, we destroy our internal dialog instance if it has been created. Now the lifecycle of the dialog is properly connected to the application’s lifecycle.

The open method now contains our dialog instantiation. The first time the open method is called, the dialog is instantiated. The oView argument of this method is used to connect the current view to the dialog. We will call the open method of this object later in the controller.

The onCloseDialog event handler is simply moved from the HelloPanel controller to the reuse object.

webapp/controller/HelloPanel.controller.js
sap.ui.define([
	"sap/ui/core/mvc/Controller",
	"sap/m/MessageToast"
], function (Controller, MessageToast) {
	"use strict";
	return Controller.extend("sap.ui.demo.wt.controller.HelloPanel", {
		onShowHello : function () {
			// read msg from i18n model
			var oBundle = this.getView().getModel("i18n").getResourceBundle();
			var sRecipient = this.getView().getModel().getProperty("/recipient/name");
			var sMsg = oBundle.getText("helloMsg", [sRecipient]);
			// show message
			MessageToast.show(sMsg);
		},
		onOpenDialog : function () {
			this.getOwnerComponent().helloDialog.open(this.getView());
		}
	});
});

The onOpenDialog method now accesses its component by calling the helper method getOwnerComponent. When calling the open method of the reuse object we pass in the current view to connect it to the dialog.

webapp/view/App.view.xml
<mvc:View
	controllerName="sap.ui.demo.wt.controller.App"
	xmlns="sap.m"
	xmlns:mvc="sap.ui.core.mvc"
	displayBlock="true">
	<App class="myAppDemoWT">
		<pages>
			<Page title="{i18n>homePageTitle}">
				<headerContent>
					<Button
						icon="sap-icon://hello-world"
						press="onOpenDialog"/>
				</headerContent>
				<content>
					<mvc:XMLView viewName="sap.ui.demo.wt.view.HelloPanel"/>
				</content>
			</Page>
		</pages>
	</App>
</mvc:View>
We add a button to the header area of the app view to show the reuse of the hello world dialog. When pressing the button the dialog will be opened as with the button that we previously created in the panel.
webapp/controller/App.controller.js
sap.ui.define([
	"sap/ui/core/mvc/Controller"
], function (Controller) {
	"use strict";

	return Controller.extend("sap.ui.demo.wt.controller.App", {

		onOpenDialog : function () {
			this.getOwnerComponent().helloDialog.open(this.getView());
		}
	});

});

We add the method onOpenDialog also to the app controller so that the dialog will open with a reference to the current view.

Conventions
  • Put all assets that are used across multiple controllers in separate modules.