Reputation: 14586
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?
Upvotes: 2
Views: 4036
Reputation: 18044
There are several ways to reset the control values depending on what kind of approach you took to create the new entry. Given, for example, relative bindings in the target container definition (e.g. a fragment containing sap.m.Dialog
):
Context
instance via targetContainer.setBindingContext
or, if possible, let the framework create and set one via targetContainer.bindElement
/.bindObject
. This resolves all the relative bindings that the context can resolve (if the respective paths are valid), and allows directly manipulating the bound data from the UI via two-way binding.targetContainer.unbindElement
/.unbindObject
after the edit was successfully submitted.By unbinding element, the framework cleans up the internal element binding information of the target container and resets all its relatively bound control values of the given model.
Keep in mind that the API unbindElement
/unbindObject
awaits a model name that may be required.
globalThis.onUI5Init = () => sap.ui.require([
"sap/ui/core/mvc/Controller",
"sap/ui/core/mvc/XMLView",
"sap/ui/model/json/JSONModel", // Sample model for the sake of this demo
"sap/ui/core/Core",
], async (Controller, XMLView, JSONModel, Core) => {
"use strict";
const MyController = Controller.extend("demo.MyController", {
handleAddPress: function() {
const dialog = this.byId("myDialog");
const clientListBinding = this.byId("myList").getBinding("items");
// In case of OData and if no context exists yet, consider creating a new context via odataListBinding.create, v4.ODataModel#getKeepAliveContext, etc.
clientListBinding.suspend(); // to update the list once the dialog closes
this._currentItems = this.getView().getModel().getProperty("/myItems"); // for the cancel case (onESCPress)
dialog.getModel().setProperty("/myItems", this._currentItems.concat({})); // new empty item. Applies only to this client-side model
dialog.bindElement(`/myItems/${/*index:*/clientListBinding.getLength()}`); // or setBindingContext
dialog.open();
},
onESCPress: function(promise) {
if (this._isStillRequesting) {
return promise.reject();
}
const model = this.getView().getModel();
model.setProperty("/myItems", this._currentItems);
return promise.resolve(); // continue closing the dialog
},
onAfterClose: function(event) {
this.handleAfterClose(event.getSource(), this.byId("myList").getBinding("items"));
},
handleAfterClose: function(dialog, listBinding) {
dialog.unbindElement(/*modelName*/); // <-- resets the data in dialog and cleans up its element binding infos internally. No dialog.destory needed.
dialog.setBusy(false);
listBinding.resume();
},
handleSubmit: function() {
const dialog = this.byId("myDialog");
if (!dialog.getBeginButton().getEnabled()) return;
dialog.setBusy(true);
if (!this._isStillRequesting) {
this._isStillRequesting = true;
/*Faking request:*/setTimeout(this.mySuccessHandler.bind(this), 3000)
};
},
mySuccessHandler: function (newKeyFromServer = globalThis.crypto.randomUUID()) {
const dialog = this.byId("myDialog");
const context = dialog.getBindingContext();
const value = context.getProperty("value");
this._isStillRequesting = false;
dialog.getModel().setProperty(context.getPath("key"), newKeyFromServer);
dialog.close();
sap.ui.require([ "sap/m/MessageToast" ], MT => MT.show(`${value} created`));
},
});
const control = await XMLView.create({
definition: document.getElementById("myxmlview").textContent,
height: "100%",
controller: new MyController(),
models: {
undefined: new JSONModel({
"myItems": [],
}),
"messages": Core.getMessageManager().getMessageModel(),
},
});
Core.getMessageManager().registerObject(control.placeAt("content"), true);
});
<script id="sap-ui-bootstrap" src="https://sdk.openui5.org/nightly/resources/sap-ui-core.js"
data-sap-ui-oninit="onUI5Init"
data-sap-ui-libs="sap.ui.core,sap.m,sap.ui.layout,sap.ui.unified"
data-sap-ui-theme="sap_horizon"
data-sap-ui-async="true"
data-sap-ui-compatversion="edge"
data-sap-ui-excludejquerycompat="true"
data-sap-ui-resourceroots='{ "demo": "./" }'
data-sap-ui-xx-waitForTheme="init"
></script>
<script id="myxmlview" type="text/xml">
<mvc:View xmlns:mvc="sap.ui.core.mvc" height="100%" controllerName="demo.MyController">
<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" press=".handleAddPress" />
</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"
initialFocus="myInput"
class="sapUiResponsiveContentPadding"
escapeHandler=".onESCPress"
afterClose=".onAfterClose"
>
<Input id="myInput"
placeholder="<New value>"
valueLiveUpdate="true"
value="{
path: 'value',
type: 'sap.ui.model.type.String',
constraints: {
minLength: 1
}
}"
submit=".handleSubmit"
/>
<beginButton>
<Button
text="Submit"
type="Emphasized"
enabled="{= !!%{value} && !%{messages>/}.length}"
press=".handleSubmit"
/>
</beginButton>
</Dialog>
</dependents>
</App>
</mvc:View>
</script>
<body id="content" class="sapUiBody sapUiSizeCompact"></body>
Of course, binding and unbinding element also applies to server-side models such as the v2.ODataModel
and v4.ODataModel
. But how an instance of v2.Context
/v4.Context
can be created or accessed can differ depending on the use case. Refer to the documentation topics and API reference of the respective model, context, ODataListBinding
, and ODataContextBinding
.
unbindElement
/unbindObject
with relative bindingsmyInput.setValue
, mySwitch.setState
, etc..myDialog.destroy()
every time just to clear the user inputs. Keep the existing cleaned up control instance and reuse it for the next data entry.Upvotes: 1
Reputation: 4231
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": ""
});
}
Upvotes: 1
Reputation: 29
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;
};
Upvotes: 0