SAPUI5 - How to change the table in the same View? - sapui5

situation:
2 diferent tables, 1 view;
want to achieve:
I've got to change the table when I change the selectedKey in a select.
I already tried to create fragments with each table and call the one I want when I change the select.
fragment1 - table 1
fragment2 - table 2
select - oEvent (onChangeSelect) - getSelectedKey - if(key === 1) - call fragment1, if(key === 2) - call fragment2, but it gives me an error.
I don't know if this is correct and I'm not finding any infos related online.
If someone can show some links or give me just an idea on how can I do this, it would be perfect.

You could use the concept of binding to achieve this without coding. assume you have something like that in your view:
<Select><!-- ... --></Select>
<core:Fragment ... /><!-- table 1 -->
<core:Fragment ... /><!-- table 2 -->
First, add a model to your app, which will contain the information about the status of the app (which of the fragments is visible currently). Easiest way is to start with an empty JSON model. If you are familiar with manifest.json config, add it there. if not, you could add this in your Components init method:
this.setModel(new JSONModel(), "config");
This way, the new empty model is available under the name "config" in all the views of the app.
Now, we will add a visibility flag, which will control the visibility of your fragments (tables), and can be changed by the select:
<Select selectedKey="{config>/selectedFragmentKey}">
<core:Item key="" text="Please choose one" />
<core:Item key="showFragment1" text="Show Fragment 1" />
<core:Item key="showFragment2" text="Show Fragment 2" />
</Select>
This new version will store the key of the selected item in the config model in the path /selectedFragment. Add visibility flag to fragments (tables):
<!-- table 1 is only visible if "Show Fragment 1" was selected -->
<Table visible="{= ${config>/selectedFragmentKey} === 'showFragment1'}" .../>
<!-- table 2 is only visible if "Show Fragment 2" was selected -->
<Table visible="{= ${config>/selectedFragmentKey} === 'showFragment2'}" .../>
and you're done.
Main lesson here: learn to use bindings.

If I understood the question correctly, you have a selection control (which has 2 entries), and a table in the view. Based on the selection item, you want to display ONE of the tables at any given point of time. I am also assuming here, that the tables are purely used in the current view only & not really a "re-useable" thing - which gets used in different places of the app (which then renders the use of fragments an overkill).
I am assuming, both tables are bound (although to different data sets).
In that case, consider using the "visible" property of the appropriate table to "false".
Remember - all UI components, including tables inherit from sap.ui.core.Control - which has the "visible" property.
In API reference for table : https://sapui5.hana.ondemand.com/#/api/sap.ui.table.Table%23methods/Summary
If you scroll down to "Borrowed from:" section, you will see the "setVisible" and "getVisibe" methods.
So, in the event fired during selection change, you can grab the table control & set the visibility using the aforementioned methods.
Alternatively, you can also set an expression binding directly on the table control in XML, which evaluates to "true" or "false" (perhaps using a formatter).
Let me know if you need further info on this.
Best Regards,
Gopal Nair.

Related

react-leaflet-draw - onEditStart/Stop events cumulates when trying to edit multiple featureGroups

I have a React application where a react-leaflet#2.7.0 + react-leaflet-draw#0.19.0 map is displaying multiple FeatureGroup components containing polygons.
On the right end side of the app is a clickable list of the feature groups names.
The groups can be selectively "activated" by clicking the names, so if a feature group is active, the EditControl therein is rendered. Only one group can be active at a time.
My problem
when I switch from one group to another and then click the edit button in EditControl, the onEditStart/Stop events of the previously active group still fire, along with the new ones. The more I switch between groups, the more events are fired.
Update:
The issue seems to be that EditControl is never unmounted, even if upon the parent state change it doesn't get rendered. If I add a random key prop to it, the issue is resolved, but then other issues occur.
I don't yet have a minimal code example to share, but to give you an idea of the implementation here is a schema:
<App>
<Map>
<MapContent id="foo">
<FeatureGroup>
<EditControl />
</FeatureGroup>
</MapContent>
<MapContent id="bar">
<FeatureGroup>
<EditControl />
</FeatureGroup>
</MapContent>
</Map>
<GroupSelector />
</App>
Except App (root component), MapContent (very basic wrapper: checks if the group is active) and GroupSelector (clickable list), all other components are from react-leaflet and react-leaflet-draw.
Flow
On click, GroupSelector updates the state of App to set an activeGroupID (i.e. "foo") and MapContent will render its EditControl only if its id matches the activeGroupID
I hope this description makes sense. Any help would be greatly appreciated!

Cannot pass model data from aggregation binding to block for lazy loading in xml view

I'm building an SAPUI5 Fiori application from the project template "SAP Fiori Master-Detail Application" in SAP Web IDE. I connect to an OData Service that gives me this nested structure (bold text represents a navigation property):
File
Properties...
ToRegister (returns collection of Registers)
Properties...
ToDocumentType (returns collection of DocumentTypes)
Properties...
ToDocument (returns collection of Documents)
Properties...
Displaying data in the Detail View is very slow, so I'm trying to use sap.uxap.ObjectPageLayout's lazy loading feature. For that, I have to extract parts of my view and put them into a custom block. This is accomplished by deriving from sap.uxap.BlockBase, according to this example (code here). Unfortunately, I couldn't find any examples that use aggregations/aggregation bindings.
I use XML Views. My Detail View looks like this:
<mvc:View
xmlns="sap.uxap"
xmlns:mvc="sap.ui.core.mvc"
xmlns:m="sap.m">
<ObjectPageLayout sections="{ToRegister}">
<sections>
<ObjectPageSection title="{RegisterName}" subSections="{ToDocumentType}">
<subSections>
<ObjectPageSubSection title="{Description}" >
<blocks>
<m:List items="{ToDocument}">
<m:CustomListItem>
...code for displaying properties...
</m:CustomListItem>
</m:List>
</blocks>
</ObjectPageSubSection>
</subSections>
</ObjectPageSection>
</sections>
</ObjectPageLayout>
</mvc:View>
This code does not use custom blocks. This is the slow version, but it works and displays the data correctly. Notice that the bold navigation properties are placed in curly braces (e.g. <m:List items="{ToDocument}">).
For my custom block, I extracted the <m:List> part into a seperate view:
<mvc:View
xmlns="sap.uxap"
xmlns:mvc="sap.ui.core.mvc"
xmlns:m="sap.m">
<m:List items="{Documents}">
<m:CustomListItem>
...code for displaying properties...
</m:CustomListItem>
</m:List>
</mvc:View>
Notice here that the property in the curly braces is not {ToDocument} anymore but {Documents}. That's because of the model mapping that has to be introduced in the original Detail View for this to work (see section Model Mapping in this article). I modified the Detail View like this:
<mvc:View
xmlns="sap.uxap"
xmlns:mvc="sap.ui.core.mvc"
xmlns:m="sap.m"
xmlns:attachmentblock="pft7.blocks.FileAttachmentList">
<ObjectPageLayout sections="{ToRegister}">
...
<blocks>
<attachmentblock:Block mode="Expanded">
<ModelMapping
externalModelName="ToDocumentType"
internalModelName="Documents"
externalPath="/ToDocument" />
</attachmentblock:Block>
</blocks>
...
</ObjectPageLayout>
</mvc:View>
It's pretty much the same. I added the xml namespace attachmentblock (which points to my custom block) and used it to replace the <m:List> child of <blocks>. Notice the attribute internalModelName of the <ModelMapping> node. It can be freely chosen and just has to be the same as the model name used in the block's view.
Finally, the problem: With this modification, my Detail View does not display the document properties anymore. Instead, it just displays the text "No data". I added some dummy text and it was properly displayed in my Detail View, so the actual inclusion of the custom block itself seems to work.
I suspect that I got the <ModelMapping> part wrong, but I don't know how to set the attributes correctly. I couldn't find any examples that use aggregations and navigation properties for this, so I'm pretty clueless. The console does not log any errors.
Can you check whether it works if you use an “Object Page with LazyLoading without blocks” as seen in this sample here?
In addition to this, you might also want to check on the reasons for the slow “Display of Detail View” via OData service.
Very often this is caused by long database access times.
If your backend is an R/3-ABAP system, you might want to check your OData service with an SQL-Trace (transaction ST05)
for long database response times caused by unnecessary requests or missing indexes.

How to get id of invisible element in fragment?

I have a Dialog in fragment:
<core:FragmentDefinition
xmlns="sap.m"
xmlns:f="sap.ui.layout.form"
xmlns:core="sap.ui.core">
<Dialog title="{i18n>AddNewItem}" resizable="true" draggable="true">
<content>
<MessageStrip
id="failMsg"
visible="false"
text="{i18n>SensorTypesCreateFail}"
type="Error"
showIcon="true"/>
</Dialog>
</core:FragmentDefinition>
As in UI5 doc:
Retrieving a control instance when the fragment is not part of a view:
When no fragment ID was given: myControl = sap.ui.getCore().byId("myControl")
When a fragment ID myFrag was given: myControl = sap.ui.core.Fragment.byId("myFrag", "myControl")
If there is no visible="false", I can get this MessageStrip by sap.ui.getCore().byId("failMsg").
But I found that with visible="false", id of MessageStrip is sap-ui-invisible-failMsg, I failed to found proper API to get it.
Of course I can use sap.ui.getCore().byId("sap-ui-invisible-failMsg"), but I am not sure whether this ID will change after I deploy it to FLP, and as #schnoedel said in another question:
Beware that the prefixes like -- and --- used by the framework may change in the future. Thats why it's recommended to use the public api functions the framework supplies like byId() and createId().
So, is there any better way to get it?
Update:
Change my code from:
this[dialogName] = sap.ui.xmlfragment("namespace." + dialogName, this);
this.getView().addDependent(this[dialogName]);
To
this[dialogName] = sap.ui.xmlfragment(dialogName, "namespace." + dialogName, this);
this.getView().addDependent(this[dialogName]);
And now my id is sap-ui-invisible-dialogName--failMsg...
It depends on what you want to achieve after getting the ID. If you just want to change a property you could do it without any ID via a Model.
For that you can assign a Model field (i.e. baseModel>/visable) to the visable property and once it should be changed you change the model and via two way binding it updates the control.
code to change a model:
this.getView().getModel("nameOfUrModel").setProperty("property", "value")
for more information about this just check the walkthrough tutorial on
https://sapui5.hana.ondemand.com/
And if you for whatever reason really need the ID:
https://sapui5.hana.ondemand.com/#docs/api/symbols/sap.ui.core.Fragment.html
here you find the command:
sap.ui.core.Fragment.byId(sFragmentId, sId)
It should be able to return the Control your using
Hope that helps
Eric
You were very close to the solution. After adding dialogName for the fragment ID in its creation, you just have to call the API ...:
sap.ui.require(["sap/ui/core/Fragment"], Fragment => Fragment.byId(dialogName, "failMsg"));
... to get the control instance as mentioned here.
However, regardless whether you provided a fragment ID or not, you can easily ignore the render prefix "sap-ui-invisible-" at any time - Meaning that you could've also been able to get the control instance via sap.ui.getCore().byId("failMsg") instead of sap.ui.getCore().byId("sap-ui-invisible-failMsg") even if the control is invisible.

SAPUI5: How to create a control hierarchy?

I hope you can help me with this. After reading all the documentation several times, googling for days, etc I don't find the way to do what i'm going to explain in a clean way, and in think I'm missing something because it's a really basic scenario.
I'm working with oData models, in this case 2 named models, "Model1", "Model2". Now what I want is to show a "parent" ComboBox based on an oData path, and a table that changes its items depending on the selection, in other words.
Model1 { //JSON representation of the data.
Accounts:[
"account 1": {invoices: ["invoice1", "invoice2", "invoice3"]},
"account 2": {invoices:["invoice4", "invoice5"]}
]
}
Combo Box:
<... items={Model1>/Accounts} /> -- This works and shows Account 1, and Account2.
Table
<Table... items="{Model1>Invoices}">
..
<items>
....
</items>
</Table>
What I want is the table to change it's context to the account selected on the ComboBox. The point is that this works, but the first time it loads the view, as there is no account selected, it calls the wrong odata path MYSERVICE/Invoices, instead of doing nothing, as the Account is not set yet, and the path for the invoices, once selected the account, shoud be MYSERVICE/Account('Account1')/Invoices for example.
I know I can achieve this with code, but I'm sure there must be a clean way to do this.
Seriously, this is driving me crazy.
Thanks for your help.
Are you sure that
items="{Model1>Invoices}"
triggers odata call? Because this is a relative path (without leading slash), normally it should not do the call.
What you can do:
Handle ComboBox selectionChange event;
In this event handler, create a path that you will bound the table to. In your case the path could look like this: "/Account(Account1)" - "/{EntitySetName}({KEY})". You can make use of createKey method of ODataModel2;
Set the table's context using the path:
oTable.bindObject({
path: sPath,
model: "Model1",
parameters: {
$expand: "Invoices"
}
});
Once the context is set, the relative binding will start working automatically and table will get the "Invoices"
I assume that the Account and Invoices are linked via navigation property and one-to-many cardinality, that's why the $expand parameter will load the corresponding invoices.

Can ItemDescriptor be mapped to a view instead of table

i have a view and it will be much easier for me to work against a view instead of a table itself. In ATG, can i map an itemdescriptor to a view instead of a table? Can someone provide me with an example?
TIA
Yes it can. It is actually no different to setting up a normal item-descriptor. However most views are read-only so you need to make sure your item-descriptor is also read-only. This can be achieved by setting the writable="false" property on the item-descriptor definition. The name of the view simply becomes the name of the table.
<item-descriptor name="ExampleItemDescriptor" cache-mode="simple" writable="false" query-expire-timeout="60000" item-cache-timeout="60000" item-cache-size="2000" query-cache-size="2000">
<table name="name_of_view" type="primary" id-column-names="multiple,columns">
<property name="aproperty" column-name="column" data-type="string" />
</table>
</item-descriptor>