Skip to Content
Technical Articles
Author's profile photo Arjun Biswas

Virtual Context in SAP UI5

Hello Readers,

In this post, I am going to discuss about a scenario (and solution) that some of us might have faced while using smart forms in ui5 with CDS generated odata services.

Scenario:

Suppose we have a table in se11. The data from the table is brought to UI5 using CDS generated OData services. Now, in UI5 we need an smart form for creating an record for the same backend table, but the fields in the form should be smart.

The trick here is that, smart form in sap ui5 basically uses element binding, but when the data comes from back end it is the data of the whole table (for aggeration binding) as we have redefined get entityset to fetch the data. We cannot redefine get entity as it would throw a dump in case there are no records in the table (it is also not the standard way to bind an smart form).

The solution here is that once the odata is instantiated in a model variable in our controller, we can create an virtual entry in the model (an empty record with all the necessary properties), this gives us an context (Virtual Context). This context of the virtual entry/record (element), we can use to bind the smart form. Therefore, solving our problem of element binding.

We can keep updating the instance of the virtual entry as and when the user enters data in the form and once the user clicks on submit, we can send an update call to the backend with the id (primary key) of the virtual entry, thus making it the saved record by the user.

Lets look at an example below for the above scenario:

Consider the below user details table: (This is an example table with just 5 columns by me. This method works with any sap standard or custom table)

Now, the requirement in the UI5 side is to have an user creation form, which is a smart form, to be binded with the fields of this table. The smart form should look like below:

The advantages of using smart form here are that, if we use Hana CDS and Bopf from backend, then the validations, value helps and the fields can be properly managed from the backend itself, instead of manually adding the fields from the xml view in UI5 side.

Now, for the above table, when we generate the odata by redefining the get entity set method, the whole table data is sent to the UI5 side. Using this we would not be able to bind the smart form right away, because as mentioned earlier, using the standard methods, we need only one record from the table to get the structure of the fields required in the smart form and use this as element binding to bind to the smart form. So, as we cannot point to any record by hard coding we can use this concept of Virtual Context.

Code for smart form:

<smartForm:SmartForm id="idUserSmartForm">
<smartForm:Group>
				<smartForm:GroupElement>
					<smartField:SmartField value="{UserName}" />
				</smartForm:GroupElement>
				<smartForm:GroupElement>
					<smartField:SmartField value="{Department}" />
				</smartForm:GroupElement>
				<smartForm:GroupElement>
					<smartField:SmartField value="{Age}" />
				</smartForm:GroupElement>
</smartForm:Group>
<smartForm:Group>
<smartForm:GroupElement>
					<smartField:SmartField value="{EmailID}" />
				</smartForm:GroupElement>
</smartForm:Group>
</smartForm:SmartForm>

Code in Controller:

//In the controller's init function
onInit: function(){
         var oModel = this.getOwnerComponent().getModel();
	 //For creation of an virtual entry
         //create an entry of the user table collection with the specified properties and values
         var oPropertiesEntityObject = {
				UserName: "",
                                Age: "",
                                Department: "",
                                EmailID: ""
			};
         //Creation of an virtual element context
         var oContext = oModel.createEntry("/ZUser_Entity", {
				"groupId": "idUserGroup",
				properties: oPropertiesEntityObject ,
				success: function (oData) {
                                        //On success of creation of an context, return an unique user id 
                                        //generated from the backend. Store this id globally within the 
                                        //controller, to use it later on for updation/deletion.
					this.userID = oData.userID;
				}.bind(this)
			});
         //Final step, bind the context against the smart form (this can be done for views, dialogs  
         // as well).
         var oSmartForm = this.getView().byId("idUserForm");
         oSmartForm.setBindingContext(oContext);
}

 

Now, once the user finishes editing the form and clicks save, we can use the globally stored id to save the form. In case the user clicks on discard, we can delete the virtually created entry.

Note: In case, while creating an virtual context, you dont want any changes in the back end i.e. any record to be created or changed in the back end, you can set the default binding context of the model as one way explicitly before creating an virtual entry.

For more information and examples on this method, kindly refer to the links below:

https://github.com/SAP/openui5/issues/1447

https://answers.sap.com/questions/12349405/twoway-binding-with-createentry-sapui5.html

Please feel free to ask any doubts/question in comments.

 

Regards,

Arjun Biswas

 

 

Assigned Tags

      7 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Denis Galand
      Denis Galand

      Excellent post, I was wondering recently how to handle create operations with smartforms, now i know, thank you Arjun ?

      Author's profile photo Arjun Biswas
      Arjun Biswas
      Blog Post Author

      Welcome Denis !

      Author's profile photo Mahesh Palavalli
      Mahesh Palavalli

      Nice example!!

      One suggestion, you don't need to pass empty values to the createEntry,

      var oPropertiesEntityObject = {
      				UserName: "",
                                      Age: "",
                                      Department: "",
                                      EmailID: ""
      			};

      it should work well without passing any empty values. You could use that to pass default values though.

      var oPropertiesEntityObject = { };

      Thanks,

      Mahesh

      Author's profile photo Dinesh Naidu
      Dinesh Naidu

      Hi Arjun,

      Can you explain that line where  success:  function() {

      }.bind(this)

       

       

      Author's profile photo Arjun Biswas
      Arjun Biswas
      Blog Post Author

      Hi Dinesh,

      Thank You for your comment.

      The below statement, is used for the success call back, which gets triggered once the response from the backend comes after the operation has been performed successfully.

      success:  function() {

      }.bind(this)

       

      bind(this), is an method using which, we can get the controller scope "this" inside our callback function.

      Author's profile photo Dinesh Naidu
      Dinesh Naidu

      Okay, thank you arjun ?

      Author's profile photo Dmitry Krokhun
      Dmitry Krokhun

      Hi,

      Thank you, your idea has worked for me.

      Maybe this will help someone, I had to set oModel.setDeferredGroups(["idUserGroup"]), otherwise changes immediately were sending to backend.