SAPUI5

How to Extend a Standard SapUI5 Control

There comes a time in every project’s lifecycle when you realize you should start using extended controls in your XML views.

For example,using the same Value Help dialogs several times in the same project can be quite common, but copying and pasting the same code would be difficult to maintain.

Also Read: SAP Certifications

Another case would be when you’re assigning a custom “displayFormat” to your datepickers repeatedly throughout your project for every datepicker in several XML views.

In this post we will take a standard control, set some of its properties, add a bit of functionality to it and then reuse it as many times as needed in our project.

Extending a SAPUI5 Standard control ,or as some people would call Inheriting from it will in the long run, simplify your code and make it easier to maintain.

In our example we will take the standard Input control and extend its functionality by :

  1. Attaching a value help dialog with a list of materials.
  2. Passing parameters from its parent XML view.
  3. Handling selection events.
  4. Adding basic input validation.

When we’re done we’ll be able to use the extended control over and over in our XML views without copying and pasting the same code in our views and controllers.

All of the above functionality will be encapsulated in a single file called “SelectMaterial.js” which will provide the new control that we can then use in our XML view like so:

<my:SelectMaterial value="{viewModel>/matnr}" lgort="3001" onSelect="onMatnrSelect" width="160px"/>

So the first step is to add a new folder called “Controls” under webapp.

Under “Controls” add a file called “SelectMaterial.js” with the following code:

sap.ui.define([
	"sap/ui/core/Control",
	"sap/m/Input",
	"sap/m/MessageToast",
	"sap/ui/model/Filter"
], function(Control, Input, MessageToast, Filter) {
	"use strict";

	return Input.extend("sapui5.demo.customcontrols.controls.SelectMaterial", {

		metadata: {
			properties: {
				value: {
					type: "string",
					defaultValue: ""
				},
				text: {
					type: "string",
					defaultValue: ""
				},
				showValueHelp: {
					type: "boolean",
					defaultValue: true
				},
				lgort: {
					type: "string",
					defaultValue: "0000"
				}
			},
			aggregations: {

			},
			events: {
				"onSelect": {
					allowPreventDefault: true,
					parameters: {
						"material": {
							type: "object"
						},
						"materialDescription": {
							type: "string"
						}
					}
				}
			},
			renderer: null
		},

		init: function() {

			Input.prototype.init.call(this);

			this.attachChange(this.onChange);

			this.attachValueHelpRequest(this.onValueHelpRequest);

			this.attachOnSelect(this.onSelect);
		},

		renderer: function(oRm, oInput) {
			sap.m.InputRenderer.render(oRm, oInput);
			oRm.write("<Label>");
			oRm.write(oInput.getText());
			oRm.write("</Label>");
		},

		setLgort: function(sValue) {
			this.setProperty("lgort", sValue, true);
			return this;
		},

		getLgort: function() {
			return this.getProperty("lgort");
		},

		onValueHelpRequest: function(oEvent) {
			// function name speaks for itself
			var that = this;
			// get selected Lgort
			var lgort = this.getLgort();

			if (!lgort) {
				MessageToast.show("Please Select Storage Location");
				return;
			}

			var oModel = new sap.ui.model.json.JSONModel();
			oModel.loadData("service/data.json");

			oModel.attachRequestCompleted(function() {

				that.setModel(oModel, "materials");

				var oTemplate = new sap.m.StandardListItem("DialogMatnrItems", {
					title: "{Material}",
					description: "{MaterialDescription}"
				});

				var oDialog = new sap.m.SelectDialog("dialogParentTableMatnr", {
					title: "Select Material",
					confirm: function(oConfirmEvent) {
						that.closeDialogMatnrItems(oConfirmEvent);
					},
					cancel: function(oCancelEvent) {
						that.closeDialogMatnrItems(oCancelEvent);
					},
					search: function(oEvt) {
						var sValue = oEvt.getParameter("value");
						if (!sValue || sValue === "") {
							oEvent.getSource().getBinding("items").filter([]);
						} else {
							var filter = new Filter("MaterialDescription", sap.ui.model.FilterOperator.Contains, sValue);
							var oBinding = oEvent.getSource().getBinding("items");
							oBinding.filter([filter]);
						}
					}
				});
				oDialog.setModel(that.getModel("materials"));
				oDialog.bindAggregation("items", "/results", oTemplate);
				oDialog.open();

			});

		},

		closeDialogMatnrItems: function(oEvent) {

			var oDialog = oEvent.getSource();
			oDialog.destroyItems();
			oDialog.destroy();

			if (oEvent.getParameter("selectedItem") && oEvent.getParameter("selectedItem").getBindingContext()) {
				var obj = oEvent.getParameter("selectedItem").getBindingContext().getObject();
				// save select dialog object in control properties

				this.setValue(obj.Material);
				this.setText(obj.MaterialDescription);
				// fire event to notify view of selection
				this.fireOnSelect({
					"material": obj.Material,
					"materialDescription": obj.MaterialDescription
				});

			}
		},

		onChange: function(oEvent) {

			// fires on focus leave or when user presses Enter

			if (oEvent.type === "sapfocusleave") {
				// ignore lost focus
				return false;
			}
			if (!oEvent.target.value) {
				// ignore if notempty control
				return false;
			}

			var lgort = this.getLgort();
			if (!lgort) {
				MessageToast.show("Please Select Storage Location");
				return;
			}

			// get control value and try to get its description from model
			var material = oEvent.target.value;
			var that = this;
			var oModel = new sap.ui.model.json.JSONModel();
			oModel.loadData("service/data.json");

			oModel.attachRequestCompleted(function() {

				that.setModel(oModel, "materials");
				var oData = oModel.getData();

				for (var i = 0; i < oData.results.length; i++) {
					if (oData.results[i].Material === material) {
						that.setValue(material);
						that.setText(oData.results[i].MaterialDescription);
						that.fireOnSelect({
							"material": material,
							"materialDescription": oData.results[i].MaterialDescription
						});
						return;
					}
				}

				MessageToast.show("Material Number " + material + " Does not Exist.");

			});

		},

		onSelect: function(oEvent) {
			
		}

	});
});

The magic begins here :

return Input.extend("sapui5.demo.customcontrols.controls.SelectMaterial"

We are inheriting all the goodness of the standard input Control and later on adding our own custom functionality.

The metadata section of our control is where we set our default properties. For example “showValueHelp” is set to true by default.

The “lgort” property is a custom property that will allow us to pass values to the control from our XML view. Its default value is “0000”.

The metadata section also defines a custom event called “onSelect” which we will fire to notify our view that the user has successfully selected a valid Material from the value help list. This event will also fire an Event object with the selected values “material” and “materialDescription”.

The next important thing to notice is the “init” function of the extended control. Here we need to attach the standard Input control’s basic events to our own custom functions.

this.attachValueHelpRequest(this.onValueHelpRequest);

In our function “onValueHelpRequest” we will query our data source of choice, populate and display a value help dialog.

Next, if the user has selected an item from the list, we will need to fire an event to let the view know about this.

this.fireOnSelect({
					"material": obj.Material,
					"materialDescription": obj.MaterialDescription
				});

We can then handle the event in our XML view :

<my:SelectMaterial value="{viewModel>/matnr}" lgort="3001" onSelect="onMatnrSelect" width="160px"/>

and of course in our view controller :

onMatnrSelect: function(oEvent) {
			
			// Called  whenever Matnr has been changed (either by pressing Enter, lost Focus,  or Value Help Selection )
			var matnr = oEvent.getParameter("material");
			var matnrDescription = oEvent.getParameter("materialDescription");

			// do something with selection 
			MessageToast.show("You selected :" + matnr + " " + matnrDescription);

			// view model is updated
			var model = this.getView().getModel("viewModel");
			MessageToast.show("Model Matnr =  :" + model.getProperty("/matnr"));
			
		}

Leave a Reply

Your email address will not be published. Required fields are marked *