When I add a SimplePlanningCalendar to my app, I got the following Error.
I added the calendar with a minimal configuration to my app.
<IconTabBar xmlns="sap.m"
id="idCategoryMenu"
selectedKey="Home"
headerMode="Inline"
stretchContentHeight="true"
applyContentPadding="false"
select=".onSelectCategory"
items="{
path: 'backend>/CategorySet',
parameters: {
expand: 'Reports'
},
sorter: {
path: 'Sort'
},
templateShareable: true
}">
<items>
<IconTabFilter id="myIconTabFilter" key="{backend>Uuid}" text="{backend>Name}">
<!-- ... -->
<SinglePlanningCalendar>
<appointments>
<unified:CalendarAppointment xmlns:unified="sap.ui.unified"
title="{title}"
startDate="{startDate}"
endDate="{endDate}"
/>
</appointments>
</SinglePlanningCalendar>
<!-- ... -->
</IconTabFilter>
</items>
</IconTabBar>
When I debug the app, I come to the following line inside the SinglePlanningCalendar.js where a key from the given vView parameter is requested, but the parameter only holds a string.
Anyone else had this problem before and knows why or how to solve this?
The problem is caused by the control implementation itself in the current version (1.71.21) I use for my development.
The fix in the commit 45696fe is available as of UI5 1.75.x.
Since I cannot change my version, I implemented the given solution in my package:
{ // Controller
onInit: function () {
// ...
this.calendarTemporaryFix(); // Not needed since UI5 1.75
},
calendarTemporaryFix: function () {
// SinglePlanningCalendar required from "sap/m/SinglePlanningCalendar"
const fnSetSelectedView = SinglePlanningCalendar.prototype.setSelectedView;
SinglePlanningCalendar.prototype.setSelectedView = function (vView) {
if (typeof vView === "string") {
vView = sap.ui.getCore().byId(vView);
}
return fnSetSelectedView.call(this, vView);
};
},
// ...
}
Related
Unfortunately, my fragment page won't open.
Trying to open a select dialogue (fragment with table) after a button press.
Here is the error:
formatter function sap.f.FlexibleColumnLayoutWithOneColumnStart.controller.Formatter not found!
Uncaught (in promise) Error: Cannot instantiate object: "new" is missing!
at constructor (Object-dbg.js:39:11)
at constructor (EventProvider-dbg.js:29:14)
at constructor (ManagedObject-dbg.js:464:17)
at constructor (Fragment-dbg.js:122:17)
at f._configDialog (DetailDetail.controller.js?eval:120:4)
at f.eval (DetailDetail.controller.js?eval:84:10)
I have a Formatter in my project folder. The property _pDialog seems to be empty, although my view clearly exists.
The error is in the method handleTableSelectDialogPress.
sap.ui.define([
"sap/ui/model/json/JSONModel",
"sap/ui/core/mvc/Controller",
"sap/ui/core/Fragment",
"sap/m/MessageToast",
"./Formatter",
"sap/ui/core/Fragment",
"sap/ui/model/Filter",
"sap/ui/model/FilterOperator",
"sap/ui/model/json/JSONModel",
"sap/ui/core/syncStyleClass",
], function(JSONModel, Controller, Fragment, MessageToast, FilterOperator, syncStyleClass) {
"use strict";
return Controller.extend("sap.f.FlexibleColumnLayoutWithOneColumnStart.controller.DetailDetail", {
// ...,
handleTableSelectDialogPress: function(oEvent) {
var oButton = oEvent.getSource(),
oView = this.getView();
if (!this._pDialog) {
this._pDialog = Fragment.load({
id: oView.getId(),
name: "sap.f.FlexibleColumnLayoutWithOneColumnStart.view.Dialog",
controller: this
}).then(function(oDialog) {
oView.addDependent(oDialog);
return oDialog;
});
}
this._pDialog.then(function(oDialog) {
this._configDialog(oButton, oDialog);
oDialog.open();
}.bind(this));
},
_configDialog: function(oButton, oDialog) {
// ...
syncStyleClass("sapUiSizeCompact", this.getView(), oDialog);
},
// ...
});
});
This is my view:
<mvc:View displayBlock="true" controllerName="sap.f.FlexibleColumnLayoutWithOneColumnStart.controller.DetailDetail" height="100%" xmlns:mvc="sap.ui.core.mvc" xmlns="sap.f" xmlns:m="sap.m">
<DynamicPage toggleHeaderOnTitleClick="false">
<!-- ... -->
<content>
<m:Button
class="sapUiSmallMarginBottom"
text="Show Table Select Dialog"
press=".handleTableSelectDialogPress"
ariaHasPopup="Dialog" />
</content>
</DynamicPage>
</mvc:View>
This is my Fragment:
<core:FragmentDefinition xmlns:core="sap.ui.core" xmlns="sap.m">
<TableSelectDialog id="myDialog"
title="Select Product"
search=".handleSearch"
confirm=".handleClose"
cancel=".handleClose"
items="{
path: '/ProductCollection',
sorter: {
path: 'Name',
descending: false
}
}">
<ColumnListItem vAlign="Middle">
<!-- ... -->
</ColumnListItem>
<columns>
<!-- ... -->
</columns>
</TableSelectDialog>
</core:FragmentDefinition>
This is my project folder:
There is a mismatch of the required dependencies in the array with the callback parameters of the controller factory function:
sap.ui.define([
"sap/ui/model/json/JSONModel",
"sap/ui/core/mvc/Controller",
"sap/ui/core/Fragment",
"sap/m/MessageToast",
"./Formatter", // <-- here you required Formatter
"sap/ui/core/Fragment", // <-- required Fragment (again)
"sap/ui/model/Filter",
"sap/ui/model/FilterOperator",
"sap/ui/model/json/JSONModel", // <-- required JSONModel (again)
"sap/ui/core/syncStyleClass",
], /*factory*/function(JSONModel, Controller, Fragment, MessageToast, FilterOperator/*<-- Formatter */, syncStyleClass/*<-- Fragment */) {/* ... */});
Consequently, the parameter syncStyleClass is not the module sap/ui/core/syncStyleClass but sap/ui/core/Fragment! And calling Fragment() without new results in the error "new" is missing!.
The name of the function parameters is not relevant to the framework. Important is the correct order of those parameters according to the list of dependencies. From the API reference of sap.ui.define:
The module export of each dependency module will be provided as a parameter to a factory function, the order of the parameters will match the order of the modules in the dependencies array.
The same rule applies to sap.ui.require.
I have a problem to display data in my detail page. I've tried almost everything but its dosnt work. On main page everything looks fine. Routing work (display proper ID on network address).
Details.controller.js :
return Controller.extend("sapProject.controller.Details", {
onInit: function () {
var oTable = this.getView().byId("details");
var oModel = new sap.ui.model.json.JSONModel();
oModel.loadData("model/Object.json");
oTable.setModel(oModel);
var oRouter = sap.ui.core.UIComponent.getRouterFor(this);
oRouter.getRoute("Details").attachMatched(this._onRouteMatched, this);
},
_onRouteMatched : function (oEvent) {
var oArgs, oView;
oArgs = oEvent.getParameter("arguments");
oView = this.getView();
oView.bindElement({
path : "/Objects(" + oArgs.employeeId + ")",
events : {
dataRequested: function () {
oView.setBusy(true);
},
dataReceived: function () {
oView.setBusy(false);
}
}
});
},
and this is my Details.view.xml:
<Page
id="details"
title="{i18n>EmployeeDetailsOf} {FirstName} {LastName}"
showNavButton="true"
navButtonPress="onBack"
class="sapUiResponsiveContentPadding">
<content>
<Panel
width="auto"
class="sapUiResponsiveMargin sapUiNoContentPadding">
<headerToolbar >
<Toolbar>
<Title text="{i18n>EmployeeIDColon} {EmployeeID}" level="H2"/>
<ToolbarSpacer />
</Toolbar>
</headerToolbar>
<content>
<f:SimpleForm>
<f:content>
<Label text="{i18n>FirstName}" />
<Text text="{FirstName}" />
<Label text="{i18n>LastName}" />
</f:content>
</f:SimpleForm>
</content>
</Panel>
</content>
</Page>
I think you are binding an empty model to your detail view because probably the loadData function is not completed when you set the model on the Table.
Try to load your json file in the manifest (best option) or differ the setModel on the _onRouteMatched function (although I don't see any table in your detail view).
EDIT:
You can also use this code after oModel.loadData("model/Object.json");
oModel.attachEventOnce("requestCompleted", function(oEvent) {
// Here your file is fully loaded
});
Firstly I recommend you to bind like this:
var sObjectPath = this.getModel().createKey("Objects", {
ID: oArgs.employeeId
});
this._bindView("/" + sObjectPath);
...
}
_bindView: function (sObjectPath) {
//Todo: Set busy indicator during view binding
this.getView().bindElement({
path: sObjectPath,
parameters: {
},
events: {
change: this._onBindingChange.bind(this),
dataRequested: function () {
}.bind(this),
dataReceived: function () {
}.bind(this)
}
});
},
Secondly check if oArgs.employeeId has a valid value and also if the model is loaded with data, easily set a brekapoint or write console.log(this.getView().getModel().oData).
I have an "Add New…" screen with multiple sap.m.Input fields. Everything is working. I submit the form and the values are stored in the DB. But once I re-open this "Add New…" screen, I get the form with previously entered values.
Currently, I can solve the issue iterating over all sap.m.Input fields with sap.ui.core.Element, resetting the values:
Element.registry.forEach(el => {
if (el.isA("sap.m.Input") && el.sId.includes(inputFieldsMask)) {
sap.ui.getCore().byId(el.sId).setValue("");
}
});
Where inputFieldsMask is a mask for all input fields of the relevant screen.
As far as I understand, Element.registry.forEach iterates over all controls in the app, therefore I'm not sure that, from a performance point of view, it's an optimal approach to clean up the fields.
Is there a better way to reset input fields from the previously entered values?
There are several ways to reset the control values depending on what kind of approach you took to create the new entry. Generally, we can make use of the following APIs:
Pass the context to the target container.
In case of working with client-side models: targetContainer.bindElement("newItemPath").
Otherwise, call myV2ODataModel.createEntry("/ThatSet", {...}), which returns a new context, and then pass it to targetContainer.setBindingContext(context, "modelName").
This resolves all the relative bindings in the target container.
<user enters some values and submits ...>
targetContainer.unbindElement("modelName") after the edit was successfully stored.
By unbinding element, relatively bound control values are reset automatically.
Example (using client-side model):
sap.ui.getCore().attachInit(() => sap.ui.require([
"sap/ui/core/mvc/XMLView",
"sap/ui/model/json/JSONModel",
"sap/base/util/uid",
], (XMLView, JSONModel, createPseudoUniqueID) => XMLView.create({
definition: `<mvc:View xmlns:mvc="sap.ui.core.mvc" height="100%">
<App xmlns="sap.m">
<Page backgroundDesign="List" title="Resetting inputs via client-side Model and Context">
<headerContent>
<Button id="addBtn" text="Add Item" type="Emphasized" />
</headerContent>
<List id="myList" growing="true" items="{
path: '/myItems',
key: 'key',
templateShareable: false
}">
<StandardListItem title="{value}" info="Key: {key}"/>
</List>
</Page>
<dependents>
<Dialog id="myDialog"
icon="sap-icon://ui-notifications"
title="New Item"
draggable="true"
class="sapUiResponsiveContentPadding"
>
<Input id="myInput"
placeholder="<New value>"
valueLiveUpdate="true"
value="{
path: 'value',
type: 'sap.ui.model.type.String',
constraints: {
minLength: 1
}
}"
/>
<beginButton>
<Button
text="Submit"
enabled="{= !!%{value} && !%{messages>/}.length}"
/>
</beginButton>
</Dialog>
</dependents>
</App>
</mvc:View>`,
models: {
undefined: new JSONModel({
"myItems": [],
}),
"messages": sap.ui.getCore().getMessageManager().getMessageModel()
},
afterInit: function() {
sap.ui.getCore().getMessageManager().registerObject(this, true);
this.byId("addBtn").attachPress(handleAddPress.bind(this));
this.byId("myInput").attachSubmit(handleSubmit.bind(this));
this.byId("myDialog").setEscapeHandler(onESCPress.bind(this))
.attachAfterClose(onAfterClose.bind(this))
.getBeginButton().attachPress(handleSubmit.bind(this));
function handleAddPress(event) {
const dialog = this.byId("myDialog");
const listBinding = this.byId("myList").getBinding("items");
listBinding.suspend(); // Do not update the list yet
this._currentItems = this.getModel().getProperty("/myItems"); // temp in case user cancels
dialog.getModel().setProperty("/myItems", this._currentItems.concat({})); // new empty item
dialog.bindElement("/myItems/" + listBinding.getLength()); // enable data synchronization via TwoWay binding
dialog.open();
}
function onESCPress(promise) {
const model = this.getModel();
model.setProperty("/myItems", this._currentItems, /*context*/null, /*async*/true);
return promise.resolve(); // continue closing dialog
}
function onAfterClose(event) {
handleAfterClose(event.getSource(), this.byId("myList").getBinding("items"));
}
function handleAfterClose(dialog, listBinding) {
dialog.unbindElement(); // reset data
dialog.setBusy(false);
listBinding.resume();
}
function handleSubmit() {
const dialog = this.byId("myDialog");
if (!dialog.getBeginButton().getEnabled()) return; // something is wrong
dialog.setBusy(true);
if (!this._isStillRequesting) {
this._isStillRequesting = true;
/* send request */setTimeout(mySuccessHandler.bind(this), 3000)
};
}
function mySuccessHandler(newKeyFromServer = createPseudoUniqueID()) {
const dialog = this.byId("myDialog");
this._isStillRequesting = false;
if (!dialog.isOpen()/* request was aborted e.g. by pressing ESC */) {
return; // exit early
}
const context = dialog.getBindingContext();
const value = context.getProperty("value");
dialog.getModel().setProperty(context.getPath("key"), newKeyFromServer);
dialog.close();
sap.ui.require([
"sap/m/MessageToast"
], MT => window.requestAnimationFrame(() => MT.show(`${value} created`)));
}
},
}).then(view => view.placeAt("content"))));
<script id="sap-ui-bootstrap" src="https://ui5.sap.com/resources/sap-ui-core.js"
data-sap-ui-libs="sap.ui.core,sap.m"
data-sap-ui-theme="sap_fiori_3"
data-sap-ui-async="true"
data-sap-ui-compatversion="edge"
data-sap-ui-excludejquerycompat="true"
data-sap-ui-xx-waitForTheme="init"
></script>
<body id="content" class="sapUiBody sapUiSizeCompact"></body>
As explained above, binding and unbinding element also applies to server-side models such as v2.ODataModel.
Benefits
✅ Reduced overload: no need to iterate over all existing controls. Reset only those automatically that need to be reset.
✅ Control agnostic: does not rely on control specific APIs such as myInput.setValue, mySwitch.setState, etc..
✅ Reduced maintenance costs: no need to maintain list of model properties in controller that application needs to reset manually.
Best practice is to use a model to store your application data and to bind any input field to that model. I added an example here. For the sake of simplicity the model data is cleared when the button is pressed.
In a real world application you would place any setup of the model to the onRouteMatched handler to ensure that the data is in an initial state.
onRouteMatched : function(event) {
this.getView().getModel().setData({
"firstName": "",
"lastName": ""
});
}
Bind all your control values to a model. Then reset this model after you've successfully saved the data.
Example:
control1.bindProperty("value", "/controlValues/control1Value"); // Binding
// control1.bindProperty("value", "/controlValues/name");
// <Input value="{/controlValues/name}" /> // <-- ideal binding in xml view
this.getView().getModel().setProperty("/controlValues", this.resetFormData()); // Clear Model
resetFormData: function () {
var emptyControlValues = {
"control1Value": "", // "name": "", <-- bind to control
"control2Value": 0, // "age": 0,
"control3Value": "", // "address": "",
"control4Value": "" // "tel": ""
};
return emptyControlValues;
};
I try to use the template:if for my XML View.
As an example i have this:
<mvc:View controllerName="Test_Start.controller.View"
xmlns:html="http://www.w3.org/1999/xhtml"
xmlns:mvc="sap.ui.core.mvc"
xmlns:temp="http://schemas.sap.com/sapui5/extension/sap.ui.core.template/1"
displayBlock="true" xmlns="sap.m">
<App>
<pages>
<Page title="{i18n>title}">
<content>
<temp:if test="{= ${Data>Enable1} === 'X'}">
<Text text="Hallo"/>
</temp:if>
</content>
</Page>
</pages>
</App>
</mvc:View>
and my Component.js looks like this:
sap.ui.define([
"sap/ui/core/UIComponent",
"sap/ui/Device",
"Test_Start/model/models",
"sap/ui/model/odata/v2/ODataModel",
"sap/ui/core/util/XMLPreprocessor"
], function(UIComponent, Device, models, ODataModel, XMLPreprocessor) {
"use strict";
return UIComponent.extend("Test_Start.Component", {
metadata: {
manifest: "json"
},
/**
* The component is initialized by UI5 automatically during the startup of the app and calls the init method once.
* #public
* #override
*/
init: function() {
// call the base component's init function
UIComponent.prototype.init.apply(this, arguments);
// set the device model
this.setModel(models.createDeviceModel(), "device");
},
onBeforeRendering: function(){
var oModel = new ODataModel("/sap/opu/odata/SAP/ZPFO_CKPT_ODATA_DYN_SRV/"),
oMetaModel = oModel.getMetaModel(),
sPath = "/DataSet";
oMetaModel.loaded().then(function() {
var oTemplateView = sap.ui.view({
preprocessors: {
xml: {
bindingContexts : {
meta : oMetaModel.getMetaContext(sPath)
},
models: {
meta: oMetaModel
}
}
},
type : sap.ui.core.mvc.ViewType.XML,
viewName: "Test_Start.view.View"
});
oTemplateView.setModel(oModel);
oTemplateView.bindElement(sPath);
});
}
});
});
Now, when I try to run my App, i get the following error:
XMLTemplateProcessor-dbg.js:53 Uncaught Error: failed to load
'http://schemas/sap/com/sapui5/extension/sap/ui/core/template/1/if.js'
from
../../resources/http://schemas/sap/com/sapui5/extension/sap/ui/core/template/1/if.js:
404 - Not Found
I did some research and found out, that i probably load the preprocessor at the wrong time, but I seem to can't find the right place to load it.
I use this SAPUI5 SDK example for my work.
Edit
I found a solution for my problem:
instead of an "onBeforeRendering" function, now i'm using a "createContent" function. Also i deleted the "init" completly.
In adition to that, I implemented an oViewContainer, like it is used in the sample.
Also controller name "Test_Start.Component" doesn't match the controllerName attribute in the view "Test_Start.controller.View".
I'm trying to extend a MessageStrip control.
For starters, I've created a new file to extend it with no new params at all
MessageStrip.js
sap.ui.define(["sap/m/MessageStrip"],
function (MessageStrip) {
"use strict";
return MessageStrip.extend("com.sap.it.cs.itsdpphome.controller.fragments.MessageStrip", {
metadata: {
properties: {
},
aggregations: {
},
events: {
}
},
init: function () {
},
renderer:{}
});
});
I've created also an xml file that retrieves a data from a model:
<List items="{msgData>/msgData}" >
<CustomListItem>
<wt:MessageStrip
text="{msgData>Text}"
type="{msgData>Type}"
showIcon="true"
showCloseButton="false"
customIcon="{msgData>customIcon}"
class="sapUiMediumMarginBottom">
<Link text="{msgData>linkText}"
target="{msgData>linkTarget}"
href="{msgData>linkUrl}" />
</wt:MessageStrip>
</CustomListItem>
</List>
I can get all model params in xml, except from "text" param!
If I remove text="{msgData>Text}", then it displays all the correct params except from the text.
But when I add the text="{msgData>Text}", then the console displays an
error:
Uncaught TypeError: Cannot read property 'setText' of null
Why is that happening?
You have to call init of your base: sap.m.MessageStrip in your own init .
init: function () {
MessageStrip.prototype.init.call(this);
},
It should solve your problem.