Skip to Content
Technical Articles

Part 1 – Custom Dynamic Form control based on OData Metadata Annotations

Overview

In this blog I am showcasing a Dynamic Custom Control which can be used to render a form in any UI Application with minimal code. It uses the OData metadata annotation to render control in form field. It supports message handling and perform validation on correctness of values based on data type of that field.

There is a similar kind of Smart Form control available but this Form Control offers far more flexibility and developer have to write much less code to declare it.

So lets start with the looking at Form control in Display and in Edit Mode.

                                                Figure 1: Sales Order Form in Display Mode

 

                                                Figure 2: Sales Order Form in Edit Mode

 

Control Features


Details of Control Type Supported in the control

This control supports varied control types. The usage of these control type and their configuration are based on annotation provided in the metadata of the property. By default this control uses 8 control types which are used commonly in any form. These are listed below along with what annotation to be used.

Additionally this control also supports loading of control type different from default one or overriding of default behavior of these control types (Refer control property propertyMap for more details  regarding this).

Date Picker


Assign Edm.Type = Edm.DateTime or Edm.DateTimeoffset in the entity to that.  an

Note:- Also Provide a custom annotation sap:display-format="Date"





Time Picker

Assign Edm.Type = Edm.Time in the entity.

Note:- Do not provide sap:display-format="Date" annotation


 

Date Time Picker


Assign Edm.Type = Edm.DateTime or  Edm.DateTimeoffset in the entity.

Note:- Do not provide sap:display-format="Date" annotation


 

Text Area


MaxLength in the entity should be more than 50 to render a TextArea control.



 

Amount-Currency Type Control


Assign sap-unit=’Name of the Currency property’ annotation to to Amount property as it is              done in this example – sap-unit=”CurrencyCode” is assigned to NetAmount.

 

Yes-No Option Control (CheckBox like)


Assign sap-isCheckBox=’true’ annotation to the property for which a ComboBox with option            yes/no is required. It can be interpreted as a Checkbox.





ValueHelps


Assign sap:value-list=”fixed-values annotation to the property for which ValueHelp is to be            rendered. Also assign the name of the property as a reference to the ValueHelp Entity to                    load data in the ValueHelp.





 

Input


By default Input control will be loaded




Field Control

This control uses Field Control feature of OData to dynamically update the visual configuration of Control type. These 4 types of Configuration are as follows:-

1. Mandatory

Pass 7 to Field Control value (here Uxfc01) at the time of fetching data.





2. Read only

Pass 1 to Field Control value (here Uxfc01) at the time of fetching data.

Additionally it also uses sap:creatable and sap:updatable annotation in entity to make a field as read only.

Note: Later will take preference over the former in case of conflict.


3. Hiding fields

Pass 0 to Field Control value (here Uxfc01) at the time of fetching data.

Additionally it also uses sap:visible annotation in entity to hide a field.

Note: Later will take preference over the former in case of conflict.

4. Optional

Pass 3 to Field Control value (here Uxfc01) at the time of fetching data.

In this case the default behavior of the control will be used.

Validations & Checks

Control itself validates whether any field in the form is invalid or not.

It verifies that all the mandatory fields are having values, fields are having valid values or not (based on Edm.Type)

 

Properties available in this control

Name Type Default Value Description
entityTypeName
string Specifies Name of the OData entity
title
string null Title of the form
mode
string null Specifies mode in which control needs to be Rendered. “E” for Edit mode and “D” for display Mode
includeOnlyFields
string[]
[]

Specifies Explicit list of fields to display. All other fields will be ignored.

Example of Usage

<Form includeOnlyFields=”F1,F2,F3″ />

useSplitLayout
boolean true Specifies whether to use split form layout (with two form containers)
required
boolean false If set to true then all the fields in the Form becomes mandatory
odataModelName
string

Specifies name of the OData Model.

Note:- Use this property only when odata model has some name

dataPath
string Specifies the path in the binded model(Name of the model is stored in the property modelName) where Form data is stored
modelName
string Name of the Binded Model where data of the Form is stored
mandatoryFields
string[] []

Specifies name of the field which have to be marked as mandatory and those are not configurable by the user.

Example of Usage

<Form mandatoryFields=”F1,F2,F3″ />

propertyMap

 

object

Using this property a user can handle:-

1. Custom Behavior of default types*

Using the user can override the the default behavior of the control type and provide their own custom behavior.

2. Custom Control type factory**

Using this user can even user their own control factory if the default one does not suites to their requirement.

*  Let say there is requirement to override enabled property of LifeCycleStatus field then it can be done in following manner

{        
  LifeCycleStatus:{
      enabled: // Handling of enable property
  }
}

 

** Let say there is requirement to use a checkbox type control instead of default Yes/No ComboBox for field then it can be done in following way

{
  CreatedByBp: {
     editFactory: // bind method name which returns checkBox control factroy which                                          // will be rendered in edit mode,

     displayFactory: // bind method name which returns checkBox control factroy                                                 //  which will be rendered in display mode
  }
}

 

Control Events

changeFieldValue – The event is fired when value in any form field control is changed

Visibility: public 

Param Type Description
oControlEvent sap.ui.base.Event
getSource
sap.ui.base.EventProvider
getParameters object
event
sap.ui.base.Event
event of the control which is fired by particular form fied
field
string
The name of an updated field
value
string
New field value
entityType
string
Name in the Entity type passed in the property entityTypeName

Now lets create a Sales Order App which is using this form control.

OData project created for this example
                                                            Soheaderdata Entity

Fields to be shown in the control is determined by the properties defined in the Entity(here Soheaderdata). Please note that properties shown in Figure 1 & 2 are same as defined in the Entity.

Value defined in the label section of the entity is used as Label of form field.

Code to fetch data

Download source code of the Form Control 

Source Code Snippet for Sales Order App

SalesOrder.view.xml
<mvc:View controllerName="sa.sap.sample.reuse.form.controller.SalesOrder" 
		  xmlns:html="http://www.w3.org/1999/xhtml"
		  xmlns:mvc="sap.ui.core.mvc" 
		  displayBlock="true" 
		  xmlns="sap.m" 
		  xmlns:c="sap.custom.controls.form">
	<App id="idAppControl">
		<pages>
			<Page title="Custom Form Sample">
				<content>
					<c:Form id="idSalesOrderDetails" 
								  entityTypeName="Soheaderdata" 
								  title="Sales Order Details" 
								  editable="true" 
								  visible="true" 
								  mode="{FormDataModel>/displayMode}"
								  modelName="FormDataModel" 
								  dataPath="/data" />
				</content>
				<footer>
					<Bar>
						<contentRight>
							<Button text="Save" visible="{=${FormDataModel>/displayMode} !== 'D'}" press="onSalesOrderSave" />
							<Button text="Edit" visible="{=${FormDataModel>/displayMode} === 'D'}" press="onSalesOrderEdit" />
							<Button text="Display" visible="{=${FormDataModel>/displayMode} !== 'D'}" press="onSalesOrderDisplay"/>
						</contentRight>
					</Bar>
				</footer>
			</Page>
		</pages>
	</App>
</mvc:View>
Control declaration

SalesOrder.controller.js
sap.ui.define([
	"sap/ui/core/mvc/Controller",
	"sap/ui/model/json/JSONModel",
	"sap/m/MessageToast"
], function (Controller, JSONModel, MessageToast) {
	"use strict";
	return Controller.extend("sa.sap.sample.reuse.form.controller.SalesOrder", {
		onInit: function () {
			var oJsonModel = new JSONModel({
				data: {},
				displayMode: "D"
			});
			this.getView().setModel(oJsonModel, "FormDataModel");
			var oDataModel = this.getOwnerComponent().getModel();
			var sPath = "/SoheaderdataSet('500000000')";
			oDataModel.read(sPath, {
				success: this._onSalesOrderReadSuccess.bind(this),
				error: this._onSalesOrderReadFail.bind(this)
			});
		},
		onSalesOrderEdit: function () {
			this.getView().getModel("FormDataModel").setProperty("/displayMode", "E");
			this.getView().byId("idSalesOrderDetails").refresh();
		},
		onSalesOrderDisplay: function () {
			this.getView().getModel("FormDataModel").setProperty("/displayMode", "D");
			this.getView().byId("idSalesOrderDetails").refresh();
		},
		onSalesOrderSave: function(){
			this.byId("idSalesOrderDetails").validate();
		},
		_onSalesOrderReadSuccess: function (oResponse) {
			this.getView().getModel("FormDataModel").setProperty("/data", oResponse);
		},
		_onSalesOrderReadFail: function () {
			MessageToast.show("Error in Reading Sales Order Details");
		}
	});
});

Note:- In the above code snippet refresh method defined in the control needs to be called on change of the mode of the screen.

As you can see in the above code snippet, with such a minimal code we have rendered a full fledged form control which differentiate it with Smart Form Control.

Download source code of the example used in this blog  from this git repo

Note:- This control renders TypeAhead, SingleSelect ValueHelp, MultiSelect ValueHelp controls(Custom) which I will be showcasing in the next blog .

 

4 Comments
You must be Logged on to comment or reply to a post.