Skip to Content
Technical Articles

Advanced UI5 view techniques (view-model, formatter, expression binding, multiple inputs for formatter)

Why and what?

This blog should explain some parts which are quite powerful, but need to be understood and applied together to unleash their full power.

The concepts are quite simple, typically the execution is the problematic part, as there are some pitfalls, especially regarding what special char to place where, to let the magic begin.

I’ve struggled with kind of each and every part explained below – I feel your pain if something doesn’t work at first save, and will happily answer your questions (if I get the feeling that you’ve at least tried and read the referenced resources).

But, let’s first set the stage.

In my setting, I’m consuming an OData V2 model from an SAP NetWeaver Gateway. Some of the endpoints are implemented the hard way (100% ABAP), some are hydrated through a table-mapping. If you’re using CDS views, the concepts should work for you as well. Long live the layer model 😉

Formatter

Further I’ve created a formatter.js file in my Fiori app’s model folder.

sap.ui.define([],
function() {
	"use strict";
	
	return { 		
		sapboolean: function (x) {
			return (x === "X");
		},
		commaToPoint: function (x) {
			try {
				return x.replace(",", ".");	
			} catch (e) {
				return x;	
			}
			
		},
		containsSomething: function (x, isRequired, state) {
						if (!isRequired) {
				return "None";
			} else if ( x === null || x === undefined) {
				return "Warning";
			} else if (state && state.length > 0) { 
			//required for manual override for changes, which don't trigger the formatter (loading of suggestions, without result)
				return state;
			} else if (x.length > 0 ) {
				return "None";
			} else {
				return "Warning";
			}
		},
	};
});

The first two should demonstrate the basic concept, the last one how to get multiple parameters into an formatter.

In each controller, which’s view might require access to one of the formatter functions, add:

sap.ui.define([ ...
	"imag/yourNameSpace/model/formatter",
	...
], function ( ...
	formatter,
	...) {
	"use strict";

	return BaseController.extend("imag.yourNameSpace.controller.Backend", {
		formatter: formatter,
		

View-State management

For keeping the state of the view out of the controller, and being able to bind parts of the view, I’ve created a view model (JSON), which contains every part I’d like to modify during run-time.

model/editView.json

{
	"creating" : false,
	
	"lane": {
		"enabled" : false,
		"required" : false
	},
	"station": {
		"enabled" : false,
		"required" : false
	},
	"baugr": {
		"enabled": false,
		"required": false,
		"state":	"None"
	}
}

Formatter

They are like converters for your view elements. Typically you apply them to your bound model to get a different representation for your view.

If you’d like to bind some values from your meta-data, you might find that some of them are interpreted as text, and can’t be applied because of that.

Problem: How do I get a value into another representation?

Push it into a function, either a given JavaScript one, or your own kind of work.

Simple example for using your own implementation:

<CheckBox 
id="chkbx_inactive" 
select="_onDeactivateCheckboxChange" 
selected="{path: 'Inactive', formatter: '.formatter.sapboolean' }"
/>

JavaScript function:

<Input maxLength="{path: 'Amount', formatter: 'parseInt'}" />

Multiple Inputs for the formatter:

<Input 
valueState="
{parts: ['Baugr','editView>/baugr/required','editView>/baugr/state'], 
formatter: '.formatter.containsSomething' }
">

The first part transports the content of the currently displayed entity, the other two if it’s required and (in-)valid due to another reason. Depending on the three parts we get some nice color around our input box.

Quite some work.

Model-Bindings

<ComboBox required="{editView>/station/required}">

To manipulate the state with the controller:

var oEditViewModel = this.getModel("editView");
oEditViewModel.setProperty("/creating", true);

Expression Binding

With multiple conditions; they have to be evaluated before giving them to the view. That’s arranged by the {= and ${ for each element in the evaluation.

If you rely just on simple conditions or combinations of them, use expression bindings.

<ComboBox 
enabled="{= ${editView>/station/enabled} &amp;&amp; ${editView>/creating}}" 
>

Taken from SAP’s manual:

<Icon 
src="sap-icon://message-warning" 
visible="{= ${status} === 'critical' }"
>

 

Metadata-Binding

Going with the default model:

<Input 
maxLength="{path: '/#Pruefpunkt/Kostentrg/@maxLength', formatter: 'parseInt'}" 
>

Different model:

<Input 
maxLength="{path: 'differentModel>/#Pruefpunkt/Kostentrg/@maxLength', formatter: 'parseInt'}" 
>

The identificator for the datapoint is always beginning with an lower-case character (maxLength in the given example), even if it’s declare like this in the metadata-model:

<Property Name="Kostentrg" ... MaxLength="4" ... Type="Edm.String"/>

 

Final Example: Input that contains everything (and more)

<Input type="Text" 
	value="{path: 'Kostentrg', mode: 'OneWay'}" 
	enabled="{= ${editView>/ktr/enabled} &amp;&amp; ${editView>/creating}}" 
	maxLength="{path: '/#Pruefpunkt/Kostentrg/@maxLength', formatter: 'parseInt'}" 
	id="inp_con_ktrg" 
	required="{editView>/ktr/required}" 
	showSuggestion="true" 
	suggestionItems="{/KostentraegerSet}" 
	suggestionItemSelected="suggestionKtrgSelected" 
	suggest="onSuggestKtrg" 
	startSuggestion="1" 
	filterSuggests="false" 
	valueState="{parts: ['Kostentrg','editView>/ktr/required','editView>/ktr/state'], formatter: '.formatter.containsSomething' }" 
	busy="{editView>/ktr/busy}" 
	busyIndicatorDelay="0" 
	valueStateText="No valid cost object chosen!">

Common Pitfalls:

  • Bindings cannot be edited through the layout-editors element-property input, you do have to open the binding menu and enter / paste the content.

Just use the Button on the right side, and your binding will be accepted …

 

Conclusion

Cooking with the ingredients I’ve shed some light onto, you’ll be able to do more with less code. Don’t believe me?

I’ll just assume that you’re using at least data-binding for your inputs, otherwise you’ll do way more coding than it’s good for you.

How would you handle an element (input) which should be hidden when a checkbox isn’t set (without knowing stuff presented in this post)?

  • Adding an onClick handler at the checkbox, writing the implementation of that handler, where you’ll have to fetch the input by its id, and checking that the id is set to something meaningful.
    Every time you change one of the three parts you’d have to keep in mind that there are side-effects, if you’re not careful.
  • With the knowledge gained through this post:
    just bind the visible state of your input to the checkbox’s value.

Utilizing the power of the bindings, expressions, and formatters helps you to keep your controller sleek, makes you more productive and generate less error-prone applications.

 

 

 

 

 

References:

Metadata-Binding

Expression-Binding

Formatter

 

Kudos:

Andreas Legath and Florian M. for bringing my attention once again back to the Metadata-Binding and making me explain myself – that led to some nice improvements and insights.

 

1 Comment
You must be Logged on to comment or reply to a post.