ValueHelpDialog error adding element with duplicate id - sapui5

The first time I open die ValueHelpDialog and chose an item there are no errors. However when I open the dialog again and click on the same entry I get the following error:
Uncaught Error: Error: adding element with duplicate id
'EQ5IPAKCZJMZR'
at constructor.x.registerElement (Core-dbg.js:2711)...
When I click again i get another error:
Uncaught TypeError: Cannot read property 'getKey' of undefined
at constructor.onVHOK (ValueHelper.js?eval:35)...
When I click a third time it works without an error.
I use
this._dialog.close();
this._dialog.destroy();
in the event handlers for the ok event as well as for the cancel event.
Is the second error related to the first one? I thought closing and destroying the dialog would be enough to avoid those duplicate id errors.
Edit: More code
Controller:
this._ValueHelper = new ValueHelper();
/* Calling valueHelper */
onVH: function(oEvent) {
this._ValueHelper.handleValueHelp(this, {
srcCtrl: oEvent.getSource()
});
},
ValueHelper.js:
handleValueHelp: function(oController, mOptions) {
if (mOptions == null) {
mOptions = {};
}
this._controller = oController;
this._view = oController.getView();
this._model = this._controller.getModel();
this._callback = mOptions.fnCallback;
this._srcCtrl = mOptions.srcCtrl;
if (this._dialog != null) {
this._dialog.open();
} else {
this._buildValueHelp();
}
},
_buildValueHelp: function() {
var oDialog = sap.ui.xmlfragment(
"xxx.view.fragments.valueHelper.ValueHelp",
this
);
this._view.addDependent(oDialog);
this._dialog = oDialog;
this._registerEnterEvent(oDialog);
var oTable = sap.ui.xmlfragment(
"xxx.view.fragments.valueHelper.ValueHelpTable",
this
);
this._table = oTable;
oDialog.setTable(oTable);
oDialog.open();
},
onVHOK: function(oEvent) {
var sId = oEvent.getParameter("tokens")[0].getKey();
this._handleOk(sId);
this._dialog.close();
/*this._dialog.destroy();*/
},
onVHClose: function() {
this._dialog.close();
/*this._dialog.destroy();*/
},
ValueHelp.fragment:
<core:FragmentDefinition xmlns="sap.m" xmlns:core="sap.ui.core" xmlns:vh="sap.ui.comp.valuehelpdialog" xmlns:fb="sap.ui.comp.filterbar">
<vh:ValueHelpDialog key="id" ok="onVHOK" cancel="onVHClose" selectionChange="onVHSelectionChange"
title="Car" supportMultiselect="false" supportRanges="false" supportRangesOnly="false" descriptionKey="Car">
<vh:filterBar>
<fb:FilterBar advancedMode="true" filterBarExpanded="true" search="onVHShipSearch">
<fb:filterGroupItems>
<fb:FilterGroupItem groupName="car" name="n1" label="Name">
<fb:control>
<Input />
</fb:control>
</fb:FilterGroupItem>
<fb:FilterGroupItem groupName="car" name="n2" label="imnr">
<fb:control>
<Input/>
</fb:control>
</fb:FilterGroupItem>
</fb:filterGroupItems>
</fb:FilterBar>
</vh:filterBar>
</vh:ValueHelpDialog>
</core:FragmentDefinition>
ValueHelpTable.fragment:
<core:FragmentDefinition xmlns="sap.m" xmlns:core="sap.ui.core" xmlns:t="sap.ui.table">
<t:Table rows="{path: '/Cars'}" selectionBehavior="Row" selectionMode="Single">
<t:columns>
<t:Column>
<t:label>
<Label text="Number"/>
</t:label>
<t:template>
<Text text="{ path: 'number', formatter: '.formatter.leadingZeroes' }"/>
</t:template>
</t:Column>
<t:Column>
<t:label>
<Label text="Name"/>
</t:label>
<t:template>
<Text text="{name}"/>
</t:template>
</t:Column>
<t:Column>
<t:label>
<Label text="imnr"/>
</t:label>
<t:template>
<Text text="{imnr}"/>
</t:template>
</t:Column>
</t:columns>
</t:Table>
</core:FragmentDefinition>

I thought closing and destroying the dialog would be enough to avoid those duplicate id errors.
Usually, it is. But the dialog is probably still being referenced by other elements. In this case, you could...:
Try to remove the references from all reference holders before destroying it.
For example, if the view has the dialog in its <dependents> aggregation:
this._dialog.attachEventOnce("afterClose", () => {
this.getView().removeDependent(this._dialog);
this._dialog.destroy();
this._dialog = null;
}).close();
But I'm not aware of any effective ways to find all reference holders. Hence ..
Double check if it's really necessary to destroy the dialog every time after closing. Such an attempt is usually a sign of premature optimization. As long as there is no memory issue, I'd keep the dialog in memory so that it can be opened quickly next time.
If you're destroying the dialog for another reason, please add it to your question.

I had the same problem and resolved it by making this sequence of choices.
closeIndicator: function(oEvent) {
this._dialog.close(); // First: close fragment
this._dialog.destroy(); //Second: destoy fragment
this._dialog=null; // Third: null name/pointer
},
The problem is that when you perform the destroy you do not nullify the variable.

Related

CanĀ“t to bind or show data from ModelData.read to form in SapUi5

i have a big trouble that's freaking me out, check it out:
This is my xml for a View, program is divided in two differents Views, first of them, a table with navigation and second one is the details, involved in a form.
<mvc:View xmlns:core="sap.ui.core" xmlns:mvc="sap.ui.core.mvc" xmlns="sap.m" controllerName="Hello_World.Hello_World.controller.View2"
xmlns:html="http://www.w3.org/1999/xhtml" xmlns:form="sap.ui.layout.form">
<App>
<pages>
<Page title="Detalles" showHeader="true">
<Button type="Back" press="patras" tooltip="test"/>
<VBox>
</VBox>
<form:SimpleForm id="formPruebas" maxContainerCols="2" layout="ResponsiveGridLayout" labelSpanL="5" labelSpanM="4" labelSpanS="6" title="Formulario">
<Label text="CustomerID"/>
<Text text="{jsonmodel>CustomerID}"/>
<Label text="CompanyName"/>
<Text text="{CompanyName}"/>
<Label text="ContactTitle" />
<Text text="{ContactTitle}"/>
<Label text="Adress"/>
<Text text="{Adress}"/>
<Label text="City"/>
<Text text="{City}"/>
<Label text="PostalCode"/>
<Text text="{PostalCode}"/>
<Label text="Country" />
<Text text="{i18n>Country}"/>
<Button text="Aceptar" type="Accept">
<layoutData>
<FlexItemData growFactor="1" />
</layoutData>
</Button>
<Button text="Editar" width="100px">
<layoutData>
<FlexItemData growFactor="1" />
</layoutData>
</Button>
</form:SimpleForm>
</Page>
</pages>
</App>
Alright so this is my controller:
onInit: function() {
var oRouter = sap.ui.core.UIComponent.getRouterFor(this);
oRouter.getRoute("View2").attachMatched(this._onRouteMatched, this);
},
_onRouteMatched: function(oEvent) {
var idDevuelto = oEvent.getParameter("arguments").data;
var idCompleto = "/Customers('" + idDevuelto + "')";
//var oForm = this.getView().byId("formPruebas");
var oForm = this.getView().byId("formPruebas");
var serviceUrl = "myurl";
var oModelData = new sap.ui.model.odata.ODataModel(serviceUrl,{
JSON:true,
useBatch: false
});
oModelData.read(idCompleto, {
success: function (oData) {
sap.m.MessageToast.show(oData.CustomerID);
var oModel = new JSONModel(oData.results);
oForm.setModel(oModel.results);
},
error: function (oError) {
sap.m.MessageToast.show("No funca");
}
});
},
However the result is not showing on my view when i run the program, any idea?
PD: I'm pretty noob on sapui5
Finally i found the solution of the problem, if you check the XML code at the beginning of the question , you realize there is not path in Form element.
In mi first view i have a table with id and a PATH which i don't have in the Form so the fastest way i solve it is to put in the XML a slash, an example:
<form:SimpleForm id="formPruebas" maxContainerCols="2" layout="ResponsiveGridLayout" labelSpanL="5" labelSpanM="4" labelSpanS="6" title="Formulario">
<Label text="CustomerID"/>
<Text text="{/CustomerID}"/> // the slash i mean
With that fix, i only do oForm.setModel(oModel) so everything is connected and works
fine, i want to share this information if more people deal with the same error as i did.
Thanks all ideas.

SAP UI5 table's column sorting is lost after table model is refreshed

I have a table (sap.ui.table) and all its column have sorting. In the table, one of the columns has a button for changing and saving the value of its cell. On saving, I want to update the table model with new data.
If I have sorted by PO number column, and the table refreshes, then the table is not again sorted in PO numbers, but it is reset to the stockroom name column sort which is default one.
How to ensure the sort that was applied to any of its column retains after model refresh?
XML.view
<table:Table id="abc"
rows="{ path: 'tableModel>/results', sorter: {path: 'FromStockroomName'}}" selectionMode="Single" selectionBehavior="RowOnly">
<table:columns>
<table:Column id="fromStk" sortProperty="FromStockroomName">
<Label text="From Stockroom"/>
<table:template>
<Text class="table-cell-text" text="{dataModel>FromStockroomName}"/>
</table:template>
</table:Column>
<table:Column sortProperty="OrderNumber">
<Label class="smartist-table-column-header" text="PO number"/>
<table:template>
<Link text="{dataModel>OrderNumber}" emphasized="true"/>
</table:template>
</table:Column>
<table:Column sortProperty="BackOrderedQty">
<Label text="B/O Qty"/>
<table:template>
<HBox>
<Button text="Reduce"
visible="{= !${dataModel>qobEditable}}" press="openBOText"/>
<Button text="Save" visible="{= ${dataModel>qobEditable}}" press="boQtySave"/>
<Input visible="{= ${dataModel>qobEditable}}"/>
</HBox>
</table:template>
</table:Column>
Controller.js
getAllBackOrderData: function () {
var filters = [
new Filter("CompanyID", FilterOperator.EQ, vc.company)
];
someService.getBackOrders(vc, filters).then(function (data) {
data.results = vc._sanitizeTableModel(data);
var backOrderModel = new JSONModel(data);
vc.getView().setModel(backOrderModel,"dataModel");
SpinnerUtils.stopSpinner(vc);
}).catch(function (error) {
...
}).then(function () {
SpinnerUtils.stopSpinner(vc);
});
**********************************************************************
_sanitizeTableModel: function (data) {
return data.results.map(function (order) {
order.qobEditable = false;
//need to add this
//order.previousBOQty = order.backOrder
return order;
})
},

Data Binding from TableSelectDialog to Form

I'm using TableSelectDialog to view some Carrier details. All I need is: If I select any item (row), then all the values from that row should get automatically populated to the form input fields.
In the attached image, if Carrier Name is selected from TableSelectDialog, then the remaining fields should be populated based on that value.
You can achieve the required functionality using the Binding context that you receive from the TableSelcect Dialog.
But for binding context to work properly, both form and the table select dialog should refer to the same Model.
Below is the working code:
VIEW.XML:
Has a Button to trigger the table select dialog.
Has a form.
<l:VerticalLayout class="sapUiContentPadding" width="100%">
<l:content>
<Button class="sapUiSmallMarginBottom" text="Show Table Select Dialog"
press="handleTableSelectDialogPress">
</Button>
<VBox class="sapUiSmallMargin">
<f:SimpleForm id="SimpleFormDisplay354">
<f:content>
<Label text="Supplier Name" />
<Text id="nameText" text="{SupplierName}" />
<Label text="Description" />
<Text text="{Description}" />
<Label text="ProductId" />
<Text text="{ProductId}" />
<Label text="Quantity" />
<Text id="countryText" text="{Quantity}" />
</f:content>
</f:SimpleForm>
</VBox>
</l:content>
</l:VerticalLayout>
Controller:
onInit: function () {
// set explored app's demo model on this sample
var oModel = new JSONModel(jQuery.sap.getModulePath("sap.ui.demo.mock", "/products.json"));
this.getView().setModel(oModel);
},
handleTableSelectDialogPress: function (oEvent) {
if (!this._oDialog) {
this._oDialog = sap.ui.xmlfragment("sap.m.sample.TableSelectDialog.Dialog", this);
}
this.getView().addDependent(this._oDialog);
// toggle compact style
this._oDialog.open();
},
handleClose: function (oEvent) {
var aContexts = oEvent.getParameter("selectedContexts");
if (aContexts && aContexts.length) {
// MessageToast.show("You have chosen " + aContexts.map(function(oContext) { return oContext.getObject().Name; }).join(", "));
this.byId('SimpleFormDisplay354').setBindingContext(aContexts[0]);
}
oEvent.getSource().getBinding("items").filter([]);
}
Now, we also have a Select dialog and on click of any Item we call method : handleClose
In handleClose, we obtain the clicked Item binding context and then tell the form : hey! refer to this context ( which is present in the model). Binding context has a path which tells the form from where to do the relative binding.
Please feel free to contact for more information.

oData binding to Simple Form

I'm creating a read only form that will be used to display a summary of information. I need to send a parameter to the backend first before getting the information but I don't seem to see that parameter is reaching it.
It does reach the entity set but it does not show the parameter. Am I binding correctly?
This is on the controller:
onInit: function() {
var urlEnding = "1000012233";
var oFilterDist = new sap.ui.model.Filter("ID",
sap.ui.model.FilterOperator.EQ, urlEnding);
var summaryText = this.getView().byId("summaryForm");
summaryText.bindElement({
path: "/SummaryScreenSet",
filters: [oFilterDist]
});
}
This is on the View
<VBox class="sapUiSmallMargin" fitContainer="true"
height="100%" width="100%" justifyContent="End"
displayInline="true" id="leftVBox" items="{/SummaryScreenSet}">
<items>
<f:SimpleForm editable="true" layout="ResponsiveGridLayout" id="summaryForm" columnsL="1" columnsXL="1" labelSpanL="5" title="Account Summary" labelSpanM="5">
<f:content>
<Label text="Status" id="__label6" design="Bold" class="sizeText"/>
<ObjectStatus text="{CONTRACT_STATUS}" id="__status6" state="Success" class="boldText"/>
<Label text="Permit Required" id="__label10" design="Bold" class="sizeText"/>
<Text text="{PERMIT_REQD}" id="__text32" wrapping="false" class="sizeText"/>
<Label text="Bill Date | Due Date" id="__label11" design="Bold" class="sizeText"/>
<Text text="{BILL_DATE} | {DUE_DATE}" id="__text33" wrapping="false" class="sizeText"/>
<Label text="Last Estimated Date | Next MR Date" id="__label17" design="Bold" class="sizeText"/>
<Text text="{LAST_PAYMENT_DATE} | {nextMRDate}" id="__text39" wrapping="false" class="sizeText"/>
</f:content>
</f:SimpleForm>
</items>
</VBox>
Going to assume you want a single, specific entry. In that case, what you're looking for is the Entity, not the EntitySet + filter. Coincidentally, here's one I wrote yesterday that works. I've changed the paths and ID's to reflect yours:
var form = this.getView().byId('summaryForm');
form.bindElement({
path: "/SummaryScreenSet('" + urlEnding + "')",
events: {
change: function() {
//triggers on error too I think
form.setBusy(false);
},
dataRequested: function() {
form.setBusy(true);
}
}
});
In that case you don't need the VBOX either, just the form. Don't forget to implement SUMMARYSCREEN_GET_ENTITY or whatever the method is on your DPC_EXT.
Edit: might want to set editable on the form to false, it shrinks the layout to suit text instead of inputs.

Get bound values from pressed List Item in UI5 controller

I'm a beginner to SAP Fiori (UI5) and trying to retrieve a value from a table made with sap.m.Table in SAP Web IDE. But I don't succeed. Anybody hints on how to get there?
<Table id="table0" items="{/Entity1_Set}">
<ColumnListItem detailPress=".onShowHello" type="DetailAndActive">
<Text id="text5" maxLines="0" text="{Id}" />
<Text id="text6" maxLines="0" text="{field1}" />
<Text id="text7" maxLines="0" text="{field2}" />
<Text id="text8" maxLines="0" text="Euro"/>
</ColumnListItem>
<columns>
<Column id="column0">
<Label id="label0" text="Floor"/>
</Column>
</columns>
</Table>
sap.ui.define([
"sap/ui/core/mvc/Controller",
"sap/m/MessageToast",
], function(Controller, MessageToast) {
"use strict";
return Controller.extend("QuickStartApplication.controller.View1", {
onShowHello: function(){
MessageToast.show("Hello World!");
},
});
});
In the "hello world"-MessageToast, I would like to display values of the fields in my table.
You can pass parameters in the function, which is called during an event.
Please see also https://sapui5.hana.ondemand.com/#docs/api/symbols/sap.m.ListItemBase.html#event:detailPress
Using these parameters, you can access the bound data.
Please see the following code on how to read the binding context of the ColumnListItem:
detailPress : function(oEventParams){
var oListItem = oEventParams.getSource();
var oBindingContext = oListItem.getBindingContext(); var sSomePropertyValue = oBindingContext.getProperty("<nameOfProperty>"); }
Using .getProperty, you can access your field values.