SAPUI5 - How do I aggregate with formElement? - sapui5

I previously implemented aggregation with VBox. This get all the 'questions' and creates a Text box for each....
<VBox items="{path: 'view>questions', templateShareable: true}">
<items>
<VBox class="sapUiTinyMargin" templateShareable="true">
<Text text="Question {view>OrderSequence}"/>
</VBox>
</items>
</VBox>
I need to do the same, but for formElements. Can this be done?
<f:formElements>
<f:FormElement label="{i18n>radioLabel}">
<f:fields>
<Input value="{viewmodel>radioLabel}" id="__redioLabel"/>
</f:fields>
</f:FormElement>
</f:formElements>
It doesn't seem to work with 'items'

In UI5 elements have several characteristics:
Properties: generally scalar attributes, like "title" or "width".
Events: which are fired when something happens, like "press" or "close".
Aggregations: collections of child entities, like the "items" of a list.
Associations: related controls, like the "label" of a input field.
You can find how these relate to the concept of data binding in the official documentation here.
In your case, the "formElements" is an aggregation of the FormContainer element. Based on the documentation:
Aggregation binding can be used to automatically create child controls according to model data. This can be done either by cloning a template control, or by using a factory function. Aggregations can only be bound to lists defined in the model, that is, to arrays in a JSON model or a collection in the OData model.
This implies that ANY aggregation can be used, no matter how it is named. Now, to go back to you example, the reason why "items" does not work, is because the FormContainer parent element has no aggregation with that name. Instead, you must use the "formElements" aggregation.
<f:FormContainer formElements="{viewmodel>/my/path/to/list}">
<f:formElements>
<f:FormElement label="{i18n>radioLabel}">
<f:fields>
<Input value="{viewmodel>radioLabel}"/>
</f:fields>
</f:FormElement>
</f:formElements>
</f:FormContainer>
Also, note that usually, you do not need to give an ID to the template or any of its children (the Input in your example has an ID), that is because that element specifically will not be part of the resulting control tree. It is just used to be cloned to create the "real" elements based on the model list.
Lastly, you have a "templateShareable" property on the VBox in your first example. VBox has no such property, so it does nothing (you actually use it correctly inside the binding specification for the "items" of the parent VBox).

Solution was...
An aggregation on the form...
<f:Form id="formCustomRadio" editable="true" visible="true" formContainers="{viewmodel>answers}">
Thanks a lot

You can use the VBox and then put the f:formElements inside the items.
Or else use Standard List.
FormElements or containers don't have aggregation items.

Related

MultiComboxBox within a table closes after selection

I have tried to resolve the problem for quite sometime but have not been successful.
Inside a table, I have a sap.m.MultiComBox. The drop down in the multicombobox closes after selecting the first value. If not inside the table, the multicombobox works fine as expected (popover does not close). One additional behavior I observed is that if I don't have the selectedKeys bound, then it works fine.
Any reasons or suggestions?
<Table
growing="true"
items="{employee>/EmployeeCollection}">
<columns>
<Column width="10%" />
<Column width="55%" />
<Column width="35%"/>
</columns>
<ColumnListItem>
<Image id="image" src="{employee>dataURI}" class="sapOB_Assign_Usercircle" />
<Text text="{employee>Name}" class="tableText" />
<VBox>
<MultiComboBox id="mcb1"
selectedKeys="{employee>roles}"
items="{
path: 'roles>/BusinessRoles',
templateShareable: false
}">
<core:Item key="{roles>id}" text="{roles>name}" />
</MultiComboBox>
<HBox />
</VBox>
</ColumnListItem>
</Table>
You must be using UI5 version 1.66 or below with growing="true" on the Table. In that case, the dropdown closes immediately after the selection due to a focus loss caused by rerendering of the list item (Its DOM element being rewritten completely). The rerendering itself is caused by two-way bound selectedKeys which explains why it "works" if the property is not bound.
Normally, two-way binding should not be used together with growing. According to the API reference:
Note: Growing must not be used together with two-way binding.
But it's still unclear whether the above constraint is still valid today (See issue #3013).
In order to keep two-way binding with growing="true", add the attribute key* to the list binding info:
<Table
growing="true"
items="{
path: 'employee>/EmployeeCollection',
key: 'employeeId'
}"
>
Here is a working example: https://jsbin.com/ciyosir/edit?js,output. As you can see, the dropdown is kept open even after selection because the list item is not rerendered thanks to the extended change detection.
Additionally, I'd suggest to upgrade to the latest stable UI5 version in order to benefit from many controls having migrated to the new semantic rendering.
* The key awaits a property name from the model of which the value is unique. For more information, see topic Extended Change Detection.

Add contains filtering option in the Filter tab in the P13n dialog for the Smart Table

I am using SmartTable with property useTablePersonalisation set to true which generates P13n dialog by button . According to the manual I should be able to change data type of filter operator:
The second field offers an operator for specifying the filter in more detail. The operators that are available depends on the data type of the selected column.
I am interested in these two options from manual:
I want to get "string type" option, which autogenerates this:
But I am still getting option autogenerated for "number type" instead of "string type". I declared this field as Edm.String in the backend entity.
Please do you have any idea how to resolve this issue?
Here is my xml code, Abc is Edm.String:
<smartTable:SmartTable id="idSmartTable" smartFilterId="idSmartFilterBar" tableType="ResponsiveTable" entitySet="AbcSet"
useVariantManagement="false" useTablePersonalisation="true" header=" " showRowCount="true" enableAutoBinding="true" useExportToExcel="false"
showFullScreenButton="true">
<Table growing="true" mode="None">
<columns>
<Column>
<customData>
<core:CustomData key="p13nData" value='\{"columnKey": "Abc","leadingProperty": "Abc","sortProperty": "Abc","filterProperty": "Abc"}'/>
</customData>
<header><Text text="{i18n>Abc}" wrapping="false"/></header>
</Column>
</columns>
<items>
<ColumnListItem>
<cells>
<Text text="{Abc}"/>
</cells>
</ColumnListItem>
</items>
</Table>
</smartTable:SmartTable>
I was trying to change data type to "sap.ui.model.type.String" of CustomData in the xml table definition or in the ColumnListItem but maybe I am doing something wrong. I must use 1.38 UI5 version.
Thanks for any device.
Since you are using the custom columns in the smart table you need to define "type" property in the custom data of your column.
Following types should be used for different data types:
string
numeric
date
you can see the sample code in the URL mentioned, here they have used type numeric in the example.
[https://ui5.sap.com/#/entity/sap.ui.comp.smarttable.SmartTable/sample/sap.ui.comp.sample.smarttable.mtableCustom/code]
You can check the SAP UI5 example from the below link:
https://ui5.sap.com/#/entity/sap.ui.comp.smarttable.SmartTable/sample/sap.ui.comp.sample.smarttable.mtableCustom/code
Basically you need to use ' "type": "text" ' in the custom data along with the sort and filter property.
Thanks,
Mahesh

Why can I give items as property?

The Table is extended from the ListBase. On the Table, I can you use items aggregation from ListBase as a property like as following:
<Table inset="false" items="{ path: '/ProductCollection', sorter: { path: 'Name' } }">
Why can I use items as a property, although is defined as aggregation?
In UI5, there are basically two ways of filling an aggregation. One is by adding the elements directly, and the other is to bind them to a model.
Your example shows the latter case, where the items aggregation is bound to the collection /ProductCollection in your model.
An aggregation binding consists of two parts in XML views, one is the actual binding with the "property" as you did, and the other is to specify the "template" which is used for each element in the collection.
This is also explained further in the documentation:
<mvc:View
controllerName="sap.ui.sample.App"
xmlns="sap.m"
xmlns:mvc="sap.ui.core.mvc">
<List id="companyList" items="{/companies}">
<items>
<StandardListItem
title="{name}"
description="{city}"
/>
</items>
</List>
</mvc:View>
The List element has both an items attribute and a nested items element:
The attribute items="{/companies}" binds the children of our json model's companies array to the list. This by itself is not enough to display the companies, instead it sets the parent path for the binding of all contained list items and their descendants. In addition you need to declare a nested element.
The nested items element in our case contains a StandardListItem. This serves as a template for creating the individual list rows.
Writing "items=..." in the XML is the same as writing "bindItems(..." or "bindAggregation('items',..." in JS. The framework knows that it is an aggregation and that you are binding the path '/ProductCollection' from your unnamend odata model to it.
As Ronnie mentioned, in the binding there are properties like 'path' or 'sorters'. You can check it here https://sapui5.hana.ondemand.com/#/api/sap.ui.base.ManagedObject/methods/bindAggregation
The object oBindingInfo is the one.

Get Control ID Defined in XML Fragment

I have a fragment having many input fields. I want to check the ID of the input field in which text was entered. When I do oEvent.getSource().getId(),
I get "someId-controlId". I am not sure what that "someId" is of. But after "-" is the "controlId" for sure. Could anyone advise how to separate the control ID from the output I am getting or how to extract control ID in controller?
The way how the global ID is created depends on how the fragment was created. Moreover, this is how separators are currently used between the ID segments:
Components append "---"
Views append "--"
Controls / elements append "-" (e.g. for cloned instances via aggregation binding)
Sample output:
"componentId---viewId--controlId-__clone0"
The number of separators, how they are inserted, or even which character is used to separate each segment might change in later versions. In fact, there is a warning in the documentation not to rely on the current syntax:
Do not rely on the specific prefixing syntax because it may change at some point.
To make it worse, no errors are thrown when such a separator was indeed used in the id definition. Assuming the separator character might be included in the id definition makes extracting the right part from the global ID much harder and unpredictable.
In order to make each control distinguishable, a better approach would be to add CustomData.
<Input change=".onChange">
<customData>
<core:CustomData key="is" value="A" />
</customData>
</Input>
<Input change=".onChange">
<customData>
<core:CustomData key="is" value="B" />
</customData>
</Input>
<Input change=".onChange">
<customData>
<core:CustomData key="is" value="C" />
</customData>
</Input>
The custom data can be then retrieved in the Controller via the API data:
onChange: function(oEvent) {
const inputIsFrom = oEvent.getSource().data("is"); // returns: "A", "B", or "C"
// ...
},

How to make only one cell editable in smart table sapui5

I am using a sapui5 smart table to list down my products. It includes product code, product description and order quantity.
Among these three fields i want to update only the order quantity. It should be an inline editing in the table.
In my smart table i have enabled the property "editable" as "true". It makes the entire row is editable. Instead of making entire row editable, i want make only one cell to be editable.
Example
<smartFilterBar:SmartFilterBar id="smartFilterBar" entityType="ZDEMO_C_MyEntityType" persistencyKey="SmartFilter_Explored">
</smartFilterBar:SmartFilterBar>
<smartTable:SmartTable id="mySmartTable"
smartFilterId="smartFilterBar"
tableType="GridTable"
editable="true"
entitySet="ZDEMO_C_MyEntity"
useVariantManagement="false"
useTablePersonalisation="true"
header="My Products"
showRowCount="true"
useExportToExcel="true"
enableAutoBinding="true">
</smartTable:SmartTable>
I can see 2 ways:
Make use of "field controls" concept. It requires adding a special properties within your entity type, which define the state of the fields (cells). Also some annotations have to be introduced (in the metadata.xml by backend) to initiate the handling.
Here is a link where concept described using Form control as an example, but the same rules are applicable for Table as well:
https://blogs.sap.com/2017/06/06/dynamic-field-control-using-annotations-in-sapui5/
Redefine the table rows manually in your XML and bind the needed cell(s) against the property of the local JSON model, which could be changed depending on some conditions (e.g. Edit button press).
The 1st approach is better from the architectural perspective but requires some data model modifications (from the backend side).
The 2nd approach allows to do everything on UI and program some complex UI logic, which defines the cells state.
You choose.
You can add a sap ui table inside the smart table and add columns with customdata property. Follow these steps.
Make editable="true" as editable="false"
In your xml, make sure to add this namespace xmlns:core="sap.ui.core"
Within the smarttable tag add below.
<smartTable:SmartTable .................................
<Table>
<columns>
<Column>
<customData>
<core:CustomData key="p13nData" value='\{"columnKey": "OrderQty", "leadingProperty": "OrderQty", "columnIndex":"2"}'/>
</customData>
<Text text="Order Qty"/>
</Column>
</columns>
<items>
<ColumnListItem>
<cells>
<Input value="{OrderQty}" type="Number" editable="true"/>
</cells>
</ColumnListItem>
</items>
</Table>
</smartTable:SmartTable>
Add below tag inside table to make your column editable
<table:Column sortProperty="Max_Capacity" filterProperty="Max_Capacity" id="maxCapCol">
<Label text="Max Capacity"/>
<table:template>
<Input text="{Max_Capacity}" />
</table:template>
</table:Column>