Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.3k views
in Technique[技术] by (71.8m points)

sapui5 - List control breaks after grouping aggregation

I have two problems with a sap.m.List. I'm trying to group values by a given date.

  1. The grouping shows duplicates group headers, despite having the same values.
  2. The actual list items are not showing at all.

I'm binding everything in the controller. When I remove the sorter, the list behaves correctly and shows all data correctly. But as soon as I add the grouping, the list breaks.

Screenshot

XML View (the item is more complex, for simplicity all other variables have been removed):

<List id="idListCart" noDataText="{i18n>CartListNoData}">
  <CustomListItem id="listItemTemplate">
    <layout:Grid class="sapUiSmallMarginTop" hSpacing="1">
      <VBox alignItems="Start" class="sapUiSmallMarginEnd">
        <Text text="{Fullname}" />
        <Text text="{Startdatum}" />
      </VBox>
    </layout:Grid>
  </CustomListItem>
</List>

Controller:

this.byId("idListCart").bindItems({
  path: "/CartSet",
  template: this.byId("listItemTemplate"),
  filters: aFilters,
  sorter: new Sorter({ // required from "sap/ui/model/Sorter"
    path: "Startdatum",
    group: function(oContext) {
      return /* formatted date */;
    }
  }),
  groupHeaderFactory: function(oGroup) {
    return new GroupHeaderListItem({ // required from "sap/m/GroupHeaderListItem"
      title: oGroup.key
    });
  },
});

Any ideas what I am doing wrong?

question from:https://stackoverflow.com/questions/65876663/list-control-breaks-after-grouping-aggregation

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Generally, it's not a good practice to declare a template in XML without predefining the corresponding list binding in the parent control. Otherwise, it's just adding a plain single item which UI5 renders together with the List initially. That single item gets then cloned and replaced in the 2nd rendering cycle after the list binding from the controller is complete. I guess this is where the grouping has issues; the first item is no longer sap.m.CustomListItem but sap.m.GroupHeaderListItem causing side effects when rendering the rest of the items.

Better create the template in the controller entirely:

<List id="idListCart" noDataText="{i18n>CartListNoData}">
  <!-- Template control is created in the controller. Nothing to define here. -->
</List>
{ // Items binding info
  path: "/CartSet",
  templateShareable: false,
  template: new CustomListItem({ // required from "sap/m/CustomListItem"
    // ...
  }),
  // ...
}

This does add quite a few more LOC to the controller but avoids unexpected side effects.

We could keep the template definition in XML by wrapping it with <dependents> but that practice caused other issues in my experience with some outdated SAPUI5 controls. But you might want to give it a try:

<List id="idListCart" noDataText="{i18n>CartListNoData}">
  <dependents> <!-- Controls that belong to parent but aren't supposed to be rendered -->
    <CustomListItem id="listItemTemplate">
      <!-- ... -->
    </CustomListItem>
  </dependents>
</List><!-- No change in the controller -->

Alternative

If possible, define the list binding directly in XML where Sorter can be created declaratively, with its grouper and group header factory assigned using the '.<fnName>' syntax. Dynamic filters would still need to be created and added to the list binding separately in the controller.

<List id="idListCart"
  noDataText="{i18n>CartListNoData}"
  items="{
    path: '/CartSet',
    sorter: {
      path: 'Startdatum',
      descending: true,
      group: '.formatDate'
    },
    groupHeaderFactory: '.createGroupHeader',
    suspended: true
  }"
><!-- Template as usual: -->
  <CustomListItem>
    <!-- ... -->
  </CustomListItem>
</List>

The list binding is suspended first in order to prevent sending a request prematurely without the $filter query. Once the binding resumes, a single request will be sent instead of two.

{ // Controller

  onInit: async function() {
    const myFilters = new Filter({/* ... */}); // required from "sap/ui/model/Filter"
    await this.getOwnerComponent().getModel().metadataLoaded(); // preventing race condition
    this.byId("idListCart").getBinding("items") // access ODataListBinding
      .filter(myFilters, "Application")
      .resume(); // starts sending the request from here.
  },

  formatDate: function(oContext) {
    return /* formatted date */;
  },

  createGroupHeader: function(oGroup) {
    return new GroupHeaderListItem({ // required from "sap/m/GroupHeaderListItem"
      title: /* formatted group header using oGroup.key */
    });
  },

  // No list binding from controller
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...