问题
I'm using OpenUI5. Using the formatter.js
, I have formatted some text in my view.
But my formatter is called 3 times:
When I bind the model to panel control:
oPanel.setModel(oModel, "data");
bothsBirthday
andsFormat
areundefined
.After
onInit()
is finished and the view is rendered:sBirthday
is valorized correctly andsFormat
isundefined
Again: both
sBirthday
andsFormat
ara valorized correctly.
Why does this happen? Is it correct?
The app get an error, because the ageDescription()
in the formatter, can't manage undefined
values.
formatter.js
sap.ui.define([], function () {
"use strict";
return {
ageDescription : function (sBirthday, sFormat) {
do.something();
var sFromMyBd = moment(sBirthday, sFormat).fromNow();
do.something();
return sAge;
}
}
});
main.view.xml
<mvc:View
controllerName="controller.main"
xmlns="sap.m"
xmlns:mvc="sap.ui.core.mvc">
<Panel id="user-panel-id">
<Input id="name-input-id" enabled="false" value="{data>/user/name}" />
<Label text="{i18n>age}: " class="sapUiSmallMargin"/>
<Label text="{
parts: [
{path: 'data>/user/birthday'},
{path: 'data>/user/dateFormat'}
],
formatter: '.formatter.ageDescription' }"/>
</Panel>
</mvc:View>
Main.controller.js
sap.ui.define([
"sap/ui/core/mvc/Controller",
"sap/ui/model/json/JSONModel",
"model/formatter"
], function (Controller, JSONModel, formatter) {
"use strict";
return Controller.extend("controller.main", {
formatter: formatter,
onInit: function () {
var oModel = new JSONModel();
var oView = this.getView();
oModel.loadData("model/data.json");
var oPanel = oView.byId("user-panel-id");
oPanel.setModel(oModel,"data");
do.something();
},
});
});
data.json
{
"user": {
"name": "Frank",
"surname": "Jhonson",
"birthday": "23/03/1988",
"dateFormat": "DD/MM/YYYY",
"enabled": true,
"address": {
"street": "Minnesota street",
"city": "San Francisco",
"zip": "94112",
"country": "California"
}
}
}
回答1:
Set the model to the view only when the data request is completed:
onInit: function() { const dataUri = sap.ui.require.toUri("<myNamespace>/model/data.json"); const model = new JSONModel(dataUri); model.attachEventOnce("requestCompleted", function() { this.getView().setModel(model); }, this); // ... },
This ensures that the formatter is called only once (invoked by
checkUpdate(true)
which happens on binding initialization; see below), and no further changes are detected afterwards.Additionally (or alternatively), make the formatter more defensive. Something like:
function(value1, value2) { let result = ""; if (value1 && value2) { // format accordingly ... } return result; }
Why does this happen?
- View gets instantiated.
onInit
of the Controller gets invoked. Here, the filemodel/data.json
is requested (model is empty).- Upon adding the view to the UI, UI5 propagates existing parent models to the view.
- Bindings within the view are initialized, triggering
checkUpdate(/*forceUpdate*/true)
src in each one of them. - Due to the
forceUpdate
flag activated,change
event is fired, which forcefully triggers the formatters even if there were no changes at all:[undefined, undefined]
→[undefined, undefined]
. - 1st formatter call - Fetching
model/data.json
is now completed. Now the model needs tocheckUpdate
again. [undefined, undefined]
→[value1, undefined]
→ change detected → 2nd formatter call[value1, undefined]
→[value1, value2]
→ change detected → 3rd formatter call
回答2:
Now, given that you are trying to load a static JSON file into your project, it's better to maximize the usage of the manifest.json
.
That way, you are sure that the data is already loaded and available in the model prior to any binding.
You achieve this by adding the JSON file as a data source under sap.app
manifest.json
"sap.app": {
"id": "com.sample.app",
"type": "application",
"dataSources": {
"data": {
"type": "JSON",
"uri": "model/data.json"
}
}
}
Now, simply add this dataSource
called data
as one of the models
under sap.ui5.
"sap.ui5": {
"rootView": {
"viewName": "com.sample.app.view.App",
"type": "XML"
},
"models": {
"i18n": {
"type": "sap.ui.model.resource.ResourceModel",
"settings": {
"bundleName": "com.app.sample.i18n.i18n"
}
},
"data": {
"type": "sap.ui.model.json.JSONModel",
"dataSource": "data"
}
}
}
With this, you DON'T need to call this anymore:
var oModel = new JSONModel();
var oView = this.getView();
oModel.loadData("model/data.json");
var oPanel = oView.byId("user-panel-id");
oPanel.setModel(oModel,"data");
..as the data model
we added in the manifest.json
, is already visible to oView
and oPanel
right from the get go.
This way, it doesn't matter if the formatter gets called multiple times, as it would already have the data available to it right from the beginning.
来源:https://stackoverflow.com/questions/58798785/ui5-formatter-called-multiple-times-or-too-soon-from-an-xml-view