0

I read the data from an OData V2 service at the startup of my SAPUI5 application with oModel.read(). The data are correctly loaded from the backend into the ODataModel. Later I would like to use one of my entities in a fragment (value help).

As the control to display the data in the fragment is a sap.m.Tree control. I can not use a local JSONModel to bind the data to the fragment but have to stick to the ODataModel.

How has the binding to be done to get data displayed in my fragment?

Here is the oModel.read:

oModel.read("/CategoriesSet", {
  filters: aFilters,
  success: function(oResult) {
    // ...
  },
});

In the Value Help, the fragment is called:

onCatValueHelp: function(oEvent){
  if (!this._oDialog) {
    this._oDialog = sap.ui.xmlfragment("<XXXXX>.view.Categories", this);
    this.getView().addDependent(this._oDialog);
  }
  this._oDialog.open();
},

And this is the Fragment:

<Tree id="CatTree" mode="MultiSelect"
  items="{
    path: '?????',
    parameters: {
      countMode: 'Inline',
      operationMode: 'Client',
      numberOfExpandedLevels: 0
    }
  }">
  <StandardTreeItem title="{CatName}" tooltip="{CatID}" />
</Tree>

If I use '/CategoriesSet' for the path, then I get the data loaded into the fragment, but the data is then fetched from the backend again, but without the filter. And as the backend call is not very performant, I would rather use the data that is already existing in the model from the previous oModel.read().

Boghyon Hoffmann
  • 17,103
  • 12
  • 72
  • 170
Cruncher
  • 177
  • 1
  • 1
  • 15
  • The issue is deeper as you'd like to avoid retrieving all `CategoriesSet` entities. But with `operationMode: 'Client'`, all entities are fetched first and then the filters will be applied on the client even if the filters are defined in the tree binding constructor. According to the doc: _> If `OperationMode.Client` is used, the **complete collection** without filters is requested; filters are applied on the client side._ – Boghyon Hoffmann Feb 17 '21 at 00:18
  • Does your backend support _> pre-filtering the tree and [...] responding with a complete tree hierarchy, including all inner nodes_? Then you could add `useServersideApplicationFilters: true` to the `parameters` (but without `operationMode: 'Client'`!) in order to avoid fetching all entities. Only then the `$filter` query will be added to the request URL the API reference. – Boghyon Hoffmann Feb 17 '21 at 00:31
  • _(Personal opinion..)_ I'd reconsider whether displaying the categories in a tree is absolutely required. It can become quickly too complex for both frontend and backend devs to implement the requirement. – Boghyon Hoffmann Feb 17 '21 at 00:36

2 Answers2

1

As shown in this answer, you can preliminarily define static filters in XML.

If the filter values, however, need to be dynamically determined, you'll have to create the filter instances in JS and pass them to the tree binding. You can keep the binding definition in XML but the aggregation binding should be suspended: true first (in order to avoid sending multiple requests unnecessarily).

<Tree id="CatTree" items="{
  path: '/CategoriesSet',
  ...,
  suspended: true
}">
  <StandardTreeItem title="{CatName}" tooltip="{CatID}" />
</Tree>
// As soon as the Tree is accessible from JS:
const suspendedTreeBinding = myTree.getBinding("items");
suspendedTreeBinding.filter(aFilters, "Application");
suspendedTreeBinding.resume(); // starts sending a single data request but with the filter

There is no need to call oModel.read() beforehand.


Off-topic but try to avoid using the old factory function sap.ui.xmlfragment if possible as it's deprecated since UI5 v1.58! Use sap/ui/core/Fragment.load instead. E.g. like this.

Boghyon Hoffmann
  • 17,103
  • 12
  • 72
  • 170
  • Wow! Is the suspend/resume method possible also for OData V2 binding? – Cmdd Feb 16 '21 at 21:12
  • @Cmdd Yes. [`suspend()`](https://openui5.hana.ondemand.com/api/sap.ui.model.Binding#methods/suspend) / [`resume()`](https://openui5.hana.ondemand.com/api/sap.ui.model.Binding#methods/resume) are all public methods from `sap/ui/model/Binding` (and thus for all types of bindings, even OData V2 binding). The property `suspended: true` in binding definition (e.g. in XML) is available for all ManagedObjects / Controls. ODataListBinding, for example, sends a GET request only after calling `.resume()` – Boghyon Hoffmann Feb 16 '21 at 21:19
  • @Cmdd Applies also to OData V4: https://openui5.hana.ondemand.com/topic/b0f5c531e5034a27952cc748954cbe39 – Boghyon Hoffmann Feb 16 '21 at 21:24
  • I think I misinterpreted the docs and never give them a proper try. I thought it applied ONLY to OData V4 :-) – Cmdd Feb 16 '21 at 21:24
  • @BoghyonHoffmann: Thanks for the reply and the Off-topic remark. I appreciate that and just adjusted the fragment. Now I am trying to use the suspended: true property. But unfortunately it's not working yet. The backend is called as soon as the fragment is loaded for the first time. Is there something else I need to adjust in my oDataService or in my default oDataModel? – Cruncher Feb 16 '21 at 22:05
  • @Cruncher I just double-checked: `suspended: true` works on ODataListBinding but not on v2.OData**Tree**Binding (no handling of `suspended` in the source code yet). I'll create an issue on GitHub and clarify it whether it was by design. Thank you for letting us know about the issue. In the meantime, you could remove the binding definition from XML and use `myTree.bindItems` instead. – Boghyon Hoffmann Feb 16 '21 at 22:36
  • @Cmdd See my previous comment (Feel free to remove the +1 ) – Boghyon Hoffmann Feb 16 '21 at 22:38
0

If you want to perform the read at the beginning, you can store the result of the success callback in a JSONModel and then bind this model to the Fragment.

Alternatively, you can skip the first call and configure carefully the call in the Fragment to match your needs.

Actually this is exactly how odata models work.

Personally, I prefer the first option because filtering is easier in javascript than in a declarative approach.

Cmdd
  • 867
  • 1
  • 10
  • 22
  • Thanks for the reply. I think I can not uste the JSONModel because the data is used in the sap.m.tree control and to get a proper tree it seems to be easier to use the oDataModel. If I would want to use a JSON Model for the Tree, then it would have to be a nested JSON, but it seems difficult to transform the result of the sucess callback into that. – Cruncher Feb 16 '21 at 22:11